2006-09-30 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / tools / dbus-launch-x11.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-launch.h  dbus-launch utility
3  *
4  * Copyright (C) 2006 Thiago Macieira <thiago@kde.org>
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-launch.h"
24
25 #ifdef DBUS_BUILD_X11
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <X11/Xlib.h>
34 #include <X11/Xatom.h>
35
36 Display *xdisplay = NULL;
37 static Atom selection_atom;
38 static Atom address_atom;
39 static Atom pid_atom;
40
41 static int
42 x_io_error_handler (Display *xdisplay)
43 {
44   verbose ("X IO error\n");
45   kill_bus_and_exit (0);
46   return 0;
47 }
48
49 static char *
50 get_local_hostname (void)
51 {
52   static const int increment = 128;
53   static char *cache = NULL;
54   char *buffer = NULL;
55   int size = 0;
56
57   while (cache == NULL)
58     {
59       size += increment;
60       buffer = realloc (buffer, size);
61       if (buffer == NULL)
62         return NULL;            /* out of memory */
63
64       if (gethostname (buffer, size - 1) == -1 &&
65           errno != ENAMETOOLONG)
66         return NULL;
67
68       buffer[size - 1] = '\0';  /* to make sure */
69       cache = buffer;
70     }
71
72   return cache;
73 }
74
75 static char *
76 get_session_file (void)
77 {
78   static const char prefix[] = "/.dbus-session-file_";
79   char *hostname;
80   char *display;
81   char *home;
82   char *result;
83   char *p;
84
85   display = xstrdup (getenv ("DISPLAY"));
86   if (display == NULL)
87     {
88       verbose ("X11 integration disabled because X11 is not running\n");
89       return NULL;
90     }
91
92   /* remove the screen part of the display name */
93   p = strrchr (display, ':');
94   if (p != NULL)
95     for ( ; *p; ++p)
96       if (*p == '.')
97         {
98           *p = '\0';
99           break;
100         }
101
102   /* replace the : in the display with _ */
103   for (p = display; *p; ++p)
104     if (*p == ':')
105       *p = '_';
106
107   hostname = get_local_hostname ();
108   if (hostname == NULL)
109     {
110       /* out of memory */
111       free (display);
112       return NULL;
113     }
114
115   home = getenv ("HOME");
116   if (home == NULL)
117     {
118       /* try from the user database */
119       struct passwd *user = getpwuid (getuid());
120       if (user == NULL)
121         {
122           verbose ("X11 integration disabled because the home directory"
123                    " could not be determined\n");
124           free (display);
125           return NULL;
126         }
127
128       home = user->pw_dir;
129     }
130
131   result = malloc (strlen (home) + strlen (prefix) + strlen (hostname) +
132                    strlen (display) + 2);
133   if (result == NULL)
134     {
135       /* out of memory */
136       free (display);
137       return NULL;
138     }
139
140   strcpy (result, home);
141   strcat (result, prefix);
142   strcat (result, hostname);
143   strcat (result, "_");
144   strcat (result, display);
145   free (display);
146
147   verbose ("session file: %s\n", result);
148   return result;
149 }
150
151 static Display *
152 open_x11 (void)
153 {
154   if (xdisplay != NULL)
155     return xdisplay;
156
157   xdisplay = XOpenDisplay (NULL);
158   if (xdisplay != NULL)
159     {
160       verbose ("Connected to X11 display '%s'\n", DisplayString (xdisplay));
161       XSetIOErrorHandler (x_io_error_handler);
162     }
163   return xdisplay;
164 }
165
166 static int
167 init_x_atoms (Display *display)
168 {
169   static const char selection_prefix[] = "DBUS_SESSION_SELECTION_";
170   static const char address_prefix[] = "DBUS_SESSION_ADDRESS";
171   static const char pid_prefix[] = "DBUS_SESSION_PID";
172   static int init = FALSE;
173   char *atom_name;
174   char *hostname;
175   char *user_name;
176   struct passwd *user;
177
178   if (init)
179     return TRUE;
180
181   user = getpwuid (getuid ());
182   if (user == NULL)
183     {
184       verbose ("Could not determine the user informations; aborting X11 integration.\n");
185       return FALSE;
186     }
187   user_name = xstrdup(user->pw_name);
188
189   hostname = get_local_hostname ();
190   if (hostname == NULL)
191     {
192       verbose ("Could not create X11 atoms; aborting X11 integration.\n");
193       free (user_name);
194       return FALSE;
195     }
196
197   atom_name = malloc (strlen (hostname) + strlen (user_name) + 2 +
198                       MAX (strlen (selection_prefix),
199                            MAX (strlen (address_prefix),
200                                 strlen (pid_prefix))));
201   if (atom_name == NULL)
202     {
203       verbose ("Could not create X11 atoms; aborting X11 integration.\n");
204       free (user_name);
205       return FALSE;
206     }
207
208   /* create the selection atom */
209   strcpy (atom_name, selection_prefix);
210   strcat (atom_name, user_name);
211   strcat (atom_name, "_");
212   strcat (atom_name, hostname);
213   selection_atom = XInternAtom (display, atom_name, FALSE);
214
215   /* create the address property atom */
216   strcpy (atom_name, address_prefix);
217   address_atom = XInternAtom (display, atom_name, FALSE);
218
219   /* create the PID property atom */
220   strcpy (atom_name, pid_prefix);
221   pid_atom = XInternAtom (display, atom_name, FALSE);
222
223   free (atom_name);
224   free (user_name);
225   init = TRUE;
226   return TRUE;
227 }
228
229 /*
230  * Gets the daemon address from the X11 display.
231  * Returns FALSE if there was an error. Returning
232  * TRUE does not mean the address exists.
233  */
234 int
235 x11_get_address (char **paddress, pid_t *pid, long *wid)
236 {
237   Atom type;
238   Window owner;
239   int format;
240   unsigned long items;
241   unsigned long after;
242   char *data;
243
244   *paddress = NULL;
245
246   /* locate the selection owner */
247   owner = XGetSelectionOwner (xdisplay, selection_atom);
248   if (owner == None)
249     return TRUE;                /* no owner */
250   if (wid != NULL)
251     *wid = (long) owner;
252
253   /* get the bus address */
254   XGetWindowProperty (xdisplay, owner, address_atom, 0, 1024, False,
255                       XA_STRING, &type, &format, &items, &after,
256                       (unsigned char **) &data);
257   if (type == None || after != 0 || data == NULL || format != 8)
258     return FALSE;               /* error */
259
260   *paddress = xstrdup (data);
261   XFree (data);
262
263   /* get the PID */
264   if (pid != NULL)
265     {
266       *pid = 0;
267       XGetWindowProperty (xdisplay, owner, pid_atom, 0, sizeof pid, False,
268                           XA_CARDINAL, &type, &format, &items, &after,
269                           (unsigned char **) &data);
270       if (type != None && after == 0 && data != NULL && format == 32)
271         *pid = (pid_t) *(long*) data;
272       XFree (data);
273     }
274
275   return TRUE;                  /* success */
276 }
277
278 /*
279  * Saves the address in the X11 display. Returns 0 on success.
280  * If an error occurs, returns -1. If the selection already exists,
281  * returns 1. (i.e. another daemon is already running)
282  */
283 static Window
284 set_address_in_x11(char *address, pid_t pid)
285 {
286   char *current_address;
287   Window wid;
288
289   /* lock the X11 display to make sure we're doing this atomically */
290   XGrabServer (xdisplay);
291
292   if (!x11_get_address (&current_address, NULL, NULL))
293     {
294       /* error! */
295       XUngrabServer (xdisplay);
296       return None;
297     }
298
299   if (current_address != NULL)
300     {
301       /* someone saved the address in the meantime */
302       XUngrabServer (xdisplay);
303       free (current_address);
304       return None;
305     }
306
307   /* Create our window */
308   wid = XCreateSimpleWindow (xdisplay, RootWindow (xdisplay, 0), -20, -20, 10, 10,
309                              0, WhitePixel (xdisplay, 0),
310                              BlackPixel (xdisplay, 0));
311   verbose ("Created window %d\n", wid);
312
313   /* Save the property in the window */
314   XChangeProperty (xdisplay, wid, address_atom, XA_STRING, 8, PropModeReplace,
315                    (unsigned char *)address, strlen (address));
316   XChangeProperty (xdisplay, wid, pid_atom, XA_CARDINAL, 32, PropModeReplace,
317                    (unsigned char *)&pid, sizeof(pid) / 4);
318
319   /* Now grab the selection */
320   XSetSelectionOwner (xdisplay, selection_atom, wid, CurrentTime);
321
322   /* Ungrab the server to let other people use it too */
323   XUngrabServer (xdisplay);
324
325   XFlush (xdisplay);
326
327   return wid;
328 }
329
330 /*
331  * Saves the session address in session file. Returns TRUE on
332  * success, FALSE if an error occurs.
333  */
334 static int
335 set_address_in_file (char *address, pid_t pid, Window wid)
336 {
337   char *session_file;
338   FILE *f;
339
340   session_file = get_session_file();
341   if (session_file == NULL)
342     return FALSE;
343
344   f = fopen (session_file, "w");
345   if (f == NULL)
346     return FALSE;               /* some kind of error */
347   fprintf (f, "%s\n%ld\n%ld\n", address, (long)pid, (long)wid);
348
349   fclose (f);
350   free (session_file);
351
352   return TRUE;
353 }
354
355 int
356 x11_save_address (char *address, pid_t pid, long *wid)
357 {
358   Window id = set_address_in_x11 (address, pid);
359   if (id != None)
360     {
361       if (!set_address_in_file (address, pid, id))
362         return FALSE;
363
364       if (wid != NULL)
365         *wid = (long) id;
366       return TRUE;
367     }
368   return FALSE;
369 }
370
371 int
372 x11_init (void)
373 {
374   return open_x11 () != NULL && init_x_atoms (xdisplay);
375 }
376
377 void
378 x11_handle_event (void)
379 {
380   if (xdisplay != NULL)
381     {      
382       while (XPending (xdisplay))
383         {
384           XEvent ignored;
385           XNextEvent (xdisplay, &ignored);
386         }
387     }
388 }  
389
390 #else
391 void dummy_dbus_launch_x11 (void) { }
392 #endif