1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2/* main.c  main() for message bus
3 *
4 * Copyright (C) 2003 Red Hat, Inc.
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#include <config.h>
25#include "bus.h"
26#include "driver.h"
27#include <dbus/dbus-internals.h>
28#include <dbus/dbus-watch.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#ifdef HAVE_SIGNAL_H
33#include <signal.h>
34#endif
35#ifdef HAVE_ERRNO_H
36#include <errno.h>
37#endif
38#ifdef HAVE_UNISTD_H
39#include <unistd.h>     /* for write() and STDERR_FILENO */
40#endif
41#include "selinux.h"
42
43static BusContext *context;
44
45#ifdef DBUS_UNIX
46
47static int reload_pipe[2];
48#define RELOAD_READ_END 0
49#define RELOAD_WRITE_END 1
50
51static void close_reload_pipe (DBusWatch **);
52
53typedef enum
54 {
55   ACTION_RELOAD = 'r',
56   ACTION_QUIT = 'q'
57 } SignalAction;
58
59static void
60signal_handler (int sig)
61{
62  switch (sig)
63    {
64#ifdef DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX
65    case SIGIO:
66      /* explicit fall-through */
67#endif /* DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX  */
68#ifdef SIGHUP
69    case SIGHUP:
70      {
71        DBusString str;
72        char action[2] = { ACTION_RELOAD, '\0' };
73
74        _dbus_string_init_const (&str, action);
75        if ((reload_pipe[RELOAD_WRITE_END] > 0) &&
76            !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
77          {
78            /* If we receive SIGHUP often enough to fill the pipe buffer (4096
79             * times on old Linux, 65536 on modern Linux) before it can be
80             * drained, let's just warn and ignore. The configuration will be
81             * reloaded while draining the pipe buffer, which is what we
82             * wanted. It's harmless that it will be reloaded fewer times than
83             * we asked for, since the reload is delayed anyway, so new changes
84             * will be picked up.
85             *
86             * We use write() because _dbus_warn uses vfprintf, which isn't
87             * async-signal-safe.
88             *
89             * This is necessarily Unix-specific, but so are POSIX signals,
90             * so... */
91            static const char message[] =
92              "Unable to write to reload pipe - buffer full?\n";
93
94            write (STDERR_FILENO, message, strlen (message));
95          }
96      }
97      break;
98#endif
99
100    case SIGTERM:
101      {
102        DBusString str;
103        char action[2] = { ACTION_QUIT, '\0' };
104        _dbus_string_init_const (&str, action);
105        if ((reload_pipe[RELOAD_WRITE_END] < 0) ||
106            !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
107          {
108            /* If we can't write to the socket, dying seems a more
109             * important response to SIGTERM than cleaning up sockets,
110             * so we exit. We'd use exit(), but that's not async-signal-safe,
111             * so we'll have to resort to _exit(). */
112            static const char message[] =
113              "Unable to write termination signal to pipe - buffer full?\n"
114              "Will exit instead.\n";
115
116            write (STDERR_FILENO, message, strlen (message));
117            _exit (1);
118          }
119      }
120      break;
121    }
122}
123#endif /* DBUS_UNIX */
124
125static void
126usage (void)
127{
128  fprintf (stderr, DBUS_DAEMON_NAME " [--version] [--session] [--system] [--config-file=FILE] [--print-address[=DESCRIPTOR]] [--print-pid[=DESCRIPTOR]] [--fork] [--nofork] [--introspect] [--address=ADDRESS] [--systemd-activation] [--nopidfile]\n");
129  exit (1);
130}
131
132static void
133version (void)
134{
135  printf ("D-Bus Message Bus Daemon %s\n"
136          "Copyright (C) 2002, 2003 Red Hat, Inc., CodeFactory AB, and others\n"
137          "This is free software; see the source for copying conditions.\n"
138          "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
139          DBUS_VERSION_STRING);
140  exit (0);
141}
142
143static void
144introspect (void)
145{
146  DBusString xml;
147  const char *v_STRING;
148
149  if (!_dbus_string_init (&xml))
150    goto oom;
151
152  if (!bus_driver_generate_introspect_string (&xml))
153    {
154      _dbus_string_free (&xml);
155      goto oom;
156    }
157
158  v_STRING = _dbus_string_get_const_data (&xml);
159  printf ("%s\n", v_STRING);
160
161  exit (0);
162
163 oom:
164  _dbus_warn ("Can not introspect - Out of memory\n");
165  exit (1);
166}
167
168static void
169check_two_config_files (const DBusString *config_file,
170                        const char       *extra_arg)
171{
172  if (_dbus_string_get_length (config_file) > 0)
173    {
174      fprintf (stderr, "--%s specified but configuration file %s already requested\n",
175               extra_arg, _dbus_string_get_const_data (config_file));
176      exit (1);
177    }
178}
179
180static void
181check_two_addresses (const DBusString *address,
182                     const char       *extra_arg)
183{
184  if (_dbus_string_get_length (address) > 0)
185    {
186      fprintf (stderr, "--%s specified but address %s already requested\n",
187               extra_arg, _dbus_string_get_const_data (address));
188      exit (1);
189    }
190}
191
192static void
193check_two_addr_descriptors (const DBusString *addr_fd,
194                            const char       *extra_arg)
195{
196  if (_dbus_string_get_length (addr_fd) > 0)
197    {
198      fprintf (stderr, "--%s specified but printing address to %s already requested\n",
199               extra_arg, _dbus_string_get_const_data (addr_fd));
200      exit (1);
201    }
202}
203
204static void
205check_two_pid_descriptors (const DBusString *pid_fd,
206                           const char       *extra_arg)
207{
208  if (_dbus_string_get_length (pid_fd) > 0)
209    {
210      fprintf (stderr, "--%s specified but printing pid to %s already requested\n",
211               extra_arg, _dbus_string_get_const_data (pid_fd));
212      exit (1);
213    }
214}
215
216#ifdef DBUS_UNIX
217static dbus_bool_t
218handle_reload_watch (DBusWatch    *watch,
219		     unsigned int  flags,
220		     void         *data)
221{
222  DBusError error;
223  DBusString str;
224  char *action_str;
225  char action = '\0';
226
227  while (!_dbus_string_init (&str))
228    _dbus_wait_for_memory ();
229
230  if ((reload_pipe[RELOAD_READ_END] > 0) &&
231      _dbus_read_socket (reload_pipe[RELOAD_READ_END], &str, 1) != 1)
232    {
233      _dbus_warn ("Couldn't read from reload pipe.\n");
234      close_reload_pipe (&watch);
235      return TRUE;
236    }
237
238  action_str = _dbus_string_get_data (&str);
239  if (action_str != NULL)
240    {
241      action = action_str[0];
242    }
243  _dbus_string_free (&str);
244
245  /* this can only fail if we don't understand the config file
246   * or OOM.  Either way we should just stick with the currently
247   * loaded config.
248   */
249  dbus_error_init (&error);
250
251  switch (action)
252    {
253    case ACTION_RELOAD:
254      if (! bus_context_reload_config (context, &error))
255        {
256          _DBUS_ASSERT_ERROR_IS_SET (&error);
257          _dbus_assert (dbus_error_has_name (&error, DBUS_ERROR_FAILED) ||
258                        dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY));
259          _dbus_warn ("Unable to reload configuration: %s\n",
260                      error.message);
261          dbus_error_free (&error);
262        }
263      break;
264
265    case ACTION_QUIT:
266      {
267        DBusLoop *loop;
268        /*
269         * On OSs without abstract sockets, we want to quit
270         * gracefully rather than being killed by SIGTERM,
271         * so that DBusServer gets a chance to clean up the
272         * sockets from the filesystem. fd.o #38656
273         */
274        loop = bus_context_get_loop (context);
275        if (loop != NULL)
276          {
277            _dbus_loop_quit (loop);
278          }
279      }
280      break;
281
282    default:
283      break;
284    }
285
286  return TRUE;
287}
288
289static void
290setup_reload_pipe (DBusLoop *loop)
291{
292  DBusError error;
293  DBusWatch *watch;
294
295  dbus_error_init (&error);
296
297  if (!_dbus_full_duplex_pipe (&reload_pipe[0], &reload_pipe[1],
298			       TRUE, &error))
299    {
300      _dbus_warn ("Unable to create reload pipe: %s\n",
301		  error.message);
302      dbus_error_free (&error);
303      exit (1);
304    }
305
306  watch = _dbus_watch_new (reload_pipe[RELOAD_READ_END],
307			   DBUS_WATCH_READABLE, TRUE,
308			   handle_reload_watch, NULL, NULL);
309
310  if (watch == NULL)
311    {
312      _dbus_warn ("Unable to create reload watch: %s\n",
313		  error.message);
314      dbus_error_free (&error);
315      exit (1);
316    }
317
318  if (!_dbus_loop_add_watch (loop, watch))
319    {
320      _dbus_warn ("Unable to add reload watch to main loop: %s\n",
321		  error.message);
322      dbus_error_free (&error);
323      exit (1);
324    }
325
326}
327
328static void
329close_reload_pipe (DBusWatch **watch)
330{
331    _dbus_loop_remove_watch (bus_context_get_loop (context), *watch);
332    _dbus_watch_invalidate (*watch);
333    _dbus_watch_unref (*watch);
334    *watch = NULL;
335
336    _dbus_close_socket (reload_pipe[RELOAD_READ_END], NULL);
337    reload_pipe[RELOAD_READ_END] = -1;
338
339    _dbus_close_socket (reload_pipe[RELOAD_WRITE_END], NULL);
340    reload_pipe[RELOAD_WRITE_END] = -1;
341}
342#endif /* DBUS_UNIX */
343
344int
345main (int argc, char **argv)
346{
347  DBusError error;
348  DBusString config_file;
349  DBusString address;
350  DBusString addr_fd;
351  DBusString pid_fd;
352  const char *prev_arg;
353  DBusPipe print_addr_pipe;
354  DBusPipe print_pid_pipe;
355  int i;
356  dbus_bool_t print_address;
357  dbus_bool_t print_pid;
358  BusContextFlags flags;
359
360  if (!_dbus_string_init (&config_file))
361    return 1;
362
363  if (!_dbus_string_init (&address))
364    return 1;
365
366  if (!_dbus_string_init (&addr_fd))
367    return 1;
368
369  if (!_dbus_string_init (&pid_fd))
370    return 1;
371
372  print_address = FALSE;
373  print_pid = FALSE;
374
375  flags = BUS_CONTEXT_FLAG_WRITE_PID_FILE;
376
377  prev_arg = NULL;
378  i = 1;
379  while (i < argc)
380    {
381      const char *arg = argv[i];
382
383      if (strcmp (arg, "--help") == 0 ||
384          strcmp (arg, "-h") == 0 ||
385          strcmp (arg, "-?") == 0)
386        {
387          usage ();
388        }
389      else if (strcmp (arg, "--version") == 0)
390        {
391          version ();
392        }
393      else if (strcmp (arg, "--introspect") == 0)
394        {
395          introspect ();
396        }
397      else if (strcmp (arg, "--nofork") == 0)
398        {
399          flags &= ~BUS_CONTEXT_FLAG_FORK_ALWAYS;
400          flags |= BUS_CONTEXT_FLAG_FORK_NEVER;
401        }
402      else if (strcmp (arg, "--fork") == 0)
403        {
404          flags &= ~BUS_CONTEXT_FLAG_FORK_NEVER;
405          flags |= BUS_CONTEXT_FLAG_FORK_ALWAYS;
406        }
407      else if (strcmp (arg, "--nopidfile") == 0)
408        {
409          flags &= ~BUS_CONTEXT_FLAG_WRITE_PID_FILE;
410        }
411      else if (strcmp (arg, "--systemd-activation") == 0)
412        {
413          flags |= BUS_CONTEXT_FLAG_SYSTEMD_ACTIVATION;
414        }
415      else if (strcmp (arg, "--system") == 0)
416        {
417          check_two_config_files (&config_file, "system");
418
419          if (!_dbus_append_system_config_file (&config_file))
420            exit (1);
421        }
422      else if (strcmp (arg, "--session") == 0)
423        {
424          check_two_config_files (&config_file, "session");
425
426          if (!_dbus_append_session_config_file (&config_file))
427            exit (1);
428        }
429      else if (strstr (arg, "--config-file=") == arg)
430        {
431          const char *file;
432
433          check_two_config_files (&config_file, "config-file");
434
435          file = strchr (arg, '=');
436          ++file;
437
438          if (!_dbus_string_append (&config_file, file))
439            exit (1);
440        }
441      else if (prev_arg &&
442               strcmp (prev_arg, "--config-file") == 0)
443        {
444          check_two_config_files (&config_file, "config-file");
445
446          if (!_dbus_string_append (&config_file, arg))
447            exit (1);
448        }
449      else if (strcmp (arg, "--config-file") == 0)
450        {
451          /* wait for next arg */
452        }
453      else if (strstr (arg, "--address=") == arg)
454        {
455          const char *file;
456
457          check_two_addresses (&address, "address");
458
459          file = strchr (arg, '=');
460          ++file;
461
462          if (!_dbus_string_append (&address, file))
463            exit (1);
464        }
465      else if (prev_arg &&
466               strcmp (prev_arg, "--address") == 0)
467        {
468          check_two_addresses (&address, "address");
469
470          if (!_dbus_string_append (&address, arg))
471            exit (1);
472        }
473      else if (strcmp (arg, "--address") == 0)
474        {
475          /* wait for next arg */
476        }
477      else if (strstr (arg, "--print-address=") == arg)
478        {
479          const char *desc;
480
481          check_two_addr_descriptors (&addr_fd, "print-address");
482
483          desc = strchr (arg, '=');
484          ++desc;
485
486          if (!_dbus_string_append (&addr_fd, desc))
487            exit (1);
488
489          print_address = TRUE;
490        }
491      else if (prev_arg &&
492               strcmp (prev_arg, "--print-address") == 0)
493        {
494          check_two_addr_descriptors (&addr_fd, "print-address");
495
496          if (!_dbus_string_append (&addr_fd, arg))
497            exit (1);
498
499          print_address = TRUE;
500        }
501      else if (strcmp (arg, "--print-address") == 0)
502        {
503          print_address = TRUE; /* and we'll get the next arg if appropriate */
504        }
505      else if (strstr (arg, "--print-pid=") == arg)
506        {
507          const char *desc;
508
509          check_two_pid_descriptors (&pid_fd, "print-pid");
510
511          desc = strchr (arg, '=');
512          ++desc;
513
514          if (!_dbus_string_append (&pid_fd, desc))
515            exit (1);
516
517          print_pid = TRUE;
518        }
519      else if (prev_arg &&
520               strcmp (prev_arg, "--print-pid") == 0)
521        {
522          check_two_pid_descriptors (&pid_fd, "print-pid");
523
524          if (!_dbus_string_append (&pid_fd, arg))
525            exit (1);
526
527          print_pid = TRUE;
528        }
529      else if (strcmp (arg, "--print-pid") == 0)
530        {
531          print_pid = TRUE; /* and we'll get the next arg if appropriate */
532        }
533      else
534        {
535          usage ();
536        }
537
538      prev_arg = arg;
539
540      ++i;
541    }
542
543  if (_dbus_string_get_length (&config_file) == 0)
544    {
545      fprintf (stderr, "No configuration file specified.\n");
546      usage ();
547    }
548
549  _dbus_pipe_invalidate (&print_addr_pipe);
550  if (print_address)
551    {
552      _dbus_pipe_init_stdout (&print_addr_pipe);
553      if (_dbus_string_get_length (&addr_fd) > 0)
554        {
555          long val;
556          int end;
557          if (!_dbus_string_parse_int (&addr_fd, 0, &val, &end) ||
558              end != _dbus_string_get_length (&addr_fd) ||
559              val < 0 || val > _DBUS_INT_MAX)
560            {
561              fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
562                       _dbus_string_get_const_data (&addr_fd));
563              exit (1);
564            }
565
566          _dbus_pipe_init (&print_addr_pipe, val);
567        }
568    }
569  _dbus_string_free (&addr_fd);
570
571  _dbus_pipe_invalidate (&print_pid_pipe);
572  if (print_pid)
573    {
574      _dbus_pipe_init_stdout (&print_pid_pipe);
575      if (_dbus_string_get_length (&pid_fd) > 0)
576        {
577          long val;
578          int end;
579          if (!_dbus_string_parse_int (&pid_fd, 0, &val, &end) ||
580              end != _dbus_string_get_length (&pid_fd) ||
581              val < 0 || val > _DBUS_INT_MAX)
582            {
583              fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
584                       _dbus_string_get_const_data (&pid_fd));
585              exit (1);
586            }
587
588          _dbus_pipe_init (&print_pid_pipe, val);
589        }
590    }
591  _dbus_string_free (&pid_fd);
592
593  if (!bus_selinux_pre_init ())
594    {
595      _dbus_warn ("SELinux pre-initialization failed\n");
596      exit (1);
597    }
598
599  dbus_error_init (&error);
600  context = bus_context_new (&config_file, flags,
601                             &print_addr_pipe, &print_pid_pipe,
602                             _dbus_string_get_length(&address) > 0 ? &address : NULL,
603                             &error);
604  _dbus_string_free (&config_file);
605  if (context == NULL)
606    {
607      _dbus_warn ("Failed to start message bus: %s\n",
608                  error.message);
609      dbus_error_free (&error);
610      exit (1);
611    }
612
613  /* bus_context_new() closes the print_addr_pipe and
614   * print_pid_pipe
615   */
616
617#ifdef DBUS_UNIX
618  setup_reload_pipe (bus_context_get_loop (context));
619
620  /* POSIX signals are Unix-specific, and _dbus_set_signal_handler is
621   * unimplemented (and probably unimplementable) on Windows, so there's
622   * no point in trying to make the handler portable to non-Unix. */
623
624  _dbus_set_signal_handler (SIGTERM, signal_handler);
625#ifdef SIGHUP
626  _dbus_set_signal_handler (SIGHUP, signal_handler);
627#endif
628#ifdef DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX
629  _dbus_set_signal_handler (SIGIO, signal_handler);
630#endif /* DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX */
631#endif /* DBUS_UNIX */
632
633  _dbus_verbose ("We are on D-Bus...\n");
634  _dbus_loop_run (bus_context_get_loop (context));
635
636  bus_context_shutdown (context);
637  bus_context_unref (context);
638  bus_selinux_shutdown ();
639
640  return 0;
641}
642