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