dbus: fix 64-bit compiler warnings
[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 #include "dbus/dbus-internals.h"
41
42 #define MAX_ADDR_LEN 512
43 #define PIPE_READ_END  0
44 #define PIPE_WRITE_END 1
45
46 /* PROCESSES
47  *
48  * If you are in a shell and run "dbus-run-session myapp", here is what
49  * happens (compare and contrast with dbus-launch):
50  *
51  * shell
52  *   \- dbus-run-session myapp
53  *      \- dbus-daemon --nofork --print-address --session
54  *      \- myapp
55  *
56  * All processes are long-running.
57  *
58  * When myapp exits, dbus-run-session kills dbus-daemon and terminates.
59  *
60  * If dbus-daemon exits, dbus-run-session warns and continues to run.
61  *
62  * PIPES
63  *
64  * dbus-daemon --print-address -> bus_address_pipe -> d-r-s
65  */
66
67 static const char me[] = "dbus-run-session";
68
69 static void usage (int ecode) _DBUS_GNUC_NORETURN;
70
71 static void
72 usage (int ecode)
73 {
74   fprintf (stderr,
75       "%s [OPTIONS] [--] PROGRAM [ARGUMENTS]\n"
76       "%s --version\n"
77       "%s --help\n"
78       "\n"
79       "Options:\n"
80       "--dbus-daemon=BINARY       run BINARY instead of dbus-daemon\n"
81       "--config-file=FILENAME     pass to dbus-daemon instead of --session\n"
82       "\n",
83       me, me, me);
84   exit (ecode);
85 }
86
87 static void version (void) _DBUS_GNUC_NORETURN;
88
89 static void
90 version (void)
91 {
92   printf ("%s %s\n"
93           "Copyright (C) 2003-2006 Red Hat, Inc.\n"
94           "Copyright (C) 2006 Thiago Macieira\n"
95           "Copyright © 2011-2012 Nokia Corporation\n"
96           "\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",
99           me, VERSION);
100   exit (0);
101 }
102
103 static void oom (void) _DBUS_GNUC_NORETURN;
104
105 static void
106 oom (void)
107 {
108   fprintf (stderr, "%s: out of memory\n", me);
109   exit (1);
110 }
111
112 typedef enum
113 {
114   READ_STATUS_OK,    /**< Read succeeded */
115   READ_STATUS_ERROR, /**< Some kind of error */
116   READ_STATUS_EOF    /**< EOF returned */
117 } ReadStatus;
118
119 static ReadStatus
120 read_line (int        fd,
121            char      *buf,
122            size_t     maxlen)
123 {
124   size_t bytes = 0;
125   ReadStatus retval;
126
127   memset (buf, '\0', maxlen);
128   maxlen -= 1; /* ensure nul term */
129
130   retval = READ_STATUS_OK;
131
132   while (1)
133     {
134       ssize_t chunk;
135       size_t to_read;
136
137     again:
138       to_read = maxlen - bytes;
139
140       if (to_read == 0)
141         break;
142
143       chunk = read (fd,
144                     buf + bytes,
145                     to_read);
146       if (chunk < 0 && errno == EINTR)
147         goto again;
148
149       if (chunk < 0)
150         {
151           retval = READ_STATUS_ERROR;
152           break;
153         }
154       else if (chunk == 0)
155         {
156           retval = READ_STATUS_EOF;
157           break; /* EOF */
158         }
159       else /* chunk > 0 */
160         bytes += chunk;
161     }
162
163   if (retval == READ_STATUS_EOF &&
164       bytes > 0)
165     retval = READ_STATUS_OK;
166
167   /* whack newline */
168   if (retval != READ_STATUS_ERROR &&
169       bytes > 0 &&
170       buf[bytes-1] == '\n')
171     buf[bytes-1] = '\0';
172
173   return retval;
174 }
175
176 static void
177 exec_dbus_daemon (const char *dbus_daemon,
178                   int         bus_address_pipe[2],
179                   const char *config_file)
180 {
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];
184
185   close (bus_address_pipe[PIPE_READ_END]);
186
187   sprintf (write_address_fd_as_string, "%d", bus_address_pipe[PIPE_WRITE_END]);
188
189   execlp (dbus_daemon,
190           dbus_daemon,
191           "--nofork",
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 */
195           NULL);
196
197   fprintf (stderr, "%s: failed to execute message bus daemon '%s': %s\n",
198            me, dbus_daemon, strerror (errno));
199 }
200
201 static void exec_app (int prog_arg, char **argv) _DBUS_GNUC_NORETURN;
202
203 static void
204 exec_app (int prog_arg, char **argv)
205 {
206   execvp (argv[prog_arg], argv + prog_arg);
207
208   fprintf (stderr, "%s: failed to exec '%s': %s\n", me, argv[prog_arg],
209            strerror (errno));
210   exit (1);
211 }
212
213 int
214 main (int argc, char **argv)
215 {
216   int prog_arg = 0;
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;
222   int i = 1;
223   int requires_arg = 0;
224   pid_t bus_pid;
225   pid_t app_pid;
226
227   while (i < argc)
228     {
229       const char *arg = argv[i];
230
231       if (requires_arg)
232         {
233           const char **arg_dest;
234
235           assert (prev_arg != NULL);
236
237           if (strcmp (prev_arg, "--config-file") == 0)
238             {
239               arg_dest = &config_file;
240             }
241           else if (strcmp (prev_arg, "--dbus-daemon") == 0)
242             {
243               arg_dest = &dbus_daemon;
244             }
245           else
246             {
247               /* shouldn't happen */
248               fprintf (stderr, "%s: internal error: %s not fully implemented\n",
249                        me, prev_arg);
250               return 127;
251             }
252
253           if (*arg_dest != NULL)
254             {
255               fprintf (stderr, "%s: %s given twice\n", me, prev_arg);
256               return 127;
257             }
258
259           *arg_dest = arg;
260           requires_arg = 0;
261           prev_arg = arg;
262           ++i;
263           continue;
264         }
265
266       if (strcmp (arg, "--help") == 0 ||
267           strcmp (arg, "-h") == 0 ||
268           strcmp (arg, "-?") == 0)
269         {
270           usage (0);
271         }
272       else if (strcmp (arg, "--version") == 0)
273         {
274           version ();
275         }
276       else if (strstr (arg, "--config-file=") == arg)
277         {
278           const char *file;
279
280           if (config_file != NULL)
281             {
282               fprintf (stderr, "%s: --config-file given twice\n", me);
283               return 127;
284             }
285
286           file = strchr (arg, '=');
287           ++file;
288
289           config_file = file;
290         }
291       else if (strstr (arg, "--dbus-daemon=") == arg)
292         {
293           const char *file;
294
295           if (dbus_daemon != NULL)
296             {
297               fprintf (stderr, "%s: --dbus-daemon given twice\n", me);
298               return 127;
299             }
300
301           file = strchr (arg, '=');
302           ++file;
303
304           dbus_daemon = file;
305         }
306       else if (strcmp (arg, "--config-file") == 0 ||
307                strcmp (arg, "--dbus-daemon") == 0)
308         {
309           requires_arg = 1;
310         }
311       else if (arg[0] == '-')
312         {
313           if (strcmp (arg, "--") != 0)
314             {
315               fprintf (stderr, "%s: option '%s' is unknown\n", me, arg);
316               return 127;
317             }
318           else
319             {
320               prog_arg = i + 1;
321               break;
322             }
323         }
324       else
325         {
326           prog_arg = i;
327           break;
328         }
329
330       prev_arg = arg;
331       ++i;
332     }
333
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)
337     {
338       fprintf (stderr, "%s: a non-option argument is required\n", me);
339       return 127;
340     }
341
342   if (requires_arg)
343     {
344       fprintf (stderr, "%s: option '%s' requires an argument\n", me, prev_arg);
345       return 127;
346     }
347
348   if (dbus_daemon == NULL)
349     dbus_daemon = "dbus-daemon";
350
351   if (pipe (bus_address_pipe) < 0)
352     {
353       fprintf (stderr, "%s: failed to create pipe: %s\n", me, strerror (errno));
354       return 127;
355     }
356
357   bus_pid = fork ();
358
359   if (bus_pid < 0)
360     {
361       fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
362       return 127;
363     }
364
365   if (bus_pid == 0)
366     {
367       /* child */
368       exec_dbus_daemon (dbus_daemon, bus_address_pipe, config_file);
369       /* not reached */
370       return 127;
371     }
372
373   close (bus_address_pipe[PIPE_WRITE_END]);
374
375   switch (read_line (bus_address_pipe[PIPE_READ_END], bus_address, MAX_ADDR_LEN))
376     {
377     case READ_STATUS_OK:
378       break;
379
380     case READ_STATUS_EOF:
381       fprintf (stderr, "%s: EOF reading address from bus daemon\n", me);
382       return 127;
383       break;
384
385     case READ_STATUS_ERROR:
386       fprintf (stderr, "%s: error reading address from bus daemon: %s\n",
387                me, strerror (errno));
388       return 127;
389       break;
390
391     default:
392       _dbus_assert_not_reached ("invalid read result");
393     }
394
395   close (bus_address_pipe[PIPE_READ_END]);
396
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))
402     oom ();
403
404   app_pid = fork ();
405
406   if (app_pid < 0)
407     {
408       fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno));
409       return 127;
410     }
411
412   if (app_pid == 0)
413     {
414       /* child */
415       exec_app (prog_arg, argv);
416       /* not reached */
417       return 127;
418     }
419
420   while (1)
421     {
422       int child_status;
423       pid_t child_pid = waitpid (-1, &child_status, 0);
424
425       if (child_pid == (pid_t) -1)
426         {
427           int errsv = errno;
428
429           if (errsv == EINTR)
430             continue;
431
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));
436           return 127;
437         }
438       else if (child_pid == bus_pid)
439         {
440           /* no need to kill it, now */
441           bus_pid = 0;
442
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));
449           else
450             fprintf (stderr, "%s: dbus-daemon died or something\n", me);
451         }
452       else if (child_pid == app_pid)
453         {
454           if (bus_pid != 0)
455             kill (bus_pid, SIGTERM);
456
457           if (WIFEXITED (child_status))
458             return WEXITSTATUS (child_status);
459
460           /* if it died from a signal, behave like sh(1) */
461           if (WIFSIGNALED (child_status))
462             return 128 + WTERMSIG (child_status);
463
464           /* I give up (this should never be reached) */
465           fprintf (stderr, "%s: child process died or something\n", me);
466           return 127;
467         }
468       else
469         {
470           fprintf (stderr, "%s: ignoring unknown child process %ld\n", me,
471               (long) child_pid);
472         }
473     }
474
475   return 0;
476 }