why linux is powerful or: how to erase half your system and then fix it

after a brief bout of stupidity i quickly realized that my makefile had gone awry and was quickly eating through my filesystem.

after ^C killing it, it seems i only took out most of /lib/* and /usr/sbin/* — who needs those anyways… apparently almost everyone.

what happened next. well it turns out i was lucky and had a few shells and a webbrowser open– attempts to launch new programs will fail, but existing programs are already loaded in memory so i was able to work.

since almost everything was broken, i first had to get dpkg and apt/apt-utils going again. after much anguish, i manually installed the missing library files and binaries from http://packages.ubuntu.com/ and i was on my way with apt.

if you’re manually installing files from .deb’s use:
dpkg -x <package_file.deb> outputfolder/

which will let you get in and use mv and cp to put back the missing .so files.
once some basic tools were working, you can try and fix up your tool chain doing things like:
sudo apt-get --reinstall install <package_name>

it’s good to do this to apt-utils, dpkg, and whatever other utilities are throwing library errors. the packages themselves need various utilities installed, and as you get a missing abc.so file, find out what package it needs with:
dpkg -S <filename>

my apt seems to be back, but many utilities still aren’t. finding out what should go in /lib was a little harder but i was able to get a list of packages like this:

(find out what *should* be installed)
dpkg --get-selections > installed-software

(list which files come from these packages, sort and uniq it)
dpkg -L `cat installed-software` | sort | uniq > uniq-software

(find what we deleted)
cat uniq-software | grep ^/lib/ > missing-software

(which packages does this come from, sort and uniq it. (might get too long argument list))
dpkg -S `cat missing-software` > package-list

(find the package name by itself, get rid of the colons, sort, uniq)
cat package-list | awk '{print $1}' | sed -e 's/://' | sed -e 's/,//' | sort | uniq > reinstall-me

(do the reinstall)
sudo apt-get --reinstall install `cat reinstall-me`

at the moment this is running, and i luckily had a cd lying around to help speed up the process. use:
apt-cdrom add

(with the cd in the drive) to add it to your /etc/apt/sources.list

turns out this eventually failed with some obscure errors… it ultimately might be a more concise list if you know what got killed, but i think the sheer number of packages i messed up and that needed to get updated somehow confused apt with some cyclical cycles, and i had to go about it slightly more manually and generally.

first of all, when every i had an annoying library error, i ran this script:
#!/bin/bash
in=`echo $1 | sed -e 's/://'`
fix=`dpkg -S $in | awk '{print $1}' | sed -e 's/://'`
sudo apt-get -y --reinstall install $fix

which would reinstall the missing library. since i ended up doing this rather repetitively, it helped a lot.

secondly, i was forced to run something like this:
for i in `cat get-selections`; do
sudo apt-get -y --reinstall install $i;
done

which reinstalls each package. slowly after pruning the list for problem packages, and deleting the successful ones, my system came back. at one point you won’t have to run them individually, and you can run:
sudo apt-get --reinstall install `cat get-selections`

and be done! i finished with a:
do-release-upgrade

and now things are looking great. luckily no important user files were squished, and hopefully this is a good reference for you. i hopefully won’t have to reference it again for all the dpkg commands i’ll forget.

the python subprocess module

i’m sure that i won’t be able to tell you anything revolutionary which can’t be found out by reading the manual, but i thought i would clarify it, and by showing you a specific example which i needed.

subprocess.Popen accepts a bunch or args, one of which is the shell argument, which is False by default. If you specify shell=True then the first argument of popen should be a string which is what gets parsed by the shell and then eventually run. (nothing revolutionary)

the magic happens if you use shell=False (the default), in which case the first argument then accepts an array of arguments to pass. this array exactly transforms to become the sys.argv of the subprocess that you’ve opened with popen. magic!

this means you could pass an argument like: “hello how are you” and it will get received as one element in sys.argv, versus being split up into 4 arguments: “hello”, “how”, “are”, “you”. it’s still possible to try to do some shell quoting magic, and achieve the same result, but it’s much harder that way.


>>> _ = subprocess.Popen(['python', '-c', 'print "dude, this is sweet"'])
>>> dude, this is sweet

vs.


>>> _ = subprocess.Popen("python -c 'print "dude, this isnt so sweet"'", shell=True)
>>> dude, this isnt so sweet

and i’m not 100% sure how i would even add an ascii apostrophe for the isn’t.

the second thing i should mention is that you have to remember that each argument actually needs to be split up; for example:


>>> _ = subprocess.Popen(['ls', '-F', '--human-readable', '-G'])
[ ls output ]

yes it’s true that you can combine flags into one argument, but that’s magic happening inside the program.

all this wouldn’t be powerful if we couldn’t pipe programs together. here is a simple example:


>>> p1 = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(['grep', '-i', 'sda'], stdin=p1.stdout)
[ dmesg output that matches sda ]

i think it’s pretty self explanatory. now let’s say we wanted to add one more stage to the pipeline, but have it be something that usually gets executed with os.system:


p1 = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-i', 'sda'], stdin=p1.stdout)
p3 = subprocess.Popen(['less'], stdin=p2.stdout)
sts = os.waitpid(p3.pid, 0)
print 'all done'

this above example should all be pasted into a file and run; the call to waitpid is important, because it stops the interpreter from continuing on before less has finished executing.

hope this took the learning curve and guessing out of using the new subprocess module, (even though it actually has existed for a while…)

the power to yield a better console interface

as part of a different project, i needed to duplicate some existing terminal magic in python. what i needed to write was something similar to the getch() function in curses. it can be found in: ncurses*/base/lib_getch.c after doing an: apt-get source libncurses5

what’s the magic? i need to stay in a continuous loop reading from the file descriptor, however i want to return periodically so that gobject doesn’t block and the interface can remain responsive. enter: yield, who comes in and saves the day. see the accompanying code for specifics.

as part of the bigger scheme, i wanted to write a console like interface for talking to a dbus server that both allows you to run methods, and receive signals. i wanted to use gobject, and i didn’t want to use threads! and because i wanted to make it pro, i decided it should look and feel like my standard bash shell (except prettier). it’s intended to be easy to use, and running the module will give you an example session. check back for a more complete and expressive code base.

1) if anyone knows who to do a ualarm (not alarm) in python, help is appreciated. either through ctypes or as a c extension for python.

2) please leave me your comments on my greadline module! more features are on the way, such as history and cursor support.

code: http://www.cs.mcgill.ca/~james/code/greadline.tar

cheetah == fortran

turns out the cheetah python templating engine (2.0 since year 2006) is quite reminiscent of fortran (since the 1950’s) in their use of the #slurp directive (cheetah) and the $ string. either one, when appended to the end of a string, remove the implicit newline which usually gets printed. it took me ages to figure out how to suppress newline printing back when i did someone’s fortran homework, now i’ve had to struggle with it all over again.

i’m not a language designer, but it never seemed like the best idea to me! but what do i know? i hope this saves someone an hour of searching.

[py]inotify, polling, gtk and gio

i have this software with a gtk mainloop, using dbus and all that fun stuff that seems to play together nicely. i know about the kernel inotify support, and i wanted it to get integrated with that above stack. i thought i was supposed to do it with pyinotify and io_add_watch, but on closer inspection into the pyinotify code it turns out that it seems to actually use polling! (search for: select.poll)

thinking i was confused, i emailed a friend to see if he could confirm my suspicions. we both weren’t 100% sure, a little searching later i was convinced when i found this blog posting. i’m surprised i didn’t find out about this sooner. in any case, my application seems to be happy now.

as a random side effect, it seems that when a file is written, i still see the G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED *after* the G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT event, which i would have expected to always come last. maybe this is a bug, or maybe this is something magical that $EDITOR is doing- in any case it doesn’t affect me, i just wasn’t sure if it was a bug or not. to make it harder, different editors save the file in different ways. gedit seems to first delete the file, and then create it again– or at least from what i see in the gio debug.

the code i’m using to test all this is:

#!/usr/bin/python
import gtk
import gio
count = 0
def file_changed(monitor, file, unknown, event):
  global count
  print 'debug: %d' % count
  count = count + 1
  print 'm: %s' % monitor
  print 'f: %s' % file
  print 'u: %s' % unknown
  print 'e: %s' % event
  if event == gio.FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
    print "file finished changing"
  print '#'*79
  print 'n'
myfile = gio.File('/tmp/gio')
monitor = myfile.monitor_file()
monitor.connect("changed", file_changed)
gtk.main()

(very similar to the aforementioned blog post)

and if you want to see how i’m using it in the particular app, the latest version of evanescent is now available. (look inside of evanescent-client.py)

logging out of $SESSION

the software (evanescent) that i’m working on is supposed to log out the user from its current X session. originally i had some yucky looking code that ran a kill on gnome-session, which quickly got replaced with:

os.system('gnome-session-save --logout-dialog')

i’ve decided this was still a little crufty, so i’ve recently replaced this with:

bus = dbus.SessionBus()
remote_object = bus.get_object('org.gnome.SessionManager', '/org/gnome/SessionManager')
# specify the dbus_interface in each call
remote_object.Logout(0, dbus_interface='org.gnome.SessionManager')
# or create an Interface wrapper for the remote object
#iface = dbus.Interface(remote_object, 'org.gnome.SessionManager')
#iface.Logout(0)
# introspection is automatically supported
#print remote_object.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable')

documentation can be found, although it took a little digging. the only catch is that this is gnome specific, and you need different code for kde, and each other DE. thanks to http://www.purinchu.net/wp/2009/06/12/oh-fun/ for the kde version of the above.

help! please contribute information on making this work on other platforms, and/or how to detect which platform is running. at the moment my ugly solution is:

if os.name == 'nt': return 'windows'
elif os.getenv('KDE_FULL_SESSION') == 'true': return 'kde'
elif os.getenv('GNOME_DESKTOP_SESSION_ID') != '': return 'gnome'
else: return ''

hth

ps: i’m unable to indent code in wordpress (i’m sure it’s my fault somehow) so the above code was trimmed to give you the right idea, without being complete. leave a message if you want some source files.