*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+
+#include <config.h>
#include "dbus-launch.h"
#include <stdlib.h>
+#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
extern Display *xdisplay;
#endif
+/* PROCESSES
+ *
+ * If you are in a shell and run "dbus-launch myapp", here is what happens:
+ *
+ * shell [*]
+ * \- main() --exec--> myapp[*]
+ * \- "intermediate parent"
+ * \- bus-runner --exec--> dbus-daemon --fork
+ * \- babysitter[*] \- final dbus-daemon[*]
+ *
+ * Processes marked [*] survive the initial flurry of activity.
+ *
+ * If you run "dbus-launch --sh-syntax" then the diagram is the same, except
+ * that main() prints variables and exits 0 instead of exec'ing myapp.
+ *
+ * PIPES
+ *
+ * dbus-daemon --print-pid -> bus_pid_to_launcher_pipe -> main
+ * dbus-daemon --print-address -> bus_address_to_launcher_pipe -> main
+ * main -> bus_pid_to_babysitter_pipe -> babysitter
+ *
+ * The intermediate parent looks pretty useless at first glance. Its purpose
+ * is to avoid the bus-runner becoming a zombie: when the intermediate parent
+ * terminates, the bus-runner and babysitter are reparented to init, which
+ * reaps them if they have finished. We can't rely on main() to reap arbitrary
+ * children because it might exec myapp, after which it can't be relied on to
+ * reap its children. We *can* rely on main() to reap the intermediate parent,
+ * because that happens before it execs myapp.
+ *
+ * It's unclear why dbus-daemon needs to fork, but we explicitly tell it to
+ * for some reason, then wait for it. If we left it undefined, a forking
+ * dbus-daemon would get the parent process reparented to init and reaped
+ * when the intermediate parent terminated, and a non-forking dbus-daemon
+ * would get reparented to init and carry on there.
+ *
+ * myapp is exec'd by the process that initially ran main() so that it's
+ * the shell's child, so the shell knows how to do job control and stuff.
+ * This is desirable for the "dbus-launch an application" use-case, less so
+ * for the "dbus-launch a test suite in an isolated session" use-case.
+ */
+
static char* machine_uuid = NULL;
const char*
machine_uuid = xstrdup (uuid_arg);
}
+#ifdef DBUS_BUILD_X11
+#define UUID_MAXLEN 40
+/* Read the machine uuid from file if needed. Returns TRUE if machine_uuid is
+ * set after this function */
+static int
+read_machine_uuid_if_needed (void)
+{
+ FILE *f;
+ char uuid[UUID_MAXLEN];
+ size_t len;
+ int ret = FALSE;
+
+ if (machine_uuid != NULL)
+ return TRUE;
+
+ f = fopen (DBUS_MACHINE_UUID_FILE, "r");
+ if (f == NULL)
+ return FALSE;
+
+ if (fgets (uuid, UUID_MAXLEN, f) == NULL)
+ goto out;
+
+ len = strlen (uuid);
+ if (len < 32)
+ goto out;
+
+ /* rstrip the read uuid */
+ while (len > 31 && isspace(uuid[len - 1]))
+ len--;
+
+ if (len != 32)
+ goto out;
+
+ uuid[len] = '\0';
+ machine_uuid = xstrdup (uuid);
+ verbose ("UID: %s\n", machine_uuid);
+ ret = TRUE;
+
+out:
+ fclose(f);
+ return ret;
+}
+#endif /* DBUS_BUILD_X11 */
+
void
verbose (const char *format,
...)
{
+#ifdef DBUS_ENABLE_VERBOSE_MODE
va_list args;
static int verbose = TRUE;
static int verbose_initted = FALSE;
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
}
static void
usage (int ecode)
{
- fprintf (stderr, "dbus-launch [--version] [--help] [--sh-syntax] [--csh-syntax] [--auto-syntax] [--exit-with-session]\n");
+ fprintf (stderr, "dbus-launch [--version] [--help] [--sh-syntax]"
+ " [--csh-syntax] [--auto-syntax] [--binary-syntax] [--close-stderr]"
+ " [--exit-with-session] [--autolaunch=MACHINEID]"
+ " [--config-file=FILENAME] [PROGRAM] [ARGS...]\n");
exit (ecode);
}
static pid_t bus_pid_to_kill = -1;
+static void
+kill_bus(void)
+{
+ verbose ("Killing message bus and exiting babysitter\n");
+ kill (bus_pid_to_kill, SIGTERM);
+ sleep (3);
+ kill (bus_pid_to_kill, SIGKILL);
+}
+
void
kill_bus_and_exit (int exitcode)
{
- verbose ("Killing message bus and exiting babysitter\n");
-
/* in case these point to any NFS mounts, get rid of them immediately */
close (0);
close (1);
close (2);
- kill (bus_pid_to_kill, SIGTERM);
- sleep (3);
- kill (bus_pid_to_kill, SIGKILL);
+ kill_bus();
exit (exitcode);
}
{
if (binary_syntax)
{
- write (1, bus_address, strlen (bus_address) + 1);
- write (1, &bus_pid, sizeof bus_pid);
- write (1, &bus_wid, sizeof bus_wid);
+ do_write (1, bus_address, strlen (bus_address) + 1);
+ do_write (1, &bus_pid, sizeof bus_pid);
+ do_write (1, &bus_wid, sizeof bus_wid);
return;
}
else if (c_shell_syntax)
{
switch (sig)
{
+#ifdef SIGHUP
case SIGHUP:
+#endif
+ case SIGINT:
case SIGTERM:
got_sighup = TRUE;
break;
act.sa_flags = 0;
sigaction (SIGHUP, &act, NULL);
sigaction (SIGTERM, &act, NULL);
+ sigaction (SIGINT, &act, NULL);
#ifdef DBUS_BUILD_X11
x11_init();
else
tty_fd = -1;
- if (tty_fd >= 0)
- verbose ("stdin isatty(), monitoring it\n");
+ if (x_fd >= 0)
+ {
+ verbose ("session lifetime is defined by X, not monitoring stdin\n");
+ tty_fd = -1;
+ }
+ else if (tty_fd >= 0)
+ {
+ verbose ("stdin isatty(), monitoring it\n");
+ }
else
- verbose ("stdin was not a TTY, not monitoring it\n");
-
+ {
+ verbose ("stdin was not a TTY, not monitoring it\n");
+ }
+
if (tty_fd < 0 && x_fd < 0)
{
fprintf (stderr, "No terminal on standard input and no X display; cannot attach message bus to session lifetime\n");
while (TRUE)
{
+#ifdef DBUS_BUILD_X11
+ /* Dump events on the floor, and let
+ * IO error handler run if we lose
+ * the X connection. It's important to
+ * run this before going into select() since
+ * we might have queued outgoing messages or
+ * events.
+ */
+ x11_handle_event ();
+#endif
+
FD_ZERO (&read_set);
FD_ZERO (&err_set);
FD_SET (x_fd, &read_set);
FD_SET (x_fd, &err_set);
}
-
+
select (MAX (tty_fd, x_fd) + 1,
&read_set, NULL, &err_set, NULL);
}
#ifdef DBUS_BUILD_X11
- /* Dump events on the floor, and let
- * IO error handler run if we lose
- * the X connection
+ /* Events will be processed before we select again
*/
if (x_fd >= 0)
verbose ("X fd condition reading = %d error = %d\n",
FD_ISSET (x_fd, &read_set),
FD_ISSET (x_fd, &err_set));
- x11_handle_event ();
#endif
if (tty_fd >= 0)
s = getenv ("DBUS_DEBUG_OUTPUT");
if (s == NULL || *s == '\0')
dup2 (dev_null_fd, 2);
+ close (dev_null_fd);
}
else
{
close (2);
if (dup2 (fd, 2) == -1)
- // error; we can't report an error anymore...
- exit (1);
+ {
+ /* error; we can't report an error anymore... */
+ exit (1);
+ }
close (fd);
}
+static void
+pass_info (const char *runprog, const char *bus_address, pid_t bus_pid,
+ long bus_wid, int c_shell_syntax, int bourne_shell_syntax,
+ int binary_syntax,
+ int argc, char **argv, int remaining_args)
+{
+ char *envvar = NULL;
+ char **args = NULL;
+
+ if (runprog)
+ {
+ int i;
+
+ envvar = malloc (strlen ("DBUS_SESSION_BUS_ADDRESS=") +
+ strlen (bus_address) + 1);
+ args = malloc (sizeof (char *) * ((argc-remaining_args)+2));
+
+ if (envvar == NULL || args == NULL)
+ goto oom;
+
+ args[0] = xstrdup (runprog);
+ if (!args[0])
+ goto oom;
+ for (i = 1; i <= (argc-remaining_args); i++)
+ {
+ size_t len = strlen (argv[remaining_args+i-1])+1;
+ args[i] = malloc (len);
+ if (!args[i])
+ goto oom;
+ strncpy (args[i], argv[remaining_args+i-1], len);
+ }
+ args[i] = NULL;
+
+ strcpy (envvar, "DBUS_SESSION_BUS_ADDRESS=");
+ strcat (envvar, bus_address);
+ putenv (envvar);
+
+ execvp (runprog, args);
+ fprintf (stderr, "Couldn't exec %s: %s\n", runprog, strerror (errno));
+ exit (1);
+ }
+ else
+ {
+ print_variables (bus_address, bus_pid, bus_wid, c_shell_syntax,
+ bourne_shell_syntax, binary_syntax);
+ }
+ verbose ("dbus-launch exiting\n");
+
+ fflush (stdout);
+ fflush (stderr);
+ close (1);
+ close (2);
+ exit (0);
+oom:
+ if (envvar)
+ free (envvar);
+
+ if (args)
+ free (args);
+
+ fprintf (stderr, "Out of memory!");
+ exit (1);
+}
+
#define READ_END 0
#define WRITE_END 1
fprintf (stderr, "Autolaunch requested, but X11 support not compiled in.\n"
"Cannot continue.\n");
exit (1);
-#else
+#else /* DBUS_BUILD_X11 */
+#ifndef DBUS_ENABLE_X11_AUTOLAUNCH
+ fprintf (stderr, "X11 autolaunch support disabled at compile time.\n");
+ exit (1);
+#else /* DBUS_ENABLE_X11_AUTOLAUNCH */
char *address;
pid_t pid;
long wid;
exit (1);
}
- /* FIXME right now autolaunch always does print_variables(), but it should really
- * exec the child program instead if a child program was specified. For now
- * we just exit if this conflict arises.
- */
- if (runprog)
- {
- fprintf (stderr, "Currently --autolaunch does not support running a program\n");
- exit (1);
- }
-
verbose ("Autolaunch enabled (using X11).\n");
if (!exit_with_session)
{
}
if (address != NULL)
- {
+ {
verbose ("dbus-daemon is already running. Returning existing parameters.\n");
- print_variables (address, pid, wid, c_shell_syntax,
- bourne_shell_syntax, binary_syntax);
+ pass_info (runprog, address, pid, wid, c_shell_syntax,
+ bourne_shell_syntax, binary_syntax, argc, argv, remaining_args);
exit (0);
}
-#endif
+#endif /* DBUS_ENABLE_X11_AUTOLAUNCH */
+ }
+ else if (read_machine_uuid_if_needed())
+ {
+ x11_init();
+#endif /* DBUS_BUILD_X11 */
}
+
if (pipe (bus_pid_to_launcher_pipe) < 0 ||
pipe (bus_address_to_launcher_pipe) < 0 ||
pipe (bus_pid_to_babysitter_pipe) < 0)
char write_pid_fd_as_string[MAX_FD_LEN];
char write_address_fd_as_string[MAX_FD_LEN];
+#ifdef DBUS_BUILD_X11
+ xdisplay = NULL;
+#endif
+
if (close_stderr)
do_close_stderr ();
char *end;
long wid = 0;
long val;
- int ret2;
verbose ("=== Parent dbus-launch continues\n");
close (bus_pid_to_launcher_pipe[READ_END]);
-#ifdef DBUS_BUILD_X11
- /* FIXME the runprog == NULL is broken - we need to launch the runprog with the existing bus,
- * instead of just doing print_variables() if there's an existing bus.
- */
- if (xdisplay != NULL && runprog == NULL)
+#ifdef DBUS_ENABLE_X11_AUTOLAUNCH
+ if (xdisplay != NULL)
{
+ int ret2;
+
+ verbose("Saving x11 address\n");
ret2 = x11_save_address (bus_address, bus_pid, &wid);
- if (ret2 == 0)
+ /* Only get an existing dbus session when autolaunching */
+ if (autolaunch)
{
- /* another window got added. Return its address */
- char *address;
- pid_t pid;
- long wid;
-
- if (x11_get_address (&address, &pid, &wid) && address != NULL)
+ if (ret2 == 0)
{
- verbose ("dbus-daemon is already running. Returning existing parameters.\n");
- print_variables (address, pid, wid, c_shell_syntax,
- bourne_shell_syntax, binary_syntax);
- free (address);
-
+ char *address = NULL;
+ /* another window got added. Return its address */
bus_pid_to_kill = bus_pid;
- kill_bus_and_exit (0);
+ if (x11_get_address (&address, &bus_pid, &wid)
+ && address != NULL)
+ {
+ verbose ("dbus-daemon is already running. Returning existing parameters.\n");
+ /* Kill the old bus */
+ kill_bus();
+ pass_info (runprog, address, bus_pid, wid,
+ c_shell_syntax, bourne_shell_syntax, binary_syntax,
+ argc, argv, remaining_args);
+ }
+ }
+ if (ret2 < 0)
+ {
+ fprintf (stderr, "Error saving bus information.\n");
+ bus_pid_to_kill = bus_pid;
+ kill_bus_and_exit (1);
}
-
- /* if failed, fall through */
- }
- if (ret2 <= 0)
- {
- fprintf (stderr, "Error saving bus information.\n");
- bus_pid_to_kill = bus_pid;
- kill_bus_and_exit (1);
}
}
#endif
write_pid (bus_pid_to_babysitter_pipe[WRITE_END], bus_pid);
close (bus_pid_to_babysitter_pipe[WRITE_END]);
- if (runprog)
- {
- char *envvar;
- char **args;
-
- envvar = malloc (strlen ("DBUS_SESSION_BUS_ADDRESS=") + strlen (bus_address) + 1);
- args = malloc (sizeof (char *) * ((argc-remaining_args)+2));
-
- if (envvar == NULL || args == NULL)
- goto oom;
-
- args[0] = xstrdup (runprog);
- if (!args[0])
- goto oom;
- for (i = 1; i <= (argc-remaining_args); i++)
- {
- size_t len = strlen (argv[remaining_args+i-1])+1;
- args[i] = malloc (len);
- if (!args[i])
- goto oom;
- strncpy (args[i], argv[remaining_args+i-1], len);
- }
- args[i] = NULL;
-
- strcpy (envvar, "DBUS_SESSION_BUS_ADDRESS=");
- strcat (envvar, bus_address);
- putenv (envvar);
-
- execvp (runprog, args);
- fprintf (stderr, "Couldn't exec %s: %s\n", runprog, strerror (errno));
- exit (1);
- }
- else
- {
- print_variables (bus_address, bus_pid, wid, c_shell_syntax,
- bourne_shell_syntax, binary_syntax);
- }
-
- verbose ("dbus-launch exiting\n");
+ pass_info (runprog, bus_address, bus_pid, wid, c_shell_syntax,
+ bourne_shell_syntax, binary_syntax, argc, argv, remaining_args);
+ }
- fflush (stdout);
- fflush (stderr);
- close (1);
- close (2);
-
- exit (0);
- }
-
return 0;
- oom:
- fprintf (stderr, "Out of memory!");
- exit (1);
}