2009/04/13

WMU-6500FS - Deluge 1.1.5

I just finished a build of the deluge 1.1.5. It is bit outdated (the 1.1.6 version is out now) but I had to solve some problems along the way which taken more time than expected. Once it was finished I did not find a morale to step one bugfix version further.

Build result

[binary] [file list]

Prerequisites

The same as for the previous version plus:
[patched python socket module] [tar 1.22] [bzip2 lib]

Uninstall

If you have the deluge-1.1.0 installed, you have to clean it up first (while preserve all the dependencies).
Stop the deluge daemon if it is running. You can do it via console:
box# deluge --ui=null
>>> halt
>>> quit
Thanks!

or forcibly:
box# killall deluged
Now uninstall the previous version:
dev# cd /mnt/C/
dev# ./filopack.sh --remove deluge-1.1.0
Configuration file .filopack/.config file found and used
Sure to remove deluge-1.1.0 locally at /mnt/C (y/n)?y
...
If you are not using the filopack packaging system, you can remove the previous version as follows:
box# cd /mnt/C/
box# wget http://filodej.ic.cz/filopack/.filopack/deluge-1.1.0.lst
box# xargs rm -f < deluge-1.1.0.lst

Update the system

Before we start installing the new version, we have to update the tar archiver. The one which is part of the busybox has an ugly bug corrupting file names in long paths.
box# ./filopack.sh --download bzip2-1.0.5
Configuration file .filopack/.config file found and used
Retrieving package index... (Connecting to http://filodej.ic.cz)
Downloading package bzip2-1.0.5 from http://filodej.ic.cz ...
connected!

Length: 191 [text/plain]
connected!

Length: 40,244 [application/x-tar]

box# ./filopack.sh --install bzip2-1.0.5
...
box# ./filopack.sh --download tar-1.22
Configuration file .filopack/.config file found and used
Retrieving package index... (Connecting to http://filodej.ic.cz)
Downloading package tar-1.22 from http://filodej.ic.cz ...
connected!

Length: 1,523 [text/plain]
connected!

Length: 625,455 [application/x-tar]

box# ./filopack.sh --install tar-1.22
...

For details about the bug see this section.
Also it may be necessary to download a patched version of python socket library, you can test your system as follows:
box# python -c 'import socket; print socket.gethostbyaddr("80.68.88.204")[2];'
Segmentation fault
... if you encounter the segfault, it is better to download and install the patched python socket library:
box# wget http://filodej.ic.cz/filopack/_socket.so
connected!

Length: 116,767 [text/plain]

box# mv sys/lib/python2.5/lib-dynload/_socket.so{,.backup}
box# mv _socket.so sys/lib/python2.5/lib-dynload/
Now the problem should be fixed:
box# python -c 'import socket; print socket.gethostbyaddr("80.68.88.204")[2];'
['80.68.88.204']
For details about this issue see this section.

Install

After we updated the system we are ready to install the new version:
box# ./filopack.sh --download deluge-1.1.5
Configuration file .filopack/.config file found and used
Retrieving package index... (Connecting to http://filodej.ic.cz)
Downloading package deluge-1.1.5 from http://filodej.ic.cz ...
connected!

Length: 89,430 [text/plain]
connected!

Length: 16,084,717 [application/x-tar]

box# ./filopack.sh --install deluge-1.1.5
Sure to unpack deluge-1.1.5 locally at /mnt/C (y/n)? y
...

Run daemon

Now we are ready to try the daemon, still it is necessary to use the LD_PRELOAD prefix or deluged.sh script:
box# deluged.sh
That's all. Following text just describes details related to the issues I solved. Nothing for ordinary users ;-)

Busybox tar bug

When I run the deluge client (console version) some commands was not properly interpreted:
box# deluge
>>> info
 * unknown command: info
>>> help
 * unknown command: help
I found out that any command is implemented in a separate python file:
box# cd sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg
box# ls deluge/ui/console/commands/
add.py          config.pyc   debug.pyc       help.pyc        __init__.pyc    quit.pyc    rm.pyc
add.pyc         connect.py   halt.py0000755  info.py0000755  pause.py        resume.py
add.pyc0000644  connect.pyc  halt.pyc        info.pyc        pause.pyc       resume.pyc
config.py       debug.py     help.py0000755  __init__.py     quit.py0000755  rm.py
... it seems there are some ill-named files in the command directory, and so the console does not know the commands at all.
Let's find all such corrupted files:
box# find . -name *0000*
./deluge/core/preferencesmanager.pyc0000644
./deluge/ui/console/commands/quit.py0000755
./deluge/ui/console/commands/help.py0000755
./deluge/ui/console/commands/halt.py0000755
./deluge/ui/console/commands/info.py0000755
./deluge/ui/console/commands/add.pyc0000644
./deluge/ui/gtkui/torrentdetails.pyc0000644
./deluge/ui/gtkui/queuedtorrents.pyc0000644
./deluge/ui/gtkui/filtertreeview.pyc0000644
./deluge/ui/webui/page_decorators.py0000755
./deluge/ui/webui/torrent_options.py0000755
./deluge/ui/webui/lib/egg_handler.py0000755
./deluge/ui/webui/lib/egg_render.pyc0000644
./deluge/ui/webui/lib/webpy022/db.py0000755
./deluge/plugins/Label-0.1-py2.5.egg0000644
./deluge/plugins/webuipluginbase.pyc0000644
./deluge/data/pixmaps/checking16.png0000644
./deluge/data/pixmaps/inactive16.png0000644
Let's look also in the deluge tar archive:
box# tar tjvf deluge-1.1.5.tar.bz2 | grep 0000
-rw-r--r-- 0/0     21100 2009-04-01 12:22:09 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/core/preferencesmanager.pyc0000644
-rwxr-xr-x 0/0      1079 2009-04-01 12:22:09 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/console/commands/quit.py0000755
-rwxr-xr-x 0/0      2299 2009-04-01 12:22:09 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/console/commands/help.py0000755
-rwxr-xr-x 0/0      1125 2009-04-01 12:22:09 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/console/commands/halt.py0000755
-rwxr-xr-x 0/0      5296 2009-04-01 12:22:09 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/console/commands/info.py0000755
-rw-r--r-- 0/0      2036 2009-04-01 12:22:09 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/console/commands/add.pyc0000644
-rw-r--r-- 0/0     13688 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/gtkui/torrentdetails.pyc0000644
-rw-r--r-- 0/0      7346 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/gtkui/queuedtorrents.pyc0000644
-rw-r--r-- 0/0     13073 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/gtkui/filtertreeview.pyc0000644
-rwxr-xr-x 0/0      5062 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/webui/page_decorators.py0000755
-rwxr-xr-x 0/0      3233 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/webui/torrent_options.py0000755
-rwxr-xr-x 0/0      1553 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/webui/lib/egg_handler.py0000755
-rw-r--r-- 0/0      1522 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/webui/lib/egg_render.pyc0000644
-rwxr-xr-x 0/0     20480 2009-04-01 12:22:10 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/ui/webui/lib/webpy022/db.py0000755
-rw-r--r-- 0/0     38041 2009-04-01 12:22:11 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/plugins/Label-0.1-py2.5.egg0000644
-rw-r--r-- 0/0      2982 2009-04-01 12:22:11 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/plugins/webuipluginbase.pyc0000644
-rw-r--r-- 0/0       699 2009-04-01 12:22:11 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/data/pixmaps/checking16.png0000644
-rw-r--r-- 0/0       595 2009-04-01 12:22:11 sys/lib/python2.5/site-packages/deluge-1.1.5-py2.5-linux-i686.egg/deluge/data/pixmaps/inactive16.png0000644
At a first glance it seem that the archive is corrupted but when I tried the same operation on my mirror system (on the PC) no corrupted file appeared in the archive.
The difference was that while on the mirror system I have the GNU tar 1.20 installed, on the box there is a busybox version containing tar utility:
box# which tar
/bin/tar
box# ls -l /bin/tar
lrwxrwxrwx 1 root root 7 2008-05-21 13:40 /bin/tar -> busybox
I decided to build the newest GNU tar version (1.22) and install it to the box. A new post containing the build procedure will follow. After the installation the problem disappeared.

Socket related crash

I am not sure whether it was new to this version, but after the installation from time to time I have experienced a weird crash of the deluge daemon. Also the Windows client did not respond for long time when was connected to the daemon running on the box. After some experimenting with the deluge log I decided to debug the daemon to find out what is going on.
I was running the gdbserver on the box:
box# LD_PRELOAD="/usr/lib/libssl.so.0.9.7 /usr/lib/libboost_filesystem-gcc41-mt-1_35.so.1.35.0" gdbserver colinux:2345 `which python` `which deluged` -d
Process /usr/bin/python created; pid = 31831
Listening on port 2345
Remote debugging from host 192.168.1.102
...
Then I was ready to connect to the gdbserver from my mirror system:
deb# gdb `which python`
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-linux-uclibc"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) target remote storage:2345
Remote debugging using storage:2345
0x40000c90 in ?? ()
(gdb) cont
Continuing.
...
After some time when I was connecting and disconnectiong the windows client to the daemon the problem appeared:
[New Thread 32801]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 32801]
0x400a0fb0 in PyString_FromString (str=0xc7c3815b <Address 0xc7c3815b out of bounds>) at Objects/stringobject.c:108
108             size = strlen(str);
(gdb)
It was at the following stack:
(gdb) where
#0  0x400a0fb0 in PyString_FromString (str=0xc7c3815b <Address 0xc7c3815b out of bounds>) at Objects/stringobject.c:108
#1  0x407e8154 in gethost_common (h=0xbb9fdae8, addr=0xbb9fdb08, alen=128, af=2) at /usr/local/src/Python-2.5.2/Modules/socketmodule.c:3048
#2  0x407e5825 in socket_gethostbyaddr (self=0x0, args=0x40f2f48c) at /usr/local/src/Python-2.5.2/Modules/socketmodule.c:3273
#3  0x40094bd2 in PyCFunction_Call (func=0x405ac70c, arg=0x40f2f48c, kw=0x0) at Objects/methodobject.c:108
#4  0x400dcf1a in PyEval_EvalFrameEx (f=0x81f8624, throwflag=0) at Python/ceval.c:3573
#5  0x400de1e6 in PyEval_EvalCodeEx (co=0x405b94e8, globals=0x405b624c, locals=0x0, args=0x820022c, argcount=1, kws=0x8200230, kwcount=0, defs=0x405be518, defcount=1,
    closure=0x0) at Python/ceval.c:2836
#6  0x400dd6d0 in PyEval_EvalFrameEx (f=0x82000e4, throwflag=0) at Python/ceval.c:3669
#7  0x400dda59 in PyEval_EvalFrameEx (f=0x81e77d4, throwflag=0) at Python/ceval.c:3659
#8  0x400de1e6 in PyEval_EvalCodeEx (co=0x405c13c8, globals=0x405b602c, locals=0x0, args=0x406200b0, argcount=4, kws=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0)
    at Python/ceval.c:2836
...
a crash in PyString_FromString seems to be just a consequence, let's look up a bit:
(gdb) up
#1  0x407e8154 in gethost_common (h=0xbb9fdae8, addr=0xbb9fdb08, alen=128, af=2) at /usr/local/src/Python-2.5.2/Modules/socketmodule.c:3048
3048                            tmp = PyString_FromString(*pch);
(gdb) list
3043
3044            /* SF #1511317: h_aliases can be NULL */
3045            if (h->h_aliases) {
3046                    for (pch = h->h_aliases; *pch != NULL; pch++) {
3047                            int status;
3048                            tmp = PyString_FromString(*pch);
3049                            if (tmp == NULL)
3050                                    goto err;
3051
3052                            status = PyList_Append(name_list, tmp);
(gdb) print h
$1 = (struct hostent *) 0xbb9fdae8
(gdb) print *h
$2 = {h_name = 0xbb9f9b00 "filodej.doma", h_aliases = 0x40172891, h_addrtype = 2, h_length = 4, h_addr_list = 0xbb9f9aec}
(gdb) print h->h_aliases
$3 = (char **) 0x40172891
(gdb) print *h->h_aliases
$4 = 0xc7c3815b <Address 0xc7c3815b out of bounds>
... It seems that a host entry should contain an array of strings - aliases - but this array is apparently corrupted - it causes a crash of the C string -> Python string conversion routine. Let's look who is responsible for filling the array:
(gdb) up
#2  0x407e5825 in socket_gethostbyaddr (self=0x0, args=0x40f2f48c) at /usr/local/src/Python-2.5.2/Modules/socketmodule.c:3273
3273            ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), af);
(gdb) list
3268            PyThread_acquire_lock(netdb_lock, 1);
3269    #endif
3270            h = gethostbyaddr(ap, al, af);
3271    #endif /* HAVE_GETHOSTBYNAME_R */
3272            Py_END_ALLOW_THREADS
3273            ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), af);
3274    #ifdef USE_GETHOSTBYNAME_LOCK
3275            PyThread_release_lock(netdb_lock);
3276    #endif
3277            return ret;
The gethostbyaddr seems as a good candidate. At first I wondered whether the function is thread safe (see the following text about uClibc thread safety), but after further debugging I have found out that the apparently thread safe variant gethostbyaddr_r is called in my case, so there should be not threading issue there.
(gdb) cont
Continuing.
[New Thread 1024]
Breakpoint 2 at 0x407e574c: file /usr/local/src/Python-2.5.2/Modules/socketmodule.c, li
Pending breakpoint "socket_gethostbyaddr" resolved
[New Thread 19476]
[Switching to Thread 19476]

Breakpoint 2, socket_gethostbyaddr (self=0x0, args=0x40f27ccc) at /usr/local/src/Python
3229            if (!PyArg_ParseTuple(args, "s:gethostbyaddr", &ip_num))
...
(gdb) next
3252            Py_BEGIN_ALLOW_THREADS
(gdb) next
3255            result = gethostbyaddr_r(ap, al, af,
(gdb) next
3272            Py_END_ALLOW_THREADS
(gdb) print buf
$12 = "À¨\001eè\232?½", '\0' <repeats 16 times>, "filodej.doma\000.in-addr.arpa", '\0' <repeats 16333 times>, "@"
(gdb) next
3273            ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), af);
(gdb) print *h
$14 = {h_name = 0xbd3f9b00 "filodej.doma", h_aliases = 0x40172891, h_addrtype = 2, h_length = 4, h_addr_list = 0xbd3f9aec}
(gdb) print h->h_aliases
$15 = (char **) 0x40172891
(gdb) print *h->h_aliases
$16 = 0xc7c3815b <Address 0xc7c3815b out of bounds>
... after some googling I found some hints in the following forum.
Actually it seem there were two related bugs: the first in gethostbyname_r seems to be fixed now, steps to simply reproduce the problem:
box# python -c 'import socket; print socket.gethostbyname_ex("wh0rd.org")[2];'
['80.68.88.204']
... it was ok in my case, it seems to be fixed in 0.9.27 version.
The second bug is in gethostbyaddr_r function. The step to reproduce is following:
box# python -c 'import socket; print socket.gethostbyaddr("80.68.88.204")[2];'
Segmentation fault
... it crashes on my box. Given you have the Python installed, you can test your configuration the same way.
For the solution I decided not to patch or upgrade the uClibc but rather make a workaround in the python socket library. Zeroing the structure prior to the gethostbyaddr_r call should be enough. Let's modify the python Modules/socketmodule.c file. In the PySocket_gethostbyaddr functon there is a hp_allocated structure we are going to reset to zeroes. Just before the gethostbyaddr_r call we add the following code:
#if   defined(HAVE_GETHOSTBYNAME_R_6_ARG)
        memset((void *) &hp_allocated, '\0', sizeof(hp_allocated));
        result = gethostbyaddr_r(ap, al, af,
                &hp_allocated, buf, buf_len,
                &h, &errnop);
... and rebuild and re-pack the python package:
deb# cd /usr/local/src/Python-2.5.2
deb# make
case $MAKEFLAGS in \
*-s*) LD_LIBRARY_PATH=/usr/local/src/Python-2.5.2::/mnt/C/sys/lib:/mnt/C/sys/X11/lib CC='gcc -pthread' LDSHARED='gcc -pthread -shared' OPT='-DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes' ./python -E ./setup.py -q build;; \
*) LD_LIBRARY_PATH=/usr/local/src/Python-2.5.2::/mnt/C/sys/lib:/mnt/C/sys/X11/lib CC='gcc -pthread' LDSHARED='gcc -pthread -shared' OPT='-DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes' ./python -E ./setup.py build;; \
esac
running build
running build_ext
db.h: found (4, 3) in /mnt/C/sys/include
db lib: using (4, 3) db-4.3
/mnt/C/sys/include/sqlite3.h: version 3.5.8
INFO: Can't locate Tcl/Tk libs and/or headers
building '_socket' extension
gcc -pthread -fPIC -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I. -I/usr/local/src/Python-2.5.2/./Include -I/mnt/C/sys/include -I. -IInclude -I./Include -I/usr/local/include -I/usr/local/src/Python-2.5.2/Include -I/usr/local/src/Python-2.5.2 -c /usr/local/src/Python-2.5.2/Modules/socketmodule.c -o build/temp.linux-i686-2.5/usr/local/src/Python-2.5.2/Modules/socketmodule.o
gcc -pthread -shared -L/mnt/C/sys/lib -L/mnt/C/sys/X11/lib -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes build/temp.linux-i686-2.5/usr/local/src/Python-2.5.2/Modules/socketmodule.o -L/mnt/C/sys/lib -L/mnt/C/sys/X11/lib -L/usr/local/lib -L. -lpython2.5 -o build/lib.linux-i686-2.5/_socket.so
building 'nis' extension
gcc -pthread -fPIC -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I. -I/usr/local/src/Python-2.5.2/./Include -I/mnt/C/sys/include -I. -IInclude -I./Include -I/usr/local/include -I/usr/local/src/Python-2.5.2/Include -I/usr/local/src/Python-2.5.2 -c /usr/local/src/Python-2.5.2/Modules/nismodule.c -o build/temp.linux-i686-2.5/usr/local/src/Python-2.5.2/Modules/nismodule.o
gcc -pthread -shared -L/mnt/C/sys/lib -L/mnt/C/sys/X11/lib -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes build/temp.linux-i686-2.5/usr/local/src/Python-2.5.2/Modules/nismodule.o -L/mnt/C/sys/lib -L/mnt/C/sys/X11/lib -L/usr/local/lib -L. -lnsl -lpython2.5 -o build/lib.linux-i686-2.5/nis.so
*** WARNING: renaming "nis" since importing it failed: dynamic module does not define init function (initnis)
running build_scripts
box# make install
...
deb# cd /mnt/C/
deb# ./filopack.sh -R --pack python-2.5.2
...

Build process

The build process was identical to 1.1.0 version, I just had to create .pth link file in order to be able to remove the duplicate directories:
 
dev# cd /mnt/C/sys/lib/python2.5/site-packages
dev# echo "./deluge-1.1.5-py2.5-linux-i686.egg" > deluge.pth

dev# cd /mnt/C/
dev# ./filopack.sh --pack deluge-1.1.5
Remove all the "site-packages/deluge/" sub-paths (they seem to be redundant) and re-pack the package once more.

Read more...