1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-run-session.c - run a child process in its own session
4 * Copyright © 2003-2006 Red Hat, Inc.
5 * Copyright © 2006 Thiago Macieira <thiago@kde.org>
6 * Copyright © 2011-2012 Nokia Corporation
8 * Licensed under the Academic Free License version 2.1
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
35 #include <sys/types.h>
39 #include "dbus/dbus.h"
40 #include "dbus/dbus-internals.h"
42 #define MAX_ADDR_LEN 512
43 #define PIPE_READ_END 0
44 #define PIPE_WRITE_END 1
48 * If you are in a shell and run "dbus-run-session myapp", here is what
49 * happens (compare and contrast with dbus-launch):
52 * \- dbus-run-session myapp
53 * \- dbus-daemon --nofork --print-address --session
56 * All processes are long-running.
58 * When myapp exits, dbus-run-session kills dbus-daemon and terminates.
60 * If dbus-daemon exits, dbus-run-session warns and continues to run.
64 * dbus-daemon --print-address -> bus_address_pipe -> d-r-s
67 static const char me[] = "dbus-run-session";
69 static void usage (int ecode) _DBUS_GNUC_NORETURN;
75 "%s [OPTIONS] [--] PROGRAM [ARGUMENTS]\n"
80 "--dbus-daemon=BINARY run BINARY instead of dbus-daemon\n"
81 "--config-file=FILENAME pass to dbus-daemon instead of --session\n"
87 static void version (void) _DBUS_GNUC_NORETURN;
93 "Copyright (C) 2003-2006 Red Hat, Inc.\n"
94 "Copyright (C) 2006 Thiago Macieira\n"
95 "Copyright © 2011-2012 Nokia Corporation\n"
97 "This is free software; see the source for copying conditions.\n"
98 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
103 static void oom (void) _DBUS_GNUC_NORETURN;
108 fprintf (stderr, "%s: out of memory\n", me);
114 READ_STATUS_OK, /**< Read succeeded */
115 READ_STATUS_ERROR, /**< Some kind of error */
116 READ_STATUS_EOF /**< EOF returned */
127 memset (buf, '\0', maxlen);
128 maxlen -= 1; /* ensure nul term */
130 retval = READ_STATUS_OK;
138 to_read = maxlen - bytes;
146 if (chunk < 0 && errno == EINTR)
151 retval = READ_STATUS_ERROR;
156 retval = READ_STATUS_EOF;
163 if (retval == READ_STATUS_EOF &&
165 retval = READ_STATUS_OK;
168 if (retval != READ_STATUS_ERROR &&
170 buf[bytes-1] == '\n')
177 exec_dbus_daemon (const char *dbus_daemon,
178 int bus_address_pipe[2],
179 const char *config_file)
181 /* Child process, which execs dbus-daemon or dies trying */
182 #define MAX_FD_LEN 64
183 char write_address_fd_as_string[MAX_FD_LEN];
185 close (bus_address_pipe[PIPE_READ_END]);
187 sprintf (write_address_fd_as_string, "%d", bus_address_pipe[PIPE_WRITE_END]);
192 "--print-address", write_address_fd_as_string,
193 config_file ? "--config-file" : "--session",
194 config_file, /* has to be last in this varargs list */
197 fprintf (stderr, "%s: failed to execute message bus daemon '%s': %s\n",
198 me, dbus_daemon, strerror (errno));
201 static void exec_app (int prog_arg, char **argv) _DBUS_GNUC_NORETURN;
204 exec_app (int prog_arg, char **argv)
206 execvp (argv[prog_arg], argv + prog_arg);
208 fprintf (stderr, "%s: failed to exec '%s': %s\n", me, argv[prog_arg],
214 main (int argc, char **argv)
217 int bus_address_pipe[2] = { 0, 0 };
218 const char *config_file = NULL;
219 const char *dbus_daemon = NULL;
220 char bus_address[MAX_ADDR_LEN] = { 0 };
221 const char *prev_arg = NULL;
223 int requires_arg = 0;
229 const char *arg = argv[i];
233 const char **arg_dest;
235 assert (prev_arg != NULL);
237 if (strcmp (prev_arg, "--config-file") == 0)
239 arg_dest = &config_file;
241 else if (strcmp (prev_arg, "--dbus-daemon") == 0)
243 arg_dest = &dbus_daemon;
247 /* shouldn't happen */
248 fprintf (stderr, "%s: internal error: %s not fully implemented\n",
253 if (*arg_dest != NULL)
255 fprintf (stderr, "%s: %s given twice\n", me, prev_arg);
266 if (strcmp (arg, "--help") == 0 ||
267 strcmp (arg, "-h") == 0 ||
268 strcmp (arg, "-?") == 0)
272 else if (strcmp (arg, "--version") == 0)
276 else if (strstr (arg, "--config-file=") == arg)
280 if (config_file != NULL)
282 fprintf (stderr, "%s: --config-file given twice\n", me);
286 file = strchr (arg, '=');
291 else if (strstr (arg, "--dbus-daemon=") == arg)
295 if (dbus_daemon != NULL)
297 fprintf (stderr, "%s: --dbus-daemon given twice\n", me);
301 file = strchr (arg, '=');
306 else if (strcmp (arg, "--config-file") == 0 ||
307 strcmp (arg, "--dbus-daemon") == 0)
311 else if (arg[0] == '-')
313 if (strcmp (arg, "--") != 0)
315 fprintf (stderr, "%s: option '%s' is unknown\n", me, arg);
334 /* "dbus-run-session" and "dbus-run-session ... --" are not allowed:
335 * there must be something to run */
336 if (prog_arg < 1 || prog_arg >= argc)
338 fprintf (stderr, "%s: a non-option argument is required\n", me);
344 fprintf (stderr, "%s: option '%s' requires an argument\n", me, prev_arg);
348 if (dbus_daemon == NULL)
349 dbus_daemon = "dbus-daemon";
351 if (pipe (bus_address_pipe) < 0)
353 fprintf (stderr, "%s: failed to create pipe: %s\n", me, strerror (errno));
361 fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
368 exec_dbus_daemon (dbus_daemon, bus_address_pipe, config_file);
373 close (bus_address_pipe[PIPE_WRITE_END]);
375 switch (read_line (bus_address_pipe[PIPE_READ_END], bus_address, MAX_ADDR_LEN))
380 case READ_STATUS_EOF:
381 fprintf (stderr, "%s: EOF reading address from bus daemon\n", me);
385 case READ_STATUS_ERROR:
386 fprintf (stderr, "%s: error reading address from bus daemon: %s\n",
387 me, strerror (errno));
392 _dbus_assert_not_reached ("invalid read result");
395 close (bus_address_pipe[PIPE_READ_END]);
397 if (!dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", bus_address) ||
398 !dbus_setenv ("DBUS_SESSION_BUS_PID", NULL) ||
399 !dbus_setenv ("DBUS_SESSION_BUS_WINDOWID", NULL) ||
400 !dbus_setenv ("DBUS_STARTER_ADDRESS", NULL) ||
401 !dbus_setenv ("DBUS_STARTER_BUS_TYPE", NULL))
408 fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
415 exec_app (prog_arg, argv);
423 pid_t child_pid = waitpid (-1, &child_status, 0);
425 if (child_pid == (pid_t) -1)
432 /* shouldn't happen: the only other documented errors are ECHILD,
433 * which shouldn't happen because we terminate when all our children
434 * have died, and EINVAL, which would indicate programming error */
435 fprintf (stderr, "%s: waitpid() failed: %s\n", me, strerror (errsv));
438 else if (child_pid == bus_pid)
440 /* no need to kill it, now */
443 if (WIFEXITED (child_status))
444 fprintf (stderr, "%s: dbus-daemon exited with code %d\n",
445 me, WEXITSTATUS (child_status));
446 else if (WIFSIGNALED (child_status))
447 fprintf (stderr, "%s: dbus-daemon terminated by signal %d\n",
448 me, WTERMSIG (child_status));
450 fprintf (stderr, "%s: dbus-daemon died or something\n", me);
452 else if (child_pid == app_pid)
455 kill (bus_pid, SIGTERM);
457 if (WIFEXITED (child_status))
458 return WEXITSTATUS (child_status);
460 /* if it died from a signal, behave like sh(1) */
461 if (WIFSIGNALED (child_status))
462 return 128 + WTERMSIG (child_status);
464 /* I give up (this should never be reached) */
465 fprintf (stderr, "%s: child process died or something\n", me);
470 fprintf (stderr, "%s: ignoring unknown child process %ld\n", me,