Shallow Thoughts : : Sep

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Wed, 29 Sep 2010

"Who am I?" Maybe nobody!

We hit an interesting problem at work recently. A coworker made a deb package which, during installation, needed to figure out the ID of the user running it, so it could make files writable by that user. Of course, while a package is being installed it's run by root, so the trick is to find out who you were before you sudoed or sued to root.

He was using the command who am i -- reasonable, since it's been a staple since the early days of Unix. For those not familiar with the command, /usr/bin/who, if given two arguments, regardless of what those arguments are, will print information about the current logged-in user. It also offers a -m option to do the same thing. So who am i, who a b, and who -m should all print a line like:

$ who am i
akkana   pts/1        2010-09-29 09:33 (:0.0)

Except they don't. For me, they printed nothing at all -- which broke my colleague's install script.

A quick poll among friends on IRC showed that who am i worked for some people, failed for others, with no obvious logic to it.

It's the terminal

It took some digging to find out what was going on, but the difference turned out to be the terminal being used. The who program -- with or without -m -- gets its info from /var/run/utmp, a file that maintains a record of who's logged in to the system. And it turns out some terminals create a utmp entry, while others don't. So:
Program Creates utmp entry?
gnome-terminal yes
konsole yes
xterm no
xfterm4 yes
terminator no
rxvt no
roxterm yes

I use xterm myself. Xterm is documented (in its man page) to modify the utmp entry, and it has a command-line flat, +ut, plus two X resources, ptyHandshake and utmpInhibit. None of the three work: setting

XTerm*ptyHandshake: true
XTerm*utmpInhibit: false
then running xterm +ut still doesn't show up in who. I guess that's a bug in xterm (or Ubuntu's version of xterm).

How do you get the real user?

Okay, so who am i clearly isn't a reliable way of getting the user ID. What can you use instead?

Several people suggested the id program. It has a -r option which supposedly prints the real UID. Unfortunately, what it really does is print:

$ id -r
id: cannot print only names or real IDs in default format
The man page doesn't offer any suggestions how to use a format other than default, so we're kinda stuck there.

Update: people keep suggesting id -ru to me. Evidently I wasn't very clear in this article: the goal is to get the real id of the login user. In other words, if you're logged in as mary and using sudo, you want mary, not root.

Alas, adding -u to id's flags gets only the effective user id: -u wins over -r. This is very easy to test: sudo id -ru prints 0, as does id -ru inside su.

But elly on Freenode had a great suggestion:

stat -c '%U' `readlink /proc/self/fd/0`
What does this do?

/proc/self is a symlink to /proc/pid, a directory where you can find out all sorts of information about a process.

One of the things you can find out about a process is open file descriptors: in particular, standard input, output and error. So /proc/self/fd/0 corresponds to standard input of the current process -- which in the example above is readlink.

What is readlink? Well, /proc/self/fd/0, in the normal case, is actually a symlink to the terminal controlling the process. readlink prints the file to which that link points -- for instance, /dev/pts/1. That's the terminal being used.

Now that we know the name of the terminal, all we need to do is find out who owns it. (This is the information who am i would have gotten from utmp, had there been a utmp entry.) ls -l /dev/pts/1 will show you that it's you, even if you run it as sudo ls -l /dev/pts/1. You could take that and strip off fields to get the username, but stat, as elly suggested, is a much better way of doing that.

Put it all together, and stat -c '%U' `readlink /proc/self/fd/0 gets standard input for the current process, follows the link to get the controlling terminal, then finds out who owns that terminal.

That's you!

A similar but slightly shorter solution suggested by Mikachu: stat -c %u `tty`

Tags: ,
[ 17:39 Sep 29, 2010    More linux/cmdline | permalink to this entry | comments ]

Sun, 26 Sep 2010

GIMP Wallpaper script improvements

Dave was using some old vacation photos to test filesystem performance, and that made me realize that I had beautiful photos from the same trip that I hadn't yet turned into desktop backgrounds.

Sometimes I think that my GIMP Wallpaper script is the most useful of the GIMP plug-ins I've written. It's such a simple thing ... but I bet I use it more than any of my other plug-ins, and since I normally make backgrounds for at least two resolutions (my 1680x1050 desktop and my 1366x768 laptop), it certainly saves me a lot of time and hassle.

But an hour into my background-making, I started to have nagging doubts. I wasn't renaming these images, just keeping the original filenames from the camera, like pict0828.jpg. What if if some of these were overwriting images of the same name? The one thing my script doesn't do is check for that, and gimp_file_save doesn't pop up any warnings. I've always meant to add a check for it.

Of course, once the doubts started, I had to stop generating backgrounds and start generating code. And I'm happy with the result: wallpaper-0.4.py warns and won't let you save over an old background image, but keeps all the logic in one dialog rather than popping up extra warnings.

[wallpaper.py overwrite warning dialog]

Now I can generate backgrounds without worrying that I'm stomping on earlier ones.

Tags: , ,
[ 22:25 Sep 26, 2010    More gimp | permalink to this entry | comments ]

Thu, 23 Sep 2010

Snakes on a Couch! Using Python with CouchDB

I've been learning CouchDB, the hot NoSQL database, as part of my new job. It's interesting -- a very different mindset compared to classic databases like MySQL.

There's a fairly good Python package for it, python-couchdb ... but the documentation is somewhat incomplete and there's very little else written about it, and virtually no sample code to steal.

That makes it a perfect topic for a Linux Planet tutorial! So here it is, Part 1:

Snakes on a Couch! Using Python with CouchDB.

I have a rather fun application for the database I introduce in the article, but you'll have to wait until Part 2, two weeks from now, to see the details.

Tags: , , , ,
[ 11:55 Sep 23, 2010    More writing | permalink to this entry | comments ]

Mon, 13 Sep 2010

Of Laptops, Docking Stations and Changing Resolution

I've been setting up a new Lenovo X201 laptop at work. (Yes, work -- I've somehow fallen into an actual job where I go in to an actual office at least some of the time. How novel!)

At the office I have a Lenovo docking station, attached to a monitor and keyboard. The monitor, naturally, has a different resolution from the laptop's own monitor.

Under Gnome and compiz, when I plugged in the monitor, I could either let the monitor mirror the laptop display -- in which case X would refuse to work at greater than 1024x768, much smaller than the native resolution of either the laptop screen or the external monitor -- or I could call up the classic two-monitor configuration dialog, where I could configure the external monitor to be its correct size and sit alongside the computer's monitor. I had to do this every time I plugged in.

If I wanted to work on the big screen, then when I undocked, I had to drag all the windows on all desktops back to the built-in LCD first, or they'd be lost. Using just the external monitor and turning off the laptop screen didn't seem to be an allowed option.

That all lasted for about two days. Gnome and I just don't get along. Pretty soon gdm was mysteriously refusing to let me log in (probably didn't like my under-1000 user id), and after wasting half a day fighting it I gave up and reverted with relief to my familiar Openbox desktop.

But now I'm in the Openbox world and don't have that dialog anyway. What are my options?

xrandr monitor detection

Fortunately, I already knew about using xrandr to send to a projector; it was only a little more complicated using it for the monitor in the docking station. Running xrandr with no arguments prints all the displays it currently sees, so you can tell whether an external display or projector is connected and even what resolutions it supports.

I used that for a code snippet in my .xinitrc:

# Check whether the external monitor is connected: returns 0 on success
xrandr | grep VGA | grep " connected "
if [ $? -eq 0 ]; then
  xrandr --output VGA1 --mode 1600x900;
  xrandr --output LVDS1 --off
else
  xrandr --output VGA1 --off
  xrandr --output LVDS1 --mode 1280x800
fi

That worked nicely. When I start X it checks for an external monitor, and if it finds one it turns off the laptop display (so it's off when the laptop is sitting closed in the docking station) and sets the screen size for the external monitor.

Making it automatic

All well and good. I worked happily all day in the docking station, suspended the laptop and un-docked it, brought it home, woke it up -- and of course the display was still off. Oops.

Okay, so it also needs the same check when resuming from suspend. That used to be in /etc/acpi/resume.d, but in Lucid they've moved it (because we definitely wouldn't want users to get complacent and think they know how to configure things!) and now it lives in /etc/pm/sleep.d. I created a new file, /etc/pm/sleep.d/20_enable_display which looks like this:

#!/bin/sh

case "$1" in
    resume)
        # Check whether the external monitor is connected:
        # returns 0 on success
        xrandr | grep VGA | grep " connected "
        if [ $? -eq 0 ]; then
            xrandr --output VGA1 --mode 1600x900
            xrandr --output LVDS1 --off
        else
            xrandr --output VGA1 --off
            xrandr --output LVDS1 --mode 1280x800
        fi
        hsetroot -center `find -L $HOME/Backgrounds -name "*.*" | $HOME/bin/randomline`

        ;;
    suspend|hibernate)
        ;;
    *)
        ;;
esac
exit $?

Neat! Now, every time I wake from suspend, the laptop checks whether an external monitor is connected and sets the resolution accordingly. And it re-sets the background (using my random wallpaper method) so I don't get a tiled background on the big monitor.

Update: hsetroot -fill works better than -center given that I'm displaying background images on two different resolutions. Of course, if I wanted to get fancy I could make separate background sets, one for each monitor, and choose images from the appropriate set.

We're almost done. Two more possible adjustments.

Detecting undocking

First, while poking around in /etc/acpi I noticed a script named undock.sh. In theory, I can put the same code snippet in there, and then if I un-dock the laptop without suspending it first, it will immediately change resolution. I haven't actually tried that yet.

Projectors

Second, this business of turning off the built-in display if there's anything plugged into the VGA port is going to break if I use this laptop for presentations, since a projector will also show up as VGA1. So the code may need to be a little smarter. For example:

xrandr | grep VGA | grep " connected " | grep 16.0x

The theory here is that an external monitor will be able to do 1680 or 1600, so it will have a line like VGA1 connected 1680x1050+0+0 (normal left inverted right x axis y axis) 434mm x 270mm. The 1680x matches the 16.0x pattern in grep. A projector isn't likely to do more than 1280, so it won't match the pattern '16.0x'. However, that isn't very robust; it will probably fail for one of those fancy new 1920x1080 monitors. You could extend it with

xrandr | grep VGA | grep " connected " | egrep '16.0x|19.0x'
but that's getting even more hacky ... and it might be time to start writing some more intelligent code.

Which doubtless I'll do if I ever get a 1920x1080 monitor.

Tags: , ,
[ 23:11 Sep 13, 2010    More linux/laptop | permalink to this entry | comments ]

Thu, 09 Sep 2010

Hugin part 2: Rescuing Difficult Panoramas

[tricky Hugin panorama] Part 2 in my Hugin series is out, in which I discuss how to rescue difficult panoramas that confuse Hugin.

Hugin is an amazing program, but if you get outside the bounds of the normal "Assistant" steps, the user interface can be a bit confusing -- and sometimes it does things that are Just Plain Weird. But with help from some folks on IRC, I found out that a newer version of Hugin can fix those problems, and worked out how to do it (as well as lots of ways that seemed like they should work, but didn't).

Read the gory details in: Hugin part 2: Rescuing Difficult Panoramas.

There will be a Hugin Part 3, and possibly even a Part 4, discussing things Hugin can do beyond panoramas.

Tags: , , , ,
[ 14:58 Sep 09, 2010    More writing | permalink to this entry | comments ]

Fri, 03 Sep 2010

Fontasia v 0.3

A couple of weeks ago I posted about fontasia, my new font-chooser app. [Fontasia: font viewer/categorizer It's gone through a couple of revisions since then, and Mikael Magnusson contributed several excellent improvements, like being able to render each font in the font list.

I'd been holding off on posting 0.3, hoping to have time to do something about the font buttons -- they really need to be smaller, so there's space for more categories. But between a new job and several other commitments, I haven't had time to implement that. And the fancy font list is so cool it really ought to be shared.

So here it is: fontasia 0.3.

Tags: , ,
[ 10:31 Sep 03, 2010    More programming | permalink to this entry | comments ]