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 (¤t_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