• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include <X11/Xlib.h>
36 #include <X11/Xatom.h>
37 
38 Display *xdisplay = NULL;
39 static Atom selection_atom;
40 static Atom address_atom;
41 static Atom pid_atom;
42 
43 static int
x_io_error_handler(Display * xdisplay)44 x_io_error_handler (Display *xdisplay)
45 {
46   verbose ("X IO error\n");
47   kill_bus_and_exit (0);
48   return 0;
49 }
50 
51 static void
remove_prefix(char * s,char * prefix)52 remove_prefix (char *s,
53                char *prefix)
54 {
55   int plen;
56 
57   plen = strlen (prefix);
58 
59   if (strncmp (s, prefix, plen) == 0)
60     {
61       memmove (s, s + plen, strlen (s) - plen + 1);
62     }
63 }
64 
65 static const char*
get_homedir(void)66 get_homedir (void)
67 {
68   const char *home;
69 
70   home = getenv ("HOME");
71   if (home == NULL)
72     {
73       /* try from the user database */
74       struct passwd *user = getpwuid (getuid());
75       if (user != NULL)
76         home = user->pw_dir;
77     }
78 
79   if (home == NULL)
80     {
81       fprintf (stderr, "Can't get user home directory\n");
82       exit (1);
83     }
84 
85   return home;
86 }
87 
88 #define DBUS_DIR ".dbus"
89 #define DBUS_SESSION_BUS_DIR "session-bus"
90 
91 static char *
get_session_file(void)92 get_session_file (void)
93 {
94   static const char prefix[] = "/" DBUS_DIR "/" DBUS_SESSION_BUS_DIR "/";
95   const char *machine;
96   const char *home;
97   char *display;
98   char *result;
99   char *p;
100 
101   machine = get_machine_uuid ();
102   if (machine == NULL)
103     return NULL;
104 
105   display = xstrdup (getenv ("DISPLAY"));
106   if (display == NULL)
107     {
108       verbose ("X11 integration disabled because X11 is not running\n");
109       return NULL;
110     }
111 
112   /* remove the screen part of the display name */
113   p = strrchr (display, ':');
114   if (p != NULL)
115     {
116       for ( ; *p; ++p)
117         {
118           if (*p == '.')
119             {
120               *p = '\0';
121               break;
122             }
123         }
124     }
125 
126   /* Note that we leave the hostname in the display most of the
127    * time. The idea is that we want to be per-(machine,display,user)
128    * triplet to be extra-sure we get a bus we can connect to. Ideally
129    * we'd recognize when the hostname matches the machine we're on in
130    * all cases; we do try to drop localhost and localhost.localdomain
131    * as a special common case so that alternate spellings of DISPLAY
132    * don't result in extra bus instances.
133    *
134    * We also kill the ":" if there's nothing in front of it. This
135    * avoids an ugly double underscore in the filename.
136    */
137   remove_prefix (display, "localhost.localdomain:");
138   remove_prefix (display, "localhost:");
139   remove_prefix (display, ":");
140 
141   /* replace the : in the display with _ if the : is still there.
142    * use _ instead of - since it can't be in hostnames.
143    */
144   for (p = display; *p; ++p)
145     {
146       if (*p == ':')
147         *p = '_';
148     }
149 
150   home = get_homedir ();
151 
152   result = malloc (strlen (home) + strlen (prefix) + strlen (machine) +
153                    strlen (display) + 2);
154   if (result == NULL)
155     {
156       /* out of memory */
157       free (display);
158       return NULL;
159     }
160 
161   strcpy (result, home);
162   strcat (result, prefix);
163   strcat (result, machine);
164   strcat (result, "-");
165   strcat (result, display);
166   free (display);
167 
168   verbose ("session file: %s\n", result);
169   return result;
170 }
171 
172 static void
ensure_session_directory(void)173 ensure_session_directory (void)
174 {
175   const char *home;
176   char *dir;
177 
178   home = get_homedir ();
179 
180   /* be sure we have space for / and nul */
181   dir = malloc (strlen (home) + strlen (DBUS_DIR) + strlen (DBUS_SESSION_BUS_DIR) + 3);
182   if (dir == NULL)
183     {
184       fprintf (stderr, "no memory\n");
185       exit (1);
186     }
187 
188   strcpy (dir, home);
189   strcat (dir, "/");
190   strcat (dir, DBUS_DIR);
191 
192   if (mkdir (dir, 0700) < 0)
193     {
194       /* only print a warning here, writing the session file itself will fail later */
195       if (errno != EEXIST)
196         fprintf (stderr, "Unable to create %s\n", dir);
197     }
198 
199   strcat (dir, "/");
200   strcat (dir, DBUS_SESSION_BUS_DIR);
201 
202   if (mkdir (dir, 0700) < 0)
203     {
204       /* only print a warning here, writing the session file itself will fail later */
205       if (errno != EEXIST)
206         fprintf (stderr, "Unable to create %s\n", dir);
207     }
208 
209   free (dir);
210 }
211 
212 static Display *
open_x11(void)213 open_x11 (void)
214 {
215   if (xdisplay != NULL)
216     return xdisplay;
217 
218   xdisplay = XOpenDisplay (NULL);
219   if (xdisplay != NULL)
220     {
221       verbose ("Connected to X11 display '%s'\n", DisplayString (xdisplay));
222       XSetIOErrorHandler (x_io_error_handler);
223     }
224   return xdisplay;
225 }
226 
227 static int
init_x_atoms(Display * display)228 init_x_atoms (Display *display)
229 {
230   static const char selection_prefix[] = "_DBUS_SESSION_BUS_SELECTION_";
231   static const char address_prefix[] = "_DBUS_SESSION_BUS_ADDRESS";
232   static const char pid_prefix[] = "_DBUS_SESSION_BUS_PID";
233   static int init = FALSE;
234   char *atom_name;
235   const char *machine;
236   char *user_name;
237   struct passwd *user;
238 
239   if (init)
240     return TRUE;
241 
242   machine = get_machine_uuid ();
243   if (machine == NULL)
244     return FALSE;
245 
246   user = getpwuid (getuid ());
247   if (user == NULL)
248     {
249       verbose ("Could not determine the user informations; aborting X11 integration.\n");
250       return FALSE;
251     }
252   user_name = xstrdup(user->pw_name);
253 
254   atom_name = malloc (strlen (machine) + strlen (user_name) + 2 +
255                       MAX (strlen (selection_prefix),
256                            MAX (strlen (address_prefix),
257                                 strlen (pid_prefix))));
258   if (atom_name == NULL)
259     {
260       verbose ("Could not create X11 atoms; aborting X11 integration.\n");
261       free (user_name);
262       return FALSE;
263     }
264 
265   /* create the selection atom */
266   strcpy (atom_name, selection_prefix);
267   strcat (atom_name, user_name);
268   strcat (atom_name, "_");
269   strcat (atom_name, machine);
270   selection_atom = XInternAtom (display, atom_name, FALSE);
271 
272   /* create the address property atom */
273   strcpy (atom_name, address_prefix);
274   address_atom = XInternAtom (display, atom_name, FALSE);
275 
276   /* create the PID property atom */
277   strcpy (atom_name, pid_prefix);
278   pid_atom = XInternAtom (display, atom_name, FALSE);
279 
280   free (atom_name);
281   free (user_name);
282   init = TRUE;
283   return TRUE;
284 }
285 
286 /*
287  * Gets the daemon address from the X11 display.
288  * Returns FALSE if there was an error. Returning
289  * TRUE does not mean the address exists.
290  */
291 int
x11_get_address(char ** paddress,pid_t * pid,long * wid)292 x11_get_address (char **paddress, pid_t *pid, long *wid)
293 {
294   Atom type;
295   Window owner;
296   int format;
297   unsigned long items;
298   unsigned long after;
299   char *data;
300 
301   *paddress = NULL;
302 
303   /* locate the selection owner */
304   owner = XGetSelectionOwner (xdisplay, selection_atom);
305   if (owner == None)
306     return TRUE;                /* no owner */
307   if (wid != NULL)
308     *wid = (long) owner;
309 
310   /* get the bus address */
311   XGetWindowProperty (xdisplay, owner, address_atom, 0, 1024, False,
312                       XA_STRING, &type, &format, &items, &after,
313                       (unsigned char **) &data);
314   if (type == None || after != 0 || data == NULL || format != 8)
315     return FALSE;               /* error */
316 
317   *paddress = xstrdup (data);
318   XFree (data);
319 
320   /* get the PID */
321   if (pid != NULL)
322     {
323       *pid = 0;
324       XGetWindowProperty (xdisplay, owner, pid_atom, 0, sizeof pid, False,
325                           XA_CARDINAL, &type, &format, &items, &after,
326                           (unsigned char **) &data);
327       if (type != None && after == 0 && data != NULL && format == 32)
328         *pid = (pid_t) *(long*) data;
329       XFree (data);
330     }
331 
332   return TRUE;                  /* success */
333 }
334 
335 /*
336  * Saves the address in the X11 display. Returns 0 on success.
337  * If an error occurs, returns -1. If the selection already exists,
338  * returns 1. (i.e. another daemon is already running)
339  */
340 static Window
set_address_in_x11(char * address,pid_t pid)341 set_address_in_x11(char *address, pid_t pid)
342 {
343   char *current_address;
344   Window wid;
345   int pid32;
346 
347   /* lock the X11 display to make sure we're doing this atomically */
348   XGrabServer (xdisplay);
349 
350   if (!x11_get_address (&current_address, NULL, NULL))
351     {
352       /* error! */
353       XUngrabServer (xdisplay);
354       return None;
355     }
356 
357   if (current_address != NULL)
358     {
359       /* someone saved the address in the meantime */
360       XUngrabServer (xdisplay);
361       free (current_address);
362       return None;
363     }
364 
365   /* Create our window */
366   wid = XCreateSimpleWindow (xdisplay, RootWindow (xdisplay, 0), -20, -20, 10, 10,
367                              0, WhitePixel (xdisplay, 0),
368                              BlackPixel (xdisplay, 0));
369   verbose ("Created window %d\n", wid);
370 
371   /* Save the property in the window */
372   XChangeProperty (xdisplay, wid, address_atom, XA_STRING, 8, PropModeReplace,
373                    (unsigned char *)address, strlen (address));
374   pid32 = pid;
375   if (sizeof(pid32) != 4)
376     {
377       fprintf (stderr, "int is not 32 bits!\n");
378       exit (1);
379     }
380   XChangeProperty (xdisplay, wid, pid_atom, XA_CARDINAL, 32, PropModeReplace,
381                    (unsigned char *)&pid32, 1);
382 
383   /* Now grab the selection */
384   XSetSelectionOwner (xdisplay, selection_atom, wid, CurrentTime);
385 
386   /* Ungrab the server to let other people use it too */
387   XUngrabServer (xdisplay);
388 
389   XFlush (xdisplay);
390 
391   return wid;
392 }
393 
394 /*
395  * Saves the session address in session file. Returns TRUE on
396  * success, FALSE if an error occurs.
397  */
398 static int
set_address_in_file(char * address,pid_t pid,Window wid)399 set_address_in_file (char *address, pid_t pid, Window wid)
400 {
401   char *session_file;
402   FILE *f;
403 
404   ensure_session_directory ();
405   session_file = get_session_file();
406   if (session_file == NULL)
407     return FALSE;
408 
409   f = fopen (session_file, "w");
410   if (f == NULL)
411     return FALSE;               /* some kind of error */
412   fprintf (f,
413            "# This file allows processes on the machine with id %s using \n"
414            "# display %s to find the D-Bus session bus with the below address.\n"
415            "# If the DBUS_SESSION_BUS_ADDRESS environment variable is set, it will\n"
416            "# be used rather than this file.\n"
417            "# See \"man dbus-launch\" for more details.\n"
418            "DBUS_SESSION_BUS_ADDRESS=%s\n"
419            "DBUS_SESSION_BUS_PID=%ld\n"
420            "DBUS_SESSION_BUS_WINDOWID=%ld\n",
421            get_machine_uuid (),
422            getenv ("DISPLAY"),
423            address, (long)pid, (long)wid);
424 
425   fclose (f);
426   free (session_file);
427 
428   return TRUE;
429 }
430 
431 int
x11_save_address(char * address,pid_t pid,long * wid)432 x11_save_address (char *address, pid_t pid, long *wid)
433 {
434   Window id = set_address_in_x11 (address, pid);
435   if (id != None)
436     {
437       if (!set_address_in_file (address, pid, id))
438         return FALSE;
439 
440       if (wid != NULL)
441         *wid = (long) id;
442       return TRUE;
443     }
444   return FALSE;
445 }
446 
447 int
x11_init(void)448 x11_init (void)
449 {
450   return open_x11 () != NULL && init_x_atoms (xdisplay);
451 }
452 
453 void
x11_handle_event(void)454 x11_handle_event (void)
455 {
456   if (xdisplay != NULL)
457     {
458       while (XPending (xdisplay))
459         {
460           XEvent ignored;
461           XNextEvent (xdisplay, &ignored);
462         }
463     }
464 }
465 
466 #else
dummy_dbus_launch_x11(void)467 void dummy_dbus_launch_x11 (void) { }
468 #endif
469