Home

June 2008

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     
Powered by LiveJournal.com

How to share state with applications for Free Unix-based desktops

So you're writing an application. At some point, you have a problem where you want to be able to talk to an instance of your program while it's running. It turns out, this is fiendishly difficult for Free Unix-based desktops, and many programs get it wrong. On my Fedora 9 system, looking in /tmp, I see listings like gconfd-walters (GConf), hsperfdata_walters (OpenJDK), pulse-walters (Pulseaudio), emacs500 (GNU Emacs). All of these programs are broken. The root cause of this is the failure of Unix to provide a standard per-user, per-machine temporary directory. If we had that, things would be dramatically simpler, but we don't.


You can't use /tmp because it's not per-user. Any time you rely on a well-known name in /tmp (like the above list), your application is subject to denial-of-service attacks if another user creates that file before you log in. Security aside, it's also just harder to deal with because you have to check for whether or not the data in your temporary directory is stale or not (is something listening on that socket, or is it left over from a session crash?), and that's yet more painful code.


You can't use $HOME because it's not per-machine (think of all the sites that subject users to the pain of NFS), and won't always support Unix domain socket files.


Now, there are other solutions; you can use the X server, which is what Firefox does I believe. If your application is single-instance this isn't too bad, but there is an easier way:


Enter solution: DBus


The best solution is to use DBus. The key feature here is that the DBus session bus provides exactly what we want: a per-user, per-machine namespace, as well as a fairly sane IPC system we can use to pass messages. Here's how you use it.



  • Decide on a service name for your application. For the purposes of this discussion, we'll call it org.openssh.Ssh, for no particular reason.

  • In your application startup, try to acquire this name on the session bus, using DBus. If you're successful, your application can now provide a service. To pick a completely random example, this service might be multiplexing SSH connections. If something else already owns the service, then you can act as a client; you might send the message org.openssh.GetControlMasterSocketPath to return the path to another Unix-domain socket, because your application has a non-DBus protocol for communication.


In this example, we just used DBus to retrieve the path to another local, randomly-named Unix domain socket. That's perfectly fine - DBus is not the one true IPC system. Programs like Pulseaudio and OpenSSH already have defined protocols, and there is nothing wrong with that, we don't need to replace them with DBus. However, DBus is the best candidate for a one true atomic session namespace.


Again, all examples used in this blog post are completely hypothetical, and have no relation to the silliness of requiring users to try to hack around this issue on their own computers with variables like ControlMaster and ControlPath to enable a useful feature of your application.

Comments

(Anonymous)

Standard

Perhaps it should be lobbied into a standard that only root should have write rights to /tmp. That would stop the madness pretty fast ;)

(Anonymous)

This is actually long known

At least for GConf and PulseAudio. Unless I remember incorrectly there are plans to make both use DBus. (lkundrak)

per-session

gconf should be per-session, not per-(user,machine)

You're right of course that it should be scoped by dbus, which will make it be correctly per-session.

Re: per-session

Realistically though, we don't really support multiple concurrent desktop sessions, and I don't see that changing soon. So per-user == per-session.

In ssh/VT logins you won't interact with things that use the desktop components generally, so they don't really count as a "session".

Re: per-session

The whole reason multiple sessions break, though, is that gconfd and bonobo-activation-server are f'd up and do things per-(user,machine) and not per-session.

That's why dbus is per-session; DCOP was in KDE, and multiple sessions Just Worked for them due to that.

One of the important points of dbus was to fix this bug. Unfortunately, it got a little stalled on someone actually replacing gconf and nuking all bonobo usage ;-)

Re: per-session

It's not just gconf and Bonobo.

Firefox for example doesn't allow multiple instances (ok, you can use separate profiles, but that's basically equivalent to logging in with a different Unix uid).

Look in ~/.evolution - lots of files in there; key stores, caches, calendars, memo files...I haven't looked, but I'd place money on there being little to no locking around that data.

Eclipse is another - "this workspace is in use".

Even the GNU Bash shell gets it wrong! It doesn't by default save history from multiple instances. Hotwire uses SQLite so it's at least safe to run multiple processes, but it's not guaranteed that they'll see each other's data.

Re: per-session

If KDE really supports multiple sessions without losing data I'd be shocked. Basically nothing can just write a file using the plain POSIX APIs anymore; you have to use a data store designed with concurrency in mind. Or you have to invent a data store with APIs on top of files.

Think about all those random places where application developers want to write files; maybe it's KDE's Pidgin equivalent that wants to write a conversation log. Or a game wants to write a high score file. Etc.


Re: per-session

Well, the way multiple sessions used to work before gconf/bonobo broke them was just the way bash does; "last one wins." As long as you write files atomically, you don't get corrupt data, though you might lose the data from one of the sessions.

But UNIX users more or less understood and expected that. Remember the multiple-session scenario has to do with people already using NFS or AFS shared homedirs with multiple workstations.

In practice the race between two sessions does not really matter in that case, because you're only sitting at one computer at a time, so one of the sessions would generally not be writing anything. "locking by physical presence of user" ;-)

Of course as your data gets to be a complex multi-file cache with a sqlite database indexing it, or something, things are a lot more likely to break. But old-school KDE was just writing .ini files and such.

Re: per-session

KConfig is still based on INI files (they don't have a .ini extension, but they're formatted that way) and therefore has to handle concurrent writes to a single file even within a single session, e.g. kdeglobals may be used by many apps at the same time and written to by several of them. There's no KConfig daemon, each application accesses the files on its own. So having simultaneous sessions doesn't make much of a difference there.

Re: per-session

I guess that's true, you can't do much once you log in to session #2.

One difference is that firefox and bash and eclipse all fail somewhat gracefully... while logging in to session #2 with the gconf/bonobo problems just kind of blows up.

(Anonymous)

Regarding your completely hypothetical example: yay! Now if only SSH could automatically create and destroy master connections as needed without making any particular interactive session the master (and thus not killable without taking down the rest).