Automatic Pidgin status with DBUS and NetworkManager

It’s official.  I have a new mistress and her name is DBUS.  After seeing how easy it was to take an action when a particular storage device was attached, I was hooked.  Since I know NetworkManager has a robust DBUS interface , I decided to try something a little more interesting.

Something I’ve always wanted is for Pidgin to be aware of my network connectivity and adjust my status accordingly.  I work from home most of the time, so I set my status to indicate this fact and expose my home office phone number for colleagues.  I go into the office so occasionally that I never remember to change my Pidgin status accordingly, thus confusing people who were expecting me to be in that day.

I set out to write something to effectively bridge NetworkManager’s DBUS interface with that of Pidgin.  There would need to be some logic in the middle to determine the proper status message given the current state of the network.  First, I wrote a python module to query NetworkManager for details about activated devices.  Next, I wrote a (probably overly complicated) location module that reads a config file and provides an interface to return properties of the current location, based on the information about the current active device, as determined by the previous module.  The config file looks something like this:

[Home]
devices = eth0
subnet = 192.168.1
caption = At Home
foobar = baz

[Home-Wireless]
devices = ath0
subnet = 192.168.1
caption = At Home (wireless)

[Work]
devices = eth0
subnet = 10.0.12
caption = At the office

This lets me match settings based on not only a bit of the IP address, but also on the device name.  With those two data points, I can have separate status messages for Home and Home-Wireless.

Next, all I needed was something to watch for NetworkManager’s DBUS signal that the active device has changed, determine the new location, and tell Pidgin about it.  After seeing some examples , I found that it is surprisingly simple :

import dbus
import gobject
import dbus.mainloop.glib

import locations
import sys

def change_status(id, caption):
global pidgin

status = pidgin.PurpleSavedstatusNew(“”, 2)
pidgin.PurpleSavedstatusSetMessage(status, caption)
pidgin.PurpleSavedstatusActivate(status)

def dev_changed(*args, **kwargs):
if not kwargs[“member”] == “DeviceNowActive”:
return

loc = locations.LocationService(sys.argv[1]).current_location()
change_status(loc.name, loc.caption)

def monitor_nm():
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

bus = dbus.SystemBus()
bus.add_signal_receiver(dev_changed,
interface_keyword=”dbus_interface”,
member_keyword=”member”Smilie: ;)

loop = gobject.MainLoop()
loop.run()

def connect_to_pidgin():
global pidgin

bus = dbus.SessionBus()
obj = bus.get_object(“im.pidgin.purple.PurpleService”,
“/im/pidgin/purple/PurpleObject”Smilie: ;)
pidgin = dbus.Interface(obj, “im.pidgin.purple.PurpleInterface”Smilie: ;)

if len(sys.argv) < 2:
print “Usage: %s <location_config>” % sys.argv[0]
sys.exit(1)

connect_to_pidgin()
monitor_nm()

The dev_changed() function gets called for each signal (I think there must be a better way to select specific signals you’re interested, but this is quick and it works).  If the signal matches the correct name, then we determine the current location and call change_status() to tell Pidgin about it.  How awesome is that?

I’ve thought about generalizing this into a “Location Awareness Service” that would emit signals on DBUS to notify other applications.  It would be really awesome to write something that would tell Asterisk to forward my extension to another line, depending on where I am:

  • If I’m at “Home”, ring my desk line
  • If I’m at “Home-Wireless”, ring the house phones
  • If I’m at “Work”, forward to my work line
  • If I’m anywhere else, forward to my cell phone

On the other hand, maybe I’ll hold off on that last item… Smilie: :)

Posted in Codemonkeying, Linux

SBLIM and their broken ABI

The CMPI standard defines the interface between a conforming provider and the CIMOM that loads it.  It’s a massive set of data structures, types, and functions that allow the CIM server to delegate requests to providers, and for providers to make use of broker services.  Like any standard ABI, it must be standard and uniform to allow any provider binary to be loadable in any CIMOM.

Yesterday, I was debugging a strange crash that I was seeing with Pegasus (as shipped in Fedora Smilie: 8), which was related to libcmpiutil’s asynchronous indication support.  The indication is triggered using the CBInvokeMethod broker callback function.  Adding debug print statements galore, I tracked it down to where the actual callback was being made.  Since I can’t (and don’t want to) add debug print statements to Pegasus, I re-ran the whole thing in gdb.  Sure enough, the crash is somewhere in Pegasus.  However, I spotted something very strange in the stack trace:

#0  Pegasus::value2CIMValue (data=0x2aaab285e312, type=63920, rc=0x41e00e7c)
at CMPI_Value.cpp:70
#1  0x00002aaab16b7e2c in mbSetProperty (mb=0x5555558ab1d0,
ctx=0x5555558ab1d0, cop=0x41e01450, name=0x555555864550 “”,
val=0x2aaab285e312, type=63920) at CMPI_Broker.cpp:562
#2  0x00002aaab285d97d in stdi_trigger_indication ()
from /usr/lib64/libcmpiutil.so.0
#3  0x00002aaab18ec099 in trigger_indication (context=0x41e01450,
base_type=<value optimized out>, ns=0x555555829f30 “root/virt”Smilie: ;)
at Virt_VirtualSystemManagementService.c:362
<snip>

The stdi_trigger_indication() function is where the CBInvokeMethod() call is made, so why is the next stack frame in the mbSetProperty() function?  I stewed on this a bit and then happened to notice that in cmpift.h, setProperty comes right after invokeMethod.  This was the key to the puzzle.  Somehow, my _CMPIBrokerFT function table was off by a void*, which meant that I neatly executed the next function down, which was sure to fail.  But why?

I noticed that there were two cmpift.h files on my system, one in /usr/include/cmpi, belonging to the sblim-cmpi-devel package, and another in /usr/include/Pegasus/CMPI belonging to the tog-pegasus package.  I thought it was a long shot, but I wrote a tiny program to print the size of the _CMPIBrokerFT structure, and compiled it against each, and sure enough: the sblim version was 8 bytes longer!

Upon further investigation, I found the problem.  The very first parameter of _CMPIBrokerFT was an unsigned long in the sblim case and an unsigned int in the Pegasus case.  On a 32-bit platform, they are both 4-byte types, but on a 64-bit machine, the unsigned long is 8 bytes.  Neatly aligning the first function pointer to an 8-byte boundary meant that all my functions still “worked”, but in an off-by-one crash-you-where-you-least-expect-it kinda way.

I reported it to the sblim mailing list and got a confirmation that indeed the sblim version of cmpift.h is wrong and needs to change.  Until then, make sure you don’t have the sblim headers on your machine when compiling 64-bit providers to be used under Pegasus or you’ll regret it!

Posted in Codemonkeying Tagged , ,

Easier method providers with libcmpiutil

While writing the libvirt-cim provider, I decided that the constant repetition of common tasks in CMPI CIM providers was getting old.  There were lots of things that get done over and over again that really should be generalized for the sake of the people who have to read and maintain the provider code.  Fetching values from argument lists, instances, and object paths are some of the first tasks that I aimed to fix with libcmpiutil.  In addition to these standardized utility functions, I created a standardized method dispatch infrastructure that makes writing method providers as simple and clean as it should have been in the first place.

Most of the CMPI providers I’ve seen (I can’t say I’m a real expert here) implement a method provider like this:

CMPIStatus Foo_InvokeMethod(CMPIMethodMI *self,
const CMPIContext *context,
const CMPIResult *results,
const CMPIObjectPath *reference,
const char *methodname,
const CMPIArgs *argsin,
CMPIArgs *argsout)
{
if (strcmp(methodname, “MethodA”Smilie: ;) == 0) {
/* Do MethodA work */
} else if (strcmp(methodname, “MethodB”Smilie: ;) == 0) {
/* Do MethodB work */
} else {
/* Return no such method */
}
}

If you’re lucky, the author will have written separate functions for each task (MethodA, MethodB, etc), however many of the providers I’ve seen just put the “work” in the body of the if, which results in hundreds of lines of muck that implements every possible method of the class in a massive function-from-hell.  At the very least, the dispatch that fires off the appropriate code depending on the method name is implemented in every provider.  Further, each method implementation must fetch and check its arguments for presence and type.  Can you imagine how disgusting this gets for a class with 10 methods, each with a few arguments, when written by someone who doesn’t understand the benefit of functional depcomposition?

Enter the std_invokemethod module of libcmpiutil.  Not only is the dispatch code modularized in such a way that “encourages” (read: “forces”Smilie: ;) the author to write separate functions, it also does argument checking.  It makes sure that all of your parameters are present and of the correct type before it calls your handler function.  The handler can then grab the arguments without excessive checks at every stage.  The above method provider becomes this:

static CMPIStatus methodA(CMPIMethodMI *self,
const CMPIContext *context,
const CMPIResult *results,
const CMPIObjectPath *reference,
const CMPIArgs *argsin,
CMPIArgs *argsout)
{
/* Do MethodA work */
}

/* Another function here for MethodB */

static struct method_handler _methodA = {
.name = “MethodA”,
.handler = methodA,
.args = {{“ParamX”, CMPI_uint16},
{“ParamY”, CMPI_string},
ARG_END
}
};

/* Another handler struct here for MethodB */

static struct method_handler *my_handlers[] = {
&_methodA,
&_methodB,
NULL
};

Instead of using the standard MI creation macro, you use a special one for the std_invokemethod module, which takes a pointer to the handler list.  Then, you sit back and relax as your handler functions are dispatched pre-checked for argument type correctness!

Posted in Codemonkeying Tagged , ,

Using DBUS to take action on a USB storage device

I keep my password-encrypted SSH and PGP keys on a small USB key.  I do this to keep these sensitive files separate from my laptop, in case I were to lose it.  I figure it’s easier to keep a USB key on me whenever I’m at work or traveling.  I don’t want to have to keep my USB key inserted all the time to encrypt files and emails or SSH to remote hosts.  Even though that would probably be more secure, I think I’d wear out the flash memory and would probably get annoyed before that even happened.  So, I want to cache the sensitive data in memory, so that it’s lost on powerdown, but available to me while working normally.

Luckily, ssh-agent provides this sort of functionality already, but GPG does not (that I know of).  What I want is some way for me to just insert my key and have the PGP files copied to a volatile location and the SSH keys inserted into the agent automatically (prompting for a password first, of course).  I could do this with an autorun file on the key itself, but then inserting it into random machines might copy my sensitive files, which would be really bad.  Since DBUS seems to be pretty nifty, I figured I should see about writing a little app to respond to the DBUS mount event to run a script for me.  I don’t know much about DBUS, so it seemed like a good exercise.

At first, I saw a Linux Journal article about doing this with udev rules, but that isn’t as nice because you don’t have DISPLAY set, and it doesn’t run as your own user.  Still workable, but less nice.  So, I googled for examples of using DBUS from Python and came up with the following dispatch app:

#!/usr/bin/python

DEVICE_TAG=”dansmith”
WORK_SCRIPT=”/home/dan/bin/sshkey.sh”

import dbus
import gobject
import dbus.mainloop.glib
import os

def handler(*args, **kwargs):
if not kwargs[“member”] == “VolumeMountedSignal”:
return

uri = args[0][4].replace(“file://”, “”Smilie: ;)
tag = args[0][6]

if tag == DEVICE_TAG:
os.system(“%s %s” % (WORK_SCRIPT, uri))

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
bus.add_signal_receiver(handler,
interface_keyword=”dbus_interface”,
member_keyword=”member”Smilie: ;)

loop = gobject.MainLoop()
loop.run()

I can then exec this as part of my gnome session startup.  The app listens for the “VolumeMountedSignal” and checks the volume label against what I know my USB key has.  If it matches, it runs my little helper script.  The script mounts a tmpfs in a subdirectory of $HOME, copies the PGP files into place, and then runs ssh-add on all of the SSH keys.  Since DISPLAY is set, I get the GUI ssh-askpass box to unlock each key.

It would be interesting to extend this little dispatch app to have a GUI interface to let you add device labels and associated actions.  It might be handy to have a couple things like “exec script foo”, “copy contents to /path/bar”, “freshen a tarball backup”, etc.

Posted in Codemonkeying, Linux Tagged ,

Finally: T41p, radeon, and DVI hotplug

Maybe I'm just an idiot, but I've never been able to use the open-source radeon driver with my T41p.  The main reason being that the radeon driver seemed doomed to permanent suckage when it came to using my external 1600×1200 DVI flat panel.  Sure, I could boot up with the lid closed and the DVI plugged in and then reboot when moving to the couch to use the integrated display, but come on, that's ridiculous.

Recent versions of the evil fglrx driver seemed to solve my problem with the dynamic –enable-monitor flag.  I wrote a little script that took the output of –query-monitor and stuffed it into –enable-monitor.  This meant any time I switched configs, I could just run that script and it would enable whatever monitors were connected.  I could then use xrandr to change the resolution to what I wanted and be happy.

Ubuntu Gutsy Gibbon promised to make this all better.  However, when I upgraded, the new display settings tool didn't seem to do much for me.  It detected my external flat panel as the laptop display and I couldn't make it do anything with minimal work.  So, I restored my xorg.conf with the fglrx settings and went back to what I had.  However, fglrx in Gutsy seemed to break my ability to suspend, which is something I had just gotten used to.

This afternoon, I sat down determined to put some more time into the new bits and see if I could make something work.  First, I moved my xorg.conf file away (to a safe hiding spot) and restarted X.  I then went into the display config tool and forced the laptop display to 1400×1050.  It didn't show my external monitor, so I saved the settings and restarted.  When I got back in, my external display showed up, model undetected, but present.  So, I configured the model and resolution, saved, and restarted.  I got nothing.  The login screen showed in 1400×1050 on the internal display, and an offset 1600×1200 on the external, but after login the external display went black and I just had the internal display.

From there, I opened up a terminal to run the command-line xrandr program.  Wow, has that changed!  It now shows me connected monitors, resolutions, etc:

Screen 0: minimum 320 x 200, current 1600 x 1200, maximum 1600 x 1200
VGA-0 disconnected (normal left inverted right)
DVI-0 connected 1600×1200+0+0 (normal left inverted right) 367mm x 275mm
   1600×1200      60.0*+   59.9  
   1280×1024      75.0     59.9  
   1152×864       74.8  
   1024×768       75.1     60.0  
   800×600        75.0     60.3  
   640×480        75.0     60.0  
   720×400        70.1  
LVDS connected (normal left inverted right)
   1400×1050      50.0 +
   1280×1024      59.9  
   1280×960       59.9  
   1280×800       60.0  
   1280×768       60.0  
   1024×768       60.0     59.9  
   800×600        60.3     59.9  
   640×480        59.9     59.4  
S-video disconnected (normal left inverted right)

 Playing around a little, I noticed that I could turn on and off either display, by doing:

$ xrandr –output DVI-0 –off
$ xrandr –output DVI-0 –auto
$ xrandr –output LVDS –off
$ xrandr –output LVDS –auto

The current resolution would switch to 1400×1050 when the LVDS output was enabled (its maximum) and 1600×1200 when the LVDS output was disabled (the DVI-0 maximum).  This is perfect!  All I need is for this to happen when the lid ACPI event triggers.  If the lid is open, enable LVDS.  If it's closed, disable LVDS.  For both cases, "auto" mode for DVI-0 should be fine.  So, I wrote the following script:

 #!/bin/bash

. /usr/share/acpi-support/power-funcs
. /usr/share/acpi-support/policy-funcs
. /etc/default/acpi-support

do_it() {
    STATE=$(cat /proc/acpi/button/lid/*/state | awk '{print $2}'Smilie: ;)
    
    if [ "$STATE" = "closed" ]; then
        echo Setting LVDS off
        xrandr –output LVDS –off
    else
        echo Setting LVDS on
        xrandr –output LVDS –auto
    fi
    
    echo Setting DVI auto
    xrandr –output DVI-0 –auto
}

# Give the displays a chance to settle
sleep 2

for x in /tmp/.X11-unix/*; do
    displaynum=`echo $x | sed s#/tmp/.X11-unix/X##`
    getXuser;
    if [ x"$XAUTHORITY" != x"" ]; then
        export DISPLAY=":$displaynum"
    do_it
    fi
done

Next, I created a /etc/acpi/local/lid.sh.pre file to call my script and marked it executable (you could put the above in the lid.sh.pre itself, of course).  I copied the display setting code from /etc/acpi/lid.sh.  This ensures that DISPLAY is set appropriately so that xrandr will work correctly.

Now, when I cycle my lid (or press the little sunken button myself for testing) the display output and resolution switch automagically.  Not only that, but I can even suspend my machine again Smilie: :)

I would have really hoped that Gutsy would bring this level of polish with it.  Maybe it did and I'm just completely inept, but I didn't find any mention of it working flawlessly in the first page of a google search, so I assume I'm not alone here. 

Posted in Linux

A fireplace blower retrofit

We have a Heatilator GNDC30 direct-vent fireplace unit in our house.  It's wired for a blower unit, but the builder didn't install it (which is pretty common).  Instead of paying someone to come out and stick one in, I decided to order the blower itself and give it a shot.

The blower I got was a non-OEM replacement for the manufacturer's FK21 fan kit.  I'm sure that the OEM kit has a bracket that holds the fan in place under the fire box, but this one did not.  It really only needs to sit under the firebox and shoot air up the back

Th e blower itself is just a small squirrel cage with an AC motor and a variable motor control.  The motor is a little too heavy, so it leans to one side if you don't hold it in place.  During test-fitting I noticed that not only does it lean to one side, but that the opening of the fan is larger than the slot that carries air up the back of the firebox.  I made a little box out of sheet aluminum and stuffed a roll of non-slip in the middle to give me a cutom-sized vibration-reducing block to hold the motor level.  The difference in duct sizing causes a lot of wind noise even at low speeds.  To remedy this, I made a little duct flap out of sheet aluminum to point the air appropriately.  Here are the pieces.  And the final result with the flap installed in the blower housing.

To further reduce noise and vibration, I cut another piece of non-slip to set the whole thing on once it's in-place.  I would tend to bet that the stock unit just mounts to the sheet metal and thus could potentially rattle.

Next, I just slid the whole thing into place in the back of the firebox, and put the little support box under the motor.

It's a little hackish (okay, maybe a lot hackish), but it is a retrofit.  Hopefully tonight we can test it out and see if it makes a difference in heating the living room Smilie: :)

Posted in Hardware

Libvirt-CIM: out in the wild

I'm happy to say that today the official announcement was made, which is the final step in bringing libvirt-cim out into the open.  I have worked for the last several months on this internally with a few other colleagues, intending to take it open at some point.

There is still a lot of work to do to get it into the key distros, expand its platform support, and work out all the kinks.  But it feels good to finally have it out in the wild after all this time! 

Posted in Codemonkeying

Garage door monitor

Taylor and I both seem to be completely incapable of remembering to close the garage door in all but the most simple arrival scenarios.  Okay, maybe it’s me more often that not, but still, it’s a problem.  Several times, we have gone to leave in the morning and found that the garage door is already open.  Being in such a dense neighborhood, this is also a point of embarrassment, not just security.

So, as I do whenever I need a part of my home automated, I went to SmartHome in search of a handy device to help solve my problem.  I found some devices that attempt to close the door if you leave it open, but it seemed like user reviews were less than favorable.  I finally settled on this one from Chamberlain that just monitors the door and offers a solid green light when it’s closed and a blinking red light when it’s open.  I would prefer to have a buzzer attached to a timer to offer an auditory cue when the door needs checking, but it’s a start.

Of course, I have considered hacking the device and putting a timer/buzzer in place.  Taylor doesn’t like the idea of a hand-made project box being anywhere visible, although I think I might just give it a shot and see how “bad” it is.  I’ve also thought of making my own, using these neato serial RF transmitters and receivers I found, in combination with encoder/decoder chips.  However, I don’t see any reasonable way to have multiple devices on different frequencies, which means I wouldn’t want to waste 8 digital I/O channels on just the garage door.  Now I feel compelled to find something else to automate!

Posted in Hardware

Sensor Hack

Well, after being so happy with my proof that the server room A/C unit wasn't cycling too often, I realized that my sampling rate was low enough (10 minutes) that if it was cycling really often, I wouldn't necessarily be able to measure it.  After some old-fashioned empirical analysis (i.e. watching the unit for about 30 minutes), I decided that something needed to be done.

The real problem is that the A/C unit is being too aggressive at keeping the set temperature.  A single degree of difference will cause it to kick in.  With such a small space and such an oversized unit, it doesn't take long to cool the room, or for it to heat itself back up a degree.  Thus, I needed a way to introduce a multi-degree differential, but without modifying the electronics in the unit.  The condenser talks some serial protocol to the compressor unit, so trying to be smart and switching the signal line isn't an option.  It causes a communication error and forces the whole thing to be restarted.  Thus, I resorted to a physical solution.

 The temperature sensor in the condenser unit was mounted inside next to the coil, which has the main fan constantly circulating air over it.  This makes it very sensitive to the room temperature.  To solve this, I pulled the sensor (which has a nice long cable, thank you $manufacturer) out of the unit.  I put it inside a small length of thick poly tubing, and stuffed some fiberglass insulation in either end of it.  The result is a nice long cycle period and a very distinctive sawtooth shape to my temperature plot.

 

Image 

Posted in Hardware

Server Room Temps

I have an over-sized A/C unit for my server room, which is both good and bad.  It's good because I have plenty of capacity and can keep the room at 64 degrees in 100+ degree weather (if I so desire) with my current heat load.  This means I could add more equipment and not worry about being able to cool it.  It's bad because it means that the A/C unit might be short-cycling.

Short-cycling means that the compressor might be starting and stopping too often, which reduces the life of the most expensive component.  During the winter, I seem to notice the unit cycling on and off a lot, so I was worried.  Since I have temperature monitoring and logging of the room, I decided to extract and plot some data to see if I could see definite temperature swings this way, and thus figure out how often it's cycling.

I plotted a 12-hour subset of data from a relatively normal 40-degree day, and found that the swings were very obvious.  I counted about twenty cycles in the 12-hour period, indicating about two cycles per hour, which is definitely within the normal range.

Image

Posted in Hardware