Archive for February, 2006

Iris and iChat

Ok, so we can do this now:

That’s presence using Link-Local Messaging (a.k.a. Apple’s Bonjour Chat protocol, now documented in JEP-0174). Presence is delivered via local DNS Service Discovery. Chat is done via peer-to-peer Jabber.

Doing Bonjour presence is not all that new actually. The JDNS subsystem has been able to do it since last year. What is notable this time around is that I’m doing it via the new Iris DNS API, which is an abstraction layer, and JDNS is just one possible backend. And there’s also an avatar / buddy icon. That’s good old icon_32.png from Psi. :)

If you want to try it yourself, have a look at the irisnet command-line tool (now committed to the CVS). Quite a few rough edges remain, but it should work. Here is what I ran:

./irisnet pserv justin@ubuntu _presence._tcp 5298
"txtvers=1,1st=Justin,port.p2pj=5298,status=dnd,
msg=coding on iris stuff,
phsh=b12eebe7b5a2f2673aba2f6eb0f3264939a78f67"
-a null:icon_32.png

Yeah, it’s pretty long. :) It is supposed to be a single line, but I’ve broken it up for readability. “irisnet pserv” means to publish a service. Then it goes: JID, service type, port, list of attributes. The -a means to add an extra DNS record, in this case the avatar image (yeah, iChat delivers avatars over DNS, don’t ask questions).

The value of the phsh attribute is the SHA1 hash of the avatar image in hexidecimal. I used the ’sha1sum’ program to do this, which is present on most Linux installations at least:

$ sha1sum icon_32.png
b12eebe7b5a2f2673aba2f6eb0f3264939a78f67  icon_32.png

I don’t know when we’ll see Link-Local Messaging in Psi, but it shouldn’t be too hard to do at this point.

Comments (8)

ServiceResolver

There are three ways Psi might locate a Jabber service. ServiceResolver covers all three, on all platforms, and makes it easy.

To demonstrate how ServiceResolver works, below you can see some output of my ‘irisnet’ commandline tool (which still in development).

The main way to find a Jabber server is via SRV. ServiceResolver looks up the SRV records of a given type on a given domain, as well as the address (A/AAAA) records for each:

$ ./irisnet rservd sapo.pt _xmpp-client._tcp
[213.13.146.7] port=5222
[213.13.146.8] port=5222

You can also lookup non-SRV (”plain”) services. This is done by pre-supplying the port to use, which is returned with each address record found:

$ ./irisnet rservp gmail.com 5222
[216.239.57.107] port=5222
[64.233.161.107] port=5222
[64.233.171.107] port=5222

Lastly, suppose there’s a JEP-0174 / iChat service instance on the LAN called someguy@macbox. In this case, DNS-SD is used:

$ ./irisnet rservi someguy@macbox _presence._tcp
[192.168.1.156] port=5298

There may be three different input methods, but there is only one output method: a list of address & port pairs. The list is always sorted in the order in which the addresses should be tried. Priorities and weights are taken care of for you.

With cross-platform capability, pluggable backends, and seamless Bonjour support, the Iris DNS system is shaping up to be one of the most simple and capable such systems in existence. I suppose I don’t feel so bad about it taking forever to finish, because we really have something cool here.

Comments (3)

Checking for if the app is a GUI app

#ifdef QT_GUI_LIB
# include <QApplication>
#endif

inline bool is_gui_app()
{
#ifdef QT_GUI_LIB
        return (QApplication::type() != QApplication::Tty);
#else
        return false;
#endif
}

This is enough if you don’t plan to actually touch the Qt GUI API. Otherwise, you’ll have to drag that QT_GUI_LIB ifdef all over your code.

(I’m going to use this to determine whether or not I should set up the Windows console control handler.)

Comments

More on Process Termination

Alright, after a lot of research, I’ve concluded that cross-platform process termination is a tricky beast. It is not possible to have a clean and balanced solution for all platforms, but at the end of this post you’ll find a decent guideline.

On Windows, at the lowest level, a process can only be terminated abruptly, with no chance for the process to cleanup. In other words, it is like Unix’s kill -9. To have a clean termination, the process has to be hinted somehow, and this differs depending on the situation. A GUI app can have a WM_CLOSE event sent to its window. A console app cannot receive WM_CLOSE, so Windows has a separate facility (via SetConsoleCtrlHandler) that the app can use for determining if the parent console window is being closed, or if Ctrl-C has been pressed. What’s interesting about these events, particularly WM_CLOSE, is that they don’t explicitly mean a process should end. For example, Psi, with the system tray active, will never quit from a WM_CLOSE. If the app doesn’t have a GUI window or a console, then all you can do is terminate the process. This is why the Windows Task Manager has two listings from which to end tasks/processes. One sends WM_CLOSE (and, in the case of a console, the proper notification is relayed to the app running inside it), the other outright kills the process.

On Unix (which includes Mac OS X), there is SIGTERM, and it works for any process, console or GUI. The SIGTERM signal is a request to terminate, giving the app the opportunity to perform last minute tasks.

Now, in addition to process termination, there is the notion of “window system shutdown”, which is supported on Windows, X11, and Mac. This is a very important topic, because, at least on Windows and X11, handling of the window system shutdown is generally required for a clean exit (I say “generally”, because on X11 you have a second chance, I’ll explain next). As I already discussed, there is no mechanism to be friendly terminated on Windows. And so it should not be a surprise that Windows uses another hint here, WM_ENDSESSION, to tell you that the system is shutting down. In fact, it first uses a different hint, WM_QUERYENDSESSION, to give your app a chance to perform user interaction (e.g. “Save this Document?”) and possibly reject the shutdown.

All three window systems have varying features. X11 is the most advanced here, and actually has two phases, plus a mechanism for storing session information (like window sizes/positions). I suspect Windows and Mac don’t have advanced session management because they are not remote and can therefore just suspend/hibernate (not to mention that suspending an X server is a rather recent thing). The reason you must handle these shutdown events is because Windows has no other way of terminating your app in a friendly way, and with X11 your app probably won’t get a SIGTERM when the server shuts down. On Mac, I don’t know what happens. Therefore, because I don’t have another answer for you, you must handle the shutdown event on Mac. :)

Now, you might be wondering about why you don’t get a SIGTERM on unix when X11 goes down. Well, think about it, the X server (graphic display) and X client (your app) don’t need to be in the same place. Losing the X server simply means losing a socket. This doesn’t rule out the possibility of some smart window manager sending SIGTERM to local apps, but you can’t rely on this. If you handle nothing else, handle this “X display is gone” scenario, because it should cover all shutdown scenarios. If you lose a remote X connection, I assure you that you won’t get to do the nice X11 window system shutdown procedure (including the session management goodness, sorry). This scenario is what I was referring to about the “second chance”.

What does this all come down to?

- If you are a GUI app, handle window system shutdown (including “display is gone”).

- If you are on Unix, handle signals. You should do this even if you are a GUI app, simply because you can.

- If you are a Windows console app, handle the Windows console events, which incidentally also include window system shutdown events.

- If you are a Windows GUI app, don’t handle console events, that’s just confusing.

Comments (3)

Process Termination Handling

I just threw together some code for handling when a process is requested to terminate. The motive is so I can quit a console application cleanly. With GUI apps, detecting when the user wishes to exit is a trivial task, since any GUI toolkit worth anything will tell you when a window closes (via an ‘X’ button or ALT-F4 or whatever). However, console apps, especially those that run indefinitely, and barring those that have interactive UIs such as curses or prompts, are commonly exited by using Ctrl-C. Fortunately, tracking this kind of event wasn’t too bad to implement. Windows has a handler function specifically for console breaking, and Unix has signals (I trap SIGINT, SIGHUP, and SIGTERM, per the glibc manual).

The JDNS commandline tool has an argument to specify how long it should run, which is just plain silly. And my network interface monitoring test program had a similar option too… Until tonight! Now I can just hit ctrl-C and the app will quit. Clean exits are essential when valgrinding, and so this will make my life much easier.

And now that I’ve thought about it some more, this code may also be useful in GUI apps like Psi, for when a system shutdown occurs. However, I think the Windows code I have is for console only, so that will have to be resolved before it is useful there..

Comments

Bad Behavior has blocked 224 access attempts in the last 7 days.