[rename] renamed kdbus related macros
[platform/upstream/dbus.git] / tools / dbus-run-session.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-run-session.c - run a child process in its own session
3  *
4  * Copyright © 2003-2006 Red Hat, Inc.
5  * Copyright © 2006 Thiago Macieira <thiago@kde.org>
6  * Copyright © 2011-2012 Nokia Corporation
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
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.
14  *
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.
19  *
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
23  *
24  */
25
26 #include <config.h>
27
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <signal.h>
38
39 #include "dbus/dbus.h"
40
41 #define MAX_ADDR_LEN 512
42 #define PIPE_READ_END  0
43 #define PIPE_WRITE_END 1
44
45 /* PROCESSES
46  *
47  * If you are in a shell and run "dbus-run-session myapp", here is what
48  * happens (compare and contrast with dbus-launch):
49  *
50  * shell
51  *   \- dbus-run-session myapp
52  *      \- dbus-daemon --nofork --print-address --session
53  *      \- myapp
54  *
55  * All processes are long-running.
56  *
57  * When myapp exits, dbus-run-session kills dbus-daemon and terminates.
58  *
59  * If dbus-daemon exits, dbus-run-session warns and continues to run.
60  *
61  * PIPES
62  *
63  * dbus-daemon --print-address -> bus_address_pipe -> d-r-s
64  */
65
66 static const char me[] = "dbus-run-session";
67
68 static void
69 usage (int ecode)
70 {
71   fprintf (stderr,
72       "%s [OPTIONS] [--] PROGRAM [ARGUMENTS]\n"
73       "%s --version\n"
74       "%s --help\n"
75       "\n"
76       "Options:\n"
77       "--dbus-daemon=BINARY       run BINARY instead of dbus-daemon\n"
78       "--config-file=FILENAME     pass to dbus-daemon instead of --session\n"
79       "\n",
80       me, me, me);
81   exit (ecode);
82 }
83
84 static void
85 version (void)
86 {
87   printf ("%s %s\n"
88           "Copyright (C) 2003-2006 Red Hat, Inc.\n"
89           "Copyright (C) 2006 Thiago Macieira\n"
90           "Copyright © 2011-2012 Nokia Corporation\n"
91           "\n"
92           "This is free software; see the source for copying conditions.\n"
93           "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
94           me, VERSION);
95   exit (0);
96 }
97
98 static void
99 oom (void)
100 {
101   fprintf (stderr, "%s: out of memory\n", me);
102   exit (1);
103 }
104
105 typedef enum
106 {
107   READ_STATUS_OK,    /**< Read succeeded */
108   READ_STATUS_ERROR, /**< Some kind of error */
109   READ_STATUS_EOF    /**< EOF returned */
110 } ReadStatus;
111
112 static ReadStatus
113 read_line (int        fd,
114            char      *buf,
115            size_t     maxlen)
116 {
117   size_t bytes = 0;
118   ReadStatus retval;
119
120   memset (buf, '\0', maxlen);
121   maxlen -= 1; /* ensure nul term */
122
123   retval = READ_STATUS_OK;
124
125   while (1)
126     {
127       ssize_t chunk;
128       size_t to_read;
129
130     again:
131       to_read = maxlen - bytes;
132
133       if (to_read == 0)
134         break;
135
136       chunk = read (fd,
137                     buf + bytes,
138                     to_read);
139       if (chunk < 0 && errno == EINTR)
140         goto again;
141
142       if (chunk < 0)
143         {
144           retval = READ_STATUS_ERROR;
145           break;
146         }
147       else if (chunk == 0)
148         {
149           retval = READ_STATUS_EOF;
150           break; /* EOF */
151         }
152       else /* chunk > 0 */
153         bytes += chunk;
154     }
155
156   if (retval == READ_STATUS_EOF &&
157       bytes > 0)
158     retval = READ_STATUS_OK;
159
160   /* whack newline */
161   if (retval != READ_STATUS_ERROR &&
162       bytes > 0 &&
163       buf[bytes-1] == '\n')
164     buf[bytes-1] = '\0';
165
166   return retval;
167 }
168
169 static void
170 exec_dbus_daemon (const char *dbus_daemon,
171                   int         bus_address_pipe[2],
172                   const char *config_file)
173 {
174   /* Child process, which execs dbus-daemon or dies trying */
175 #define MAX_FD_LEN 64
176   char write_address_fd_as_string[MAX_FD_LEN];
177
178   close (bus_address_pipe[PIPE_READ_END]);
179
180   sprintf (write_address_fd_as_string, "%d", bus_address_pipe[PIPE_WRITE_END]);
181
182   execlp (dbus_daemon,
183           dbus_daemon,
184           "--nofork",
185           "--print-address", write_address_fd_as_string,
186           config_file ? "--config-file" : "--session",
187           config_file, /* has to be last in this varargs list */
188           NULL);
189
190   fprintf (stderr, "%s: failed to execute message bus daemon '%s': %s\n",
191            me, dbus_daemon, strerror (errno));
192 }
193
194 static void
195 exec_app (int prog_arg, char **argv)
196 {
197   execvp (argv[prog_arg], argv + prog_arg);
198
199   fprintf (stderr, "%s: failed to exec '%s': %s\n", me, argv[prog_arg],
200            strerror (errno));
201   exit (1);
202 }
203
204 int
205 main (int argc, char **argv)
206 {
207   int prog_arg = 0;
208   int bus_address_pipe[2] = { 0, 0 };
209   const char *config_file = NULL;
210   const char *dbus_daemon = NULL;
211   char bus_address[MAX_ADDR_LEN] = { 0 };
212   const char *prev_arg = NULL;
213   int i = 1;
214   int requires_arg = 0;
215   pid_t bus_pid;
216   pid_t app_pid;
217
218   while (i < argc)
219     {
220       const char *arg = argv[i];
221
222       if (requires_arg)
223         {
224           const char **arg_dest;
225
226           assert (prev_arg != NULL);
227
228           if (strcmp (prev_arg, "--config-file") == 0)
229             {
230               arg_dest = &config_file;
231             }
232           else if (strcmp (prev_arg, "--dbus-daemon") == 0)
233             {
234               arg_dest = &dbus_daemon;
235             }
236           else
237             {
238               /* shouldn't happen */
239               fprintf (stderr, "%s: internal error: %s not fully implemented\n",
240                        me, prev_arg);
241               return 127;
242             }
243
244           if (*arg_dest != NULL)
245             {
246               fprintf (stderr, "%s: %s given twice\n", me, prev_arg);
247               return 127;
248             }
249
250           *arg_dest = arg;
251           requires_arg = 0;
252           prev_arg = arg;
253           ++i;
254           continue;
255         }
256
257       if (strcmp (arg, "--help") == 0 ||
258           strcmp (arg, "-h") == 0 ||
259           strcmp (arg, "-?") == 0)
260         {
261           usage (0);
262         }
263       else if (strcmp (arg, "--version") == 0)
264         {
265           version ();
266         }
267       else if (strstr (arg, "--config-file=") == arg)
268         {
269           const char *file;
270
271           if (config_file != NULL)
272             {
273               fprintf (stderr, "%s: --config-file given twice\n", me);
274               return 127;
275             }
276
277           file = strchr (arg, '=');
278           ++file;
279
280           config_file = file;
281         }
282       else if (strstr (arg, "--dbus-daemon=") == arg)
283         {
284           const char *file;
285
286           if (dbus_daemon != NULL)
287             {
288               fprintf (stderr, "%s: --dbus-daemon given twice\n", me);
289               return 127;
290             }
291
292           file = strchr (arg, '=');
293           ++file;
294
295           dbus_daemon = file;
296         }
297       else if (strcmp (arg, "--config-file") == 0 ||
298                strcmp (arg, "--dbus-daemon") == 0)
299         {
300           requires_arg = 1;
301         }
302       else if (arg[0] == '-')
303         {
304           if (strcmp (arg, "--") != 0)
305             {
306               fprintf (stderr, "%s: option '%s' is unknown\n", me, arg);
307               return 127;
308             }
309           else
310             {
311               prog_arg = i + 1;
312               break;
313             }
314         }
315       else
316         {
317           prog_arg = i;
318           break;
319         }
320
321       prev_arg = arg;
322       ++i;
323     }
324
325   /* "dbus-run-session" and "dbus-run-session ... --" are not allowed:
326    * there must be something to run */
327   if (prog_arg < 1 || prog_arg >= argc)
328     {
329       fprintf (stderr, "%s: a non-option argument is required\n", me);
330       return 127;
331     }
332
333   if (requires_arg)
334     {
335       fprintf (stderr, "%s: option '%s' requires an argument\n", me, prev_arg);
336       return 127;
337     }
338
339   if (dbus_daemon == NULL)
340     dbus_daemon = "dbus-daemon";
341
342   if (pipe (bus_address_pipe) < 0)
343     {
344       fprintf (stderr, "%s: failed to create pipe: %s\n", me, strerror (errno));
345       return 127;
346     }
347
348   bus_pid = fork ();
349
350   if (bus_pid < 0)
351     {
352       fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
353       return 127;
354     }
355
356   if (bus_pid == 0)
357     {
358       /* child */
359       exec_dbus_daemon (dbus_daemon, bus_address_pipe, config_file);
360       /* not reached */
361       return 127;
362     }
363
364   close (bus_address_pipe[PIPE_WRITE_END]);
365
366   switch (read_line (bus_address_pipe[PIPE_READ_END], bus_address, MAX_ADDR_LEN))
367     {
368     case READ_STATUS_OK:
369       break;
370
371     case READ_STATUS_EOF:
372       fprintf (stderr, "%s: EOF reading address from bus daemon\n", me);
373       return 127;
374       break;
375
376     case READ_STATUS_ERROR:
377       fprintf (stderr, "%s: error reading address from bus daemon: %s\n",
378                me, strerror (errno));
379       return 127;
380       break;
381     }
382
383   close (bus_address_pipe[PIPE_READ_END]);
384
385   if (!dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", bus_address) ||
386       !dbus_setenv ("DBUS_SESSION_BUS_PID", NULL) ||
387       !dbus_setenv ("DBUS_SESSION_BUS_WINDOWID", NULL) ||
388       !dbus_setenv ("DBUS_STARTER_ADDRESS", NULL) ||
389       !dbus_setenv ("DBUS_STARTER_BUS_TYPE", NULL))
390     oom ();
391
392   app_pid = fork ();
393
394   if (app_pid < 0)
395     {
396       fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
397       return 127;
398     }
399
400   if (app_pid == 0)
401     {
402       /* child */
403       exec_app (prog_arg, argv);
404       /* not reached */
405       return 127;
406     }
407
408   while (1)
409     {
410       int child_status;
411       pid_t child_pid = waitpid (-1, &child_status, 0);
412
413       if (child_pid == (pid_t) -1)
414         {
415           int errsv = errno;
416
417           if (errsv == EINTR)
418             continue;
419
420           /* shouldn't happen: the only other documented errors are ECHILD,
421            * which shouldn't happen because we terminate when all our children
422            * have died, and EINVAL, which would indicate programming error */
423           fprintf (stderr, "%s: waitpid() failed: %s\n", me, strerror (errsv));
424           return 127;
425         }
426       else if (child_pid == bus_pid)
427         {
428           /* no need to kill it, now */
429           bus_pid = 0;
430
431           if (WIFEXITED (child_status))
432             fprintf (stderr, "%s: dbus-daemon exited with code %d\n",
433                 me, WEXITSTATUS (child_status));
434           else if (WIFSIGNALED (child_status))
435             fprintf (stderr, "%s: dbus-daemon terminated by signal %d\n",
436                 me, WTERMSIG (child_status));
437           else
438             fprintf (stderr, "%s: dbus-daemon died or something\n", me);
439         }
440       else if (child_pid == app_pid)
441         {
442           if (bus_pid != 0)
443             kill (bus_pid, SIGTERM);
444
445           if (WIFEXITED (child_status))
446             return WEXITSTATUS (child_status);
447
448           /* if it died from a signal, behave like sh(1) */
449           if (WIFSIGNALED (child_status))
450             return 128 + WTERMSIG (child_status);
451
452           /* I give up (this should never be reached) */
453           fprintf (stderr, "%s: child process died or something\n", me);
454           return 127;
455         }
456       else
457         {
458           fprintf (stderr, "%s: ignoring unknown child process %ld\n", me,
459               (long) child_pid);
460         }
461     }
462
463   return 0;
464 }