Home » Odeon Blogs »

Stefan Talpalaru, CTO

CHDK support for IXUS 220HS 1.01G

If you have a Canon IXUS 220HS (ELPH 300HS) camera with the 1.01G firmware version, this is your lucky day: now you can run CHDK on it.


Category: CHDK

Leave a Comment

stabilizing skype with gdb

We all know that Skype is a terrible piece of software, with the Linux version buggy and left behind the main code base, but lately it's been crashing like a Top Gear funny men in an overpowered car. Each time it starts and there are chat windows with new messages to be shown, the damn thing segfaults (at least on 64 bit, statically linked against Qt). While trying to find more details about the problem, I stumbled upon an unlikely fix: running skype under gdb seems to stabilize it. Is it the delay introduced by the debugger interfering with some concurrency bugs or some other mechanism at work? No idea. Just that this is how I run the rubbish right now:

LD_LIBRARY_PATH="/opt/skype" gdb /opt/skype/skype

and then type 'r' in the debugger. It hasn't crashed once under gdb so there's no stack trace available.


Category: Linux

Leave a Comment

learned it the hard way

If you enable the SandyBridge New Acceleration (SNA) in the xf86-video-intel driver on a GM965 chipset with a dual-monitor setup your GPU will hang with the kernel DRM driver crying like a little girl and both screens going blank. While the rest of xorg is still working and your current session is accessible through x11vnc, you will need to reboot to restore the video output.


Category: Linux

Leave a Comment

redis is the new memcached

One of the first things you learn about optimizing web applications is how to cache processed data in memory. Just install memcached - the easy to use key-value store - and you're set. Until you reach its limits, that is...

Memcached does not allow keys larger than 250 bytes or values bigger than 1MB. What happens when you really need bigger keys/values? You move on to redis.

If you are using a proper framework (and you should) there's probably already a redis plugin for you (here's the Django one). Now all you have to do is install redis and configure it to suit your caching needs. As a rule of thumb, speed and low resource usage are more important than data persistence. Here's my configuration (only the relevant differences from the default redis.conf):

  1. daemonize yes
  2. bind 127.0.0.1
  3. loglevel notice
  4. logfile /var/log/redis/redis.log
  5. dir /var/lib/redis/
  6. maxmemory 67108864


With these settings redis runs as a daemon, listens only on localhost, keeps the logging to a minimum and uses sensible locations for the logs and database. I also set the maximum amount of memory it can use depending on the
available RAM on the server. Adjust as needed.

The 2.4.1 version I'm using is quite fast, probably due to the jemalloc memory allocator, but if you need even more performance you can switch from TCP sockets to unix sockets. Pay attention to the socket file's default permission (755) and either run the daemon under the same user as the client(s), or change the init script to do a chmod after redis is started. Sometimes in the near future you'll be able to set the permission from redis.conf with the unixsocketperm variable and the unix socket will be the preferred way to setup local caching instead of an optimization.

Enjoy your new cache and remember that if you ever need a powerful NoSQL database, redis is there waiting for you.


Category: caching

Leave a Comment

Django redirects in reverse proxy setups

Starting with django-1.3.1 you'll get redirect failures for reverse proxy setups (cherokee and cherrypy in my case). The reason is that relative paths that you feed to HttpResponseRedirect are converted into absolute URLs using META['HTTP_HOST'] which is actually the IP of the reverse proxy. To fix this add the following line to settings.py:

  1. USE_X_FORWARDED_HOST = True

More details in the release notes.


Category: Django

1 Comment

automating remote commands over SSH with paramiko

I don't know how high ranking python is for automating system administration tasks but when I had to script remote ssh commands, I thought I'd give python a try. The main SSH library is paramiko and, while lacking in the documentation department, it's rather rich in features.

Besides the usual root login, I had a more difficult use case: using a regular user with full sudo rights instead of the superuser. These are the functions I came up with:

  1. import paramiko
  2. import getpass

  3. def ssh_connection(user, host, port=22, password=None, key_filename=None):
  4. """
  5. with password='' you will be prompted for a password when the script runs
  6. """
  7. ssh = paramiko.SSHClient()
  8. ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  9. if password == '':
  10. # ask for one on stdin
  11. password = getpass.getpass('Password for %s@%s: ' % (user, host))
  12. ssh.connect(host, port=port, username=user, password=password, key_filename=key_filename)
  13. # custom attributes
  14. ssh.user = user
  15. if user == 'root':
  16. ssh.homedir = '/root'
  17. else:
  18. ssh.homedir = '/home/%s' % user
  19. ssh.password = password
  20. ssh.use_sudo = False
  21. return ssh

  22. def run_remote(ssh, cmd, check_exit_status=True, verbose=True):
  23. chan = ssh.get_transport().open_session()
  24. stdin = chan.makefile('wb')
  25. stdout = chan.makefile('rb')
  26. stderr = chan.makefile_stderr('rb')
  27. processed_cmd = cmd
  28. if ssh.use_sudo:
  29. processed_cmd = 'sudo -S bash -c "%s"' % cmd.replace('"', '\\"')
  30. chan.exec_command(processed_cmd)
  31. if stdout.channel.closed is False: # If stdout is still open then sudo is asking us for a password
  32. stdin.write('%s\n' % ssh.password)
  33. stdin.flush()
  34. result = {
  35. 'stdout': [],
  36. 'stderr': [],
  37. }
  38. exit_status = chan.recv_exit_status()
  39. result['exit_status'] = exit_status
  40. def print_output():
  41. for line in stdout:
  42. result['stdout'].append(line)
  43. print line,
  44. for line in stderr:
  45. result['stderr'].append(line)
  46. print line,
  47. if check_exit_status and exit_status != 0:
  48. print_output()
  49. print 'non-zero exit status (%d) when running "%s"' % (exit_status, cmd)
  50. exit(exit_status)
  51. if verbose:
  52. print processed_cmd
  53. print_output()
  54. return result

Notice the cool multiline command string that works even with sudo. We need to pass multiple commands because paramiko opens a new session for each exec_command() invocation so the current directory is reset on subsequent calls. Let's see some examples:
  1. # set up a ssh connection for root and ask for the password interactively
  2. myconn = ssh_connection('root', 'example.com', password='')
  3. # run some commands with the default settings
  4. run_remote(myconn,
  5. """
  6. pwd
  7. cd /var/log
  8. pwd
  9. ls -la
  10. """)

  11. # set up a connection for a regular user with full sudo rights and use it for running commands with root privileges
  12. sudoconn = ssh_connection('jim', 'example.com', password='imanewbieandileavepasswordsintextfiles')
  13. sudoconn.use_sudo = True
  14. run_remote(sudoconn,
  15. """
  16. whoami
  17. pwd
  18. cd /var/log
  19. pwd
  20. ls -la
  21. """)

You can suppress the exit code checking and the verbosity if you want and handle that info from the returned dictionary any way it suits you.

Bottom line, it might not be easy to use paramiko's API directly but it's trivial to use my functions so go ahead and script those repetitive administration tasks. As always when doing stuff as root, try not to hose the server ;-)


Category: Python

4 Comments

using python to upload photos on Flickr

Flickr's flash uploader has a little problem: it will fail to upload certain files. I can't find a pattern here - all the files come from the same digital camera. Anyway, the problem I was facing was finding out which of the photos in a certain directory need to be uploaded again (and do that by other means than the buggy flash object). The photos already uploaded where added to  certain set so getting that info shouldn't be complicated, right?

It seems that that folks writing software with upload functionality haven't taken this case into consideration (at least for the Linux software I've tried), so I had to write my own. Since python is my preferred language right now, I located a Flickr API for it (named flickrapi, oddly enough) and an ebuild for the latest version. Here's the quick and dirty result:

  1. #!/usr/bin/env python

  2. import flickrapi
  3. import os
  4. import sys

  5. api_key = 'your own'
  6. api_secret = 'your own'

  7. flickr = flickrapi.FlickrAPI(api_key, api_secret)

  8. (token, frob) = flickr.get_token_part_one(perms='write')
  9. if not token:
  10. raw_input("Press ENTER after you authorized this program")
  11. flickr.get_token_part_two((token, frob))

  12. photosets = flickr.photosets_getList()

  13. def update_photoset(photoset_id, photo_dir, max_uploads=None):
  14. """
  15. set max_uploads to 1 while debugging to upload only one photo per run
  16. """
  17. uploaded_photos = []
  18. disk_photos = []
  19. to_upload = []

  20. for photo in flickr.walk_set(photoset_id):
  21. uploaded_photos.append('%s.JPG' % photo.get('title'))

  22. for photo in os.listdir(photo_dir):
  23. disk_photos.append(photo)
  24. disk_photos.sort()

  25. for photo in disk_photos:
  26. if photo not in uploaded_photos:
  27. to_upload.append(photo)

  28. i = 0
  29. for photo in to_upload:
  30. photo_path = os.path.join(photo_dir, photo)
  31. print photo,
  32. sys.stdout.flush()
  33. result = flickr.upload(filename=photo_path, is_public=u'0', is_family=u'1', is_friend=u'1')
  34. print
  35. sys.stdout.flush()
  36. photo_id = result.find('photoid').text
  37. flickr.photosets_addPhoto(photoset_id=photoset_id, photo_id=photo_id)

  38. i += 1
  39. if max_uploads and i == max_uploads:
  40. break

  41. if __name__ == '__main__':
  42. update_photoset(u'12345678901234', '/home/bart/photos/2011-07-24', max_uploads=None)

The assumptions made are:
- the name of the uploaded photos are derived from the file names (by dropping the extension)
- the extension is '.JPG'
- all the photos in a directory go in the same set
- the Flickr user who owns the set is the same as the one who generated the API key and secret
- the photos are all going to be available to friends and family only

If some things don't apply to you, feel free to modify the script to your liking. It can be improved by implementing command line arguments, automated set creation, multiple dirs/sets handling, restart uploads on network timeouts, etc. Another direction to explore is debugging why the callback doesn't work for flickr.upload() - that would make for a nice progress display.

My workflow right now is:
- upload 1 photo using the flash uploader
- create a set and add that photo to it (for some reason Flickr doesn't like empty sets)
- put the set id and directory path in the script
- run the script
- if the script is interrupted by network problems, run it again

Have fun.


Category: Python

6 Comments

installing postgis on Gentoo

In order to use GeoDjango I had to install postgis on a Gentoo unstable system. This is how I did it (if I did it).

First, I had to fix the postgis-1.5.2 ebuild. You can get it from here. Corresponding bugzilla item here.

After installing the ebuild in my custom overlay, I ran the following commands in a root shell:

  1. emerge postgis
  2. echo "template_postgis" >> /etc/conf.d/postgis_dbs # default GeoDjango template. used for tests
  3. echo "my_db" >> /etc/conf.d/postgis_dbs # the existing db used by my Django project
  4. emerge --config postgis


And that's all there is to it. The '--config' step creates new databases/templates and spatially enables or soft-upgrades existing ones.


Categories: Django Gentoo

Leave a Comment

upgrading bash on OS X

If you've noticed that the bash that comes with OS X is outdated and you're bothered by it, this post is for you. First, install macports if you don't have it already. Open a terminal (I prefer iTerm but the default one will do just fine) and get a root shell:

  1. $ sudo -s

Install bash:
  1. # port -cv install bash

Add it to the list of allowed login shells
  1. # echo /opt/local/bin/bash >> /etc/shells

Now you can switch to it for any account by running:
  1. chsh -s /opt/local/bin/bash

and get a nice prompt, export global variables, define aliases, function and whatnot in a $HOME/.bashrc file. Feel free to use mine if you want.

In order to load your .bashrc automatically on each terminal tab you need to do one more thing (as root):
  1. # chmod u+w /etc/bashrc

and at the end of /etc/bashrc add this line:
  1. [ -f "$HOME"/.bashrc ] && . "$HOME"/.bashrc

Finally, you can start a new terminal session, echo $BASH_VERSION and be smug about it ;-)


Category: OS X

3 Comments

there's an add-on for that

If your browsing habits are anything like mine, then you've collected a lot of open tabs in Firefox. The browser starts slower because it needs to load them all, runs a bit slower and uses a lot of RAM. Wouldn't it be nice to postpone the tab loading until you click on it? BarTab does just that. Besides the default settings, it also allows you to delay the loading of background tabs (useful if you middle-click a bunch of links and don't want the CPU/RAM usage to spike) and to unload background tabs after a certain interval (with a white-list of excepted sites).


Category: Firefox

1 Comment
Page generated in: 0.47s