/home dir cleaning

After a failed Fedora 26 upgrade on Friday, I spent the day trying to recover a corrupted rpm database, missing packages, not booting into gdm. I recovered it enough to boot, but it’s really in a weird state.

While preparing to backup my homedir, I was go through files I no longer needed. I’ve been cargo culting my homedir since I started working at Red Hat in 2004.

Here are the oldest files from day one:

[jesusr@transam ~]$ grep "Apr 19  2004" /tmp/2004files.tzt 
-rw-r-----. 1 jesusr jesusr 153 Apr 19  2004 ./.config/nautilus/first-time-flag
-rw-r-----. 1 jesusr jesusr 82 Apr 19  2004 ./.metacity/sessions/1082391428-6426-2821178921.ms
-rw-r-----. 1 jesusr jesusr 356 Apr 19  2004 ./.metacity/sessions/1082412707-4015-1328912501.ms
-rw-r-----. 1 jesusr jesusr 82 Apr 19  2004 ./.metacity/sessions/1082413316-4377-2885991371.ms
-rw-rw-r--. 1 jesusr jesusr 226 Apr 19  2004 ./cvs/rhn/ncsu-student-projects/message-bus/rhn/LoggerTest/.classpath
-rw-rw-r--. 1 jesusr jesusr 369 Apr 19  2004 ./cvs/rhn/ncsu-student-projects/message-bus/rhn/LoggerTest/.project
-rw-rw-r--. 1 jesusr jesusr 6377 Apr 19  2004 ./cvs/rhn/ncsu-student-projects/message-bus/rhn/SqlTest/src/TestDB.java
-rwxrwxr--. 1 jesusr jesusr 5441 Apr 19  2004 ./cvs/rhn/up2date/test/PyFontify.py
-rwxrwxr--. 1 jesusr jesusr 17900 Apr 19  2004 ./cvs/rhn/up2date/test/cov2html.py
-rw-rw-r--. 1 jesusr jesusr 23499 Apr 19  2004 ./cvs/rhn/up2date/test/coverage.py
-rwxrwxr--. 1 jesusr jesusr 2043 Apr 19  2004 ./cvs/rhn/up2date/test/testRhnChannel.py
-rwxrwxr--. 1 jesusr jesusr 4648 Apr 19  2004 ./cvs/rhn/up2date/test/testTransactions.py

Go through my homedir is like a digital archaeological dig 🙂

Horrible configure script

A friend of mine was having trouble building the latest version of R on RHEL 6.8. The trouble was that the configure script could not verify the updated zlib. I gave him some advice, but nothing seemed to work. Typically I’ve solved this with a simple update of the LD_LIBRARY_PATH.

Last night, I provisioned a RHEL 6.8 VM, downloaded the latest version of R, and tried building it myself. As expected, the configure script reported that I did not have zlib 1.2.5 installed.

checking if zlib version >= 1.2.5... no
checking whether zlib support suffices... configure: error: zlib library and headers are required

This is simple, just download an updated zlib, build and install. I installed zlib 1.2.11 in /usr/local/lib64. Re-ran configure and go the same result as above. I set LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH, configure still complained about not finding an updated zlib.

Updated zlib is installed, LD_LIBRARY_PATH is set. I ran sudo ldconfig -p | grep libz. It shows it can be found in /usr/local/lib64. So frustrating that everything seems to be in order and yet configure doesn’t see it.

My friend was using a blog post to help him out, when I looked at it the author didn’t see to have a problem with configure after updating zlib.

What in the world is the configure script doing to determine what version of zlib we are using?

if test "${have_zlib}" = yes; then
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if zlib version >= 1.2.5" >&5
$as_echo_n "checking if zlib version >= 1.2.5... " >&6; }
if ${r_cv_header_zlib_h+:} false; then :
  $as_echo_n "(cached) " >&6
else
  if test "$cross_compiling" = yes; then :
  r_cv_header_zlib_h=no
else

  cat confdefs.h - <conftest.$ac_ext
/* end confdefs.h.  */

#include 
#include 
#include 
int main() {
#ifdef ZLIB_VERSION
/* Work around Debian bug: it uses 1.2.3.4 even though there was no such
   version on the master site zlib.net */
  exit(strncmp(ZLIB_VERSION, "1.2.5", 5) < 0);
#else
  exit(1);
#endif
}

So it’s writing out a C program to verify the library. That’s when I saw it.

exit(strncmp(ZLIB_VERSION, "1.2.5", 5) < 0);

ZLIB_VERSION is “1.2.11”. So they are comparing the first 5 characters. So that means “1.2.1” vs “1.2.5” and as you can see “1.2.1” is LESS THAN “1.2.5”. Well that would be a problem and never work. So I tried fixing it by changing it to use 6 characters. Well, “1.2.11” in lexicographic order is before “1.2.5”. So that breaks too.

I know there has to be a better way, but since this isn’t my code I just wanted to it to build. I changed it to:

exit(strncmp(ZLIB_VERSION, "1.2.05", 6) < 0);

Why in the world would they use strncmp to compare versions? Aren’t there much better ways to determine libraries installed on a RHEL system? I was astounded this ever made it out to the wild.

vij update

Vij is a bash function that makes editing specific files easier to find in a deeply nested directory structure. At the time I was working in Java which is notorious for having a deep directory structure. In 2008, I updated vij to be more powerful by offering a list of files matching the name, giving you the option to make a choice.

Our current project is in Golang and I’ve found a deficiency in vij. We have a vendor directory which contains our dependencies and sometimes vij will find files in the vendor directory which I don’t want to edit. It would be nice to exclude a directory from the find command used by vij.

I always want to ignore the vendor directory. But I also want to allow arbitrary names to also be filtered out. The existing vij implementation results in the following crazy output.

$ vij util.go

Multiple matches found...
1: ./pkg/broker/util.go
2: ./pkg/fusortest/util.go
3: ./vendor/k8s.io/kubernetes/vendor/golang.org/x/crypto/ssh/terminal/util.go
4: ./vendor/k8s.io/kubernetes/vendor/github.com/vmware/photon-controller-go-sdk/photon/util.go
5: ./vendor/k8s.io/kubernetes/vendor/github.com/spf13/viper/util.go
6: ./vendor/k8s.io/kubernetes/vendor/github.com/spf13/cobra/doc/util.go
...
16: ./vendor/k8s.io/kubernetes/vendor/github.com/kr/pty/util.go
...
108: ./vendor/github.com/coreos/etcd/client/util.go
q: Quit

? 

I just want to see the util.go files in my code tree and ignore all of the ones in vendor. I changed the dafiles variable to have two more options. One is always ignore vendor from the normal use case, but also allow the user to pass in a string to filter on.

    if [ "$3" == "" ]; then
        dafiles=$(find . -type f -name "$2" -not -path "*/vendor/*")
    else 
        dafiles=$(find . -type f -name "$2" | grep -v $3)
    fi

Now when I run vij util.go, vij ignores the vendor directory.

$ vij util.go

Multiple matches found...
1: ./pkg/broker/util.go
2: ./pkg/fusortest/util.go
q: Quit

? 

Let’s say I want to find all files named User*.java but not the ones in the test directory. You can simply supply “test” as the second argument to vij.

BEFORE

$ vij User*.java

Multiple matches found...
1: ./src/main/java/org/candlepin/auth/permissions/UserUserPermission.java
2: ./src/main/java/org/candlepin/auth/permissions/UsernameConsumersPermission.java
3: ./src/main/java/org/candlepin/auth/UserAuth.java
4: ./src/main/java/org/candlepin/auth/UserPrincipal.java
5: ./src/main/java/org/candlepin/model/User.java
6: ./src/main/java/org/candlepin/model/UserCurator.java
7: ./src/main/java/org/candlepin/resource/UserResource.java
8: ./src/main/java/org/candlepin/service/UserServiceAdapter.java
9: ./src/test/java/org/candlepin/auth/UserPrincipalTest.java
10: ./src/test/java/org/candlepin/auth/permissions/UsernameConsumersPermissionTest.java
11: ./src/test/java/org/candlepin/model/UserTest.java
12: ./src/test/java/org/candlepin/resource/UserResourceTest.java
q: Quit

? 

AFTER

$ vij User*.java test

Multiple matches found...
1: ./src/main/java/org/candlepin/auth/permissions/UserUserPermission.java
2: ./src/main/java/org/candlepin/auth/permissions/UsernameConsumersPermission.java
3: ./src/main/java/org/candlepin/auth/UserAuth.java
4: ./src/main/java/org/candlepin/auth/UserPrincipal.java
5: ./src/main/java/org/candlepin/model/User.java
6: ./src/main/java/org/candlepin/model/UserCurator.java
7: ./src/main/java/org/candlepin/resource/UserResource.java
8: ./src/main/java/org/candlepin/service/UserServiceAdapter.java
q: Quit

? 

Here’s the full implementation of the updated vij:

# reused by other commands that require a filename
__dafiles ()
{
    cmd=$1
    if [ "$3" == "" ]; then
        dafiles=$(find . -type f -name "$2")
        #dafiles=$(find . -type f -name "$2" -not -path "*/vendor/*")
    else
        dafiles=$(find . -type f -name "$2" | grep -v $3)
    fi
    matches=$(echo $dafiles | gawk '{print NF}')

    case "$matches" in
        0)
           echo "No matches found"
           show=""
           ;;
        1)
           show=$dafiles
           ;;
        *)
           echo
           echo "Multiple matches found..."
           i=1
           for option in $dafiles
           do
              echo "$i: $option"
              i=`expr $i + 1`
           done
           echo "q: Quit"
           echo 
           read -p "? " ans
           if [ "q" == "$ans" ]; then
              show=""
           else
              show=$(echo $dafiles | gawk '{print $'$ans'}')
           fi
           ;;
    esac

    if [ "" != "$show" ]; then
       $cmd $show
    fi
}

vij ()
{
    __dafiles "vim" $1 $2
}

Network performance

Recently I had some network issues at work with lower than normal speeds, narrowed it down to the D-Link switch. I rebooted it and things seemed to be back to normal. Then at home I was hitting some similar performance issues both wired and wireless.

First thing was to verify that the wall port was giving me the correct speeds, then through the switch. Bingo, speeds were dropping. Ensured I was using Cat6 cables, found one cable that just didn’t want to connect, threw it out. Then I used the IT Crowd mantra and tried turning it off and on again. Speeds were back to normal.

Rebooted the downstairs switch as well, used iperf to measure the wired performance, 935 Mbits/sec.

$ iperf -p 2222 -c 172.31.6.3
------------------------------------------------------------
Client connecting to 172.31.6.3, TCP port 2222
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 172.31.6.221 port 35464 connected with 172.31.6.3 port 2222
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.09 GBytes   935 Mbits/sec
$ iperf -p 2222 -c 172.31.6.3
------------------------------------------------------------
Client connecting to 172.31.6.3, TCP port 2222
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 172.31.6.221 port 35470 connected with 172.31.6.3 port 2222
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.09 GBytes   935 Mbits/sec

So wired performance fixed. Next up was the wireless. I was getting 8.3MBps/66.4 Mbps, second run 7.0MBps/56Mbps. Reading the dd-wrt docs it reads:

You must use HT40 channel width & WPA2 AES when encrypting your wireless connection, otherwise the router will fallback to 802.11a/g speeds since WPA2 AES is a mandatory requirement of 802.11n specification as WPA2 AES is the only approved encryption method in the 802.11n standard.

Security was already WPA2 AES, but the Channel Width was set to the dd-wrt default of Full (20MHz). I changed it to Wide HT40 (40 MHz). Rebooted the router, and gave the tests another go.

First I used dd and nc then iperf. The dd reported 13.7MBps/109.6Mbps.

$ dd if=/dev/zero bs=1024K count=512 | nc -v 172.31.6.3 2222
Ncat: Version 7.12 ( https://nmap.org/ncat )
Ncat: Connected to 172.31.6.3:2222.
512+0 records in
512+0 records out
536870912 bytes (537 MB, 512 MiB) copied, 39.1152 s, 13.7 MB/s
Ncat: 536870912 bytes sent, 0 bytes received in 39.32 seconds.

iperf results in similar results if only slightly slower.

$ iperf -p 2222 -c 172.31.6.3
------------------------------------------------------------
Client connecting to 172.31.6.3, TCP port 2222
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 172.31.6.211 port 52358 connected with 172.31.6.3 port 2222
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec   110 MBytes  92.2 Mbits/sec
$ iperf -p 2222 -c 172.31.6.3
------------------------------------------------------------
Client connecting to 172.31.6.3, TCP port 2222
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 172.31.6.211 port 52360 connected with 172.31.6.3 port 2222
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec   102 MBytes  85.0 Mbits/sec

Two runs resulted in 92.2Mbps and 85.0Mbps. The three runs average out to 95.6Mbps wireless.

While not bad wireless performance, I was hoping to get closer to the 300Mbps product rating.

Network Hardware:

Computers:

  • Lenovo T460S
  • homebuilt server, MSI 785GTM-E45 motherboard with gigabit port

More Go links

Cobra is both a library for creating powerful modern CLI applications as well as a program to generate applications and command files.

https://github.com/spf13/cobra

Viper is a complete configuration solution for go applications including 12 factor apps. It is designed to work within an application, and can handle all types of configuration needs and formats.

https://github.com/spf13/viper

pflag is a drop-in replacement for Go’s flag package, implementing POSIX/GNU-style –flags.

https://github.com/spf13/pflag

7 common mistakes in Go and when to avoid them
https://www.youtube.com/watch?v=29LLRKIL_TI

Go! Speed Racer Go!

I finally reached a point where I could start running the go version of sm-photo-tool. I finished the option validation for the list command. While I was testing it I noticed how much faster the Go version felt. Here are the python vs Go versions of the commands.

The python version took 80 milliseconds to run the validation of parameters and print out the error message. This is expected, you have to start the python VM (and if not precompiled, the code would need to be compiled). In this test, the code was precompiled.

$ time sm-photo-tool list invalid blah
ERROR: valid options are ['album', 'galleries']

real	0m0.080s
user	0m0.059s
sys	0m0.021s

Ok so how fast is the Go version? My guess was half the time, 40ms. I was way off. Try 6ms. SIX! That’s amazing to do the exact same amount of work.

$ time ./sm-photo-tool list invalid blah
ERROR: valid options are [album galleries]

real	0m0.006s
user	0m0.001s
sys	0m0.005s

Control your caps

On all of my computers I use gnome-tweak-tool to remap CAPSLOCK to CTRL. But there are times when I’m working in a virtual machine that doesn’t have that setup so I need to go back to old school methods.

If you’re on a box just run this from any terminal:

setxkbmap -option ctrl:nocaps

That avoids trying to kill a program with C which usually doesn’t work 🙂

Private Golang, reporting for duty

Many modern programming languages have a mechanism for controlling the visibility of members and methods. Most of the languages use keywords like private and public.

Java and C++ use public, private, and protected. Ruby also uses keywords: private, and protected.

Python on the other hand uses convention to control visibility, prefixing your methods with _ or __. Even then it’s more of a suggestion in Python as you can call it anyway. There is nothing to prevent it from actually being called from other code. It’s an understanding among Python developers that if there is an underscore you shouldn’t call it.

Go also has no keyword to control visibility. What no keyword? There has to be something? Maybe it uses _ like python? Nope that won’t do anything. Then what would Go use to control visibility? Go uses a very simple syntax, capitalization.

CAPITALIZATION? What? Yes, that’s correct. A simple capital letter makes the member or method public. Lowercase member, methods are private.

// GetName is a public method of User struct
func (u *User) GetName() string {
  return u.name
}

// nickName is a private method of User struct
func (u *User) nickName() {
  // do something
}

The same logic applies to members. Given the following example struct, Foo is public and bar is private:

type Example struct {
  Foo string
  bar string
}

So if you want to make your Go methods or members private, just start them with a lowercase letter. Want others to use it, capitalize it.