1 /* -*- mode: C; c-file-style: "gnu" -*- */ 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., 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 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 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* 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 * 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 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 * 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 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 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 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 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 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 448 x11_init (void) 449 { 450 return open_x11 () != NULL && init_x_atoms (xdisplay); 451 } 452 453 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 467 void dummy_dbus_launch_x11 (void) { } 468 #endif 469