main.c 17 KB
Newer Older
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 3 4 5
/* main.c  main() for message bus
 *
 * Copyright (C) 2003 Red Hat, Inc.
 *
6
 * Licensed under the Academic Free License version 2.1
7
 *
8 9 10 11 12 13 14 15 16
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
17
 *
18 19
 * You should have received a copy of the GNU General Public License
 * 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"
John (J5) Palmieri's avatar
John (J5) Palmieri committed
26
#include "driver.h"
27
#include <dbus/dbus-internals.h>
28
#include <dbus/dbus-watch.h>
29 30 31
#include <stdio.h>
#include <stdlib.h>
#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 39 40
#ifdef HAVE_UNISTD_H
#include <unistd.h>     /* for write() and STDERR_FILENO */
#endif
41
#include "selinux.h"
42 43 44

static BusContext *context;

45 46
#ifdef DBUS_UNIX

47 48 49 50
static int reload_pipe[2];
#define RELOAD_READ_END 0
#define RELOAD_WRITE_END 1

51
static void close_reload_pipe (DBusWatch **);
52

53 54 55 56 57 58
typedef enum
 {
   ACTION_RELOAD = 'r',
   ACTION_QUIT = 'q'
 } SignalAction;

59 60 61 62 63
static void
signal_handler (int sig)
{
  switch (sig)
    {
64
#ifdef SIGHUP
65
    case SIGHUP:
66 67
      {
        DBusString str;
68 69 70
        char action[2] = { ACTION_RELOAD, '\0' };

        _dbus_string_init_const (&str, action);
71
        if ((reload_pipe[RELOAD_WRITE_END] > 0) &&
72 73
            !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
          {
74 75 76 77 78 79
            /* If we receive SIGHUP often enough to fill the pipe buffer (4096
             * times on old Linux, 65536 on modern Linux) before it can be
             * drained, let's just warn and ignore. The configuration will be
             * reloaded while draining the pipe buffer, which is what we
             * wanted. It's harmless that it will be reloaded fewer times than
             * we asked for, since the reload is delayed anyway, so new changes
80 81 82 83 84 85 86 87 88 89
             * will be picked up.
             *
             * We use write() because _dbus_warn uses vfprintf, which isn't
             * async-signal-safe.
             *
             * This is necessarily Unix-specific, but so are POSIX signals,
             * so... */
            static const char message[] =
              "Unable to write to reload pipe - buffer full?\n";

90 91 92 93
            if (write (STDERR_FILENO, message, strlen (message)) != strlen (message))
              {
                /* ignore failure to write out a warning */
              }
94 95
          }
      }
96
      break;
97
#endif
98 99 100 101 102 103

    case SIGTERM:
      {
        DBusString str;
        char action[2] = { ACTION_QUIT, '\0' };
        _dbus_string_init_const (&str, action);
104
        if ((reload_pipe[RELOAD_WRITE_END] < 0) ||
105 106
            !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
          {
107 108 109 110
            /* If we can't write to the socket, dying seems a more
             * important response to SIGTERM than cleaning up sockets,
             * so we exit. We'd use exit(), but that's not async-signal-safe,
             * so we'll have to resort to _exit(). */
111
            static const char message[] =
112 113
              "Unable to write termination signal to pipe - buffer full?\n"
              "Will exit instead.\n";
114

115 116 117 118
            if (write (STDERR_FILENO, message, strlen (message)) != strlen (message))
              {
                /* ignore failure to write out a warning */
              }
119
            _exit (1);
120 121 122
          }
      }
      break;
123 124
    }
}
125
#endif /* DBUS_UNIX */
126 127 128 129

static void
usage (void)
{
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
  fprintf (stderr,
      DBUS_DAEMON_NAME
      " [--version]"
      " [--session]"
      " [--system]"
      " [--config-file=FILE]"
      " [--print-address[=DESCRIPTOR]]"
      " [--print-pid[=DESCRIPTOR]]"
      " [--introspect]"
      " [--address=ADDRESS]"
      " [--nopidfile]"
#ifdef DBUS_UNIX
      " [--fork]"
      " [--nofork]"
      " [--systemd-activation]"
#endif
      "\n");
147 148 149 150 151 152
  exit (1);
}

static void
version (void)
{
John (J5) Palmieri's avatar
John (J5) Palmieri committed
153
  printf ("D-Bus Message Bus Daemon %s\n"
154 155 156
          "Copyright (C) 2002, 2003 Red Hat, Inc., CodeFactory AB, and others\n"
          "This is free software; see the source for copying conditions.\n"
          "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
157
          DBUS_VERSION_STRING);
158 159 160
  exit (0);
}

John (J5) Palmieri's avatar
John (J5) Palmieri committed
161 162 163 164
static void
introspect (void)
{
  DBusString xml;
165
  const char *v_STRING;
John (J5) Palmieri's avatar
John (J5) Palmieri committed
166 167 168 169 170 171 172 173 174 175 176

  if (!_dbus_string_init (&xml))
    goto oom;

  if (!bus_driver_generate_introspect_string (&xml))
    {
      _dbus_string_free (&xml);
      goto oom;
    }

  v_STRING = _dbus_string_get_const_data (&xml);
177
  printf ("%s\n", v_STRING);
John (J5) Palmieri's avatar
John (J5) Palmieri committed
178 179

  exit (0);
180

John (J5) Palmieri's avatar
John (J5) Palmieri committed
181 182 183 184
 oom:
  _dbus_warn ("Can not introspect - Out of memory\n");
  exit (1);
}
185

186 187 188 189 190 191 192
static void
check_two_config_files (const DBusString *config_file,
                        const char       *extra_arg)
{
  if (_dbus_string_get_length (config_file) > 0)
    {
      fprintf (stderr, "--%s specified but configuration file %s already requested\n",
193
               extra_arg, _dbus_string_get_const_data (config_file));
194 195 196
      exit (1);
    }
}
197

198 199 200 201 202 203 204 205 206 207 208 209
static void
check_two_addresses (const DBusString *address,
                     const char       *extra_arg)
{
  if (_dbus_string_get_length (address) > 0)
    {
      fprintf (stderr, "--%s specified but address %s already requested\n",
               extra_arg, _dbus_string_get_const_data (address));
      exit (1);
    }
}

210 211 212 213 214 215 216 217 218 219 220 221
static void
check_two_addr_descriptors (const DBusString *addr_fd,
                            const char       *extra_arg)
{
  if (_dbus_string_get_length (addr_fd) > 0)
    {
      fprintf (stderr, "--%s specified but printing address to %s already requested\n",
               extra_arg, _dbus_string_get_const_data (addr_fd));
      exit (1);
    }
}

222 223 224 225 226 227 228 229 230 231 232 233
static void
check_two_pid_descriptors (const DBusString *pid_fd,
                           const char       *extra_arg)
{
  if (_dbus_string_get_length (pid_fd) > 0)
    {
      fprintf (stderr, "--%s specified but printing pid to %s already requested\n",
               extra_arg, _dbus_string_get_const_data (pid_fd));
      exit (1);
    }
}

234
#ifdef DBUS_UNIX
235 236 237 238 239 240 241
static dbus_bool_t
handle_reload_watch (DBusWatch    *watch,
		     unsigned int  flags,
		     void         *data)
{
  DBusError error;
  DBusString str;
242 243
  char *action_str;
  char action = '\0';
244 245 246 247

  while (!_dbus_string_init (&str))
    _dbus_wait_for_memory ();

248 249
  if ((reload_pipe[RELOAD_READ_END] > 0) &&
      _dbus_read_socket (reload_pipe[RELOAD_READ_END], &str, 1) != 1)
250 251
    {
      _dbus_warn ("Couldn't read from reload pipe.\n");
252
      close_reload_pipe (&watch);
253
      return TRUE;
254
    }
255 256 257 258 259 260

  action_str = _dbus_string_get_data (&str);
  if (action_str != NULL)
    {
      action = action_str[0];
    }
261 262
  _dbus_string_free (&str);

263 264 265 266
  /* this can only fail if we don't understand the config file
   * or OOM.  Either way we should just stick with the currently
   * loaded config.
   */
267
  dbus_error_init (&error);
268 269

  switch (action)
270
    {
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    case ACTION_RELOAD:
      if (! bus_context_reload_config (context, &error))
        {
          _DBUS_ASSERT_ERROR_IS_SET (&error);
          _dbus_assert (dbus_error_has_name (&error, DBUS_ERROR_FAILED) ||
                        dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY));
          _dbus_warn ("Unable to reload configuration: %s\n",
                      error.message);
          dbus_error_free (&error);
        }
      break;

    case ACTION_QUIT:
      {
        DBusLoop *loop;
        /*
         * On OSs without abstract sockets, we want to quit
         * gracefully rather than being killed by SIGTERM,
         * so that DBusServer gets a chance to clean up the
         * sockets from the filesystem. fd.o #38656
         */
        loop = bus_context_get_loop (context);
        if (loop != NULL)
          {
            _dbus_loop_quit (loop);
          }
      }
      break;

    default:
      break;
302
    }
303

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
  return TRUE;
}

static void
setup_reload_pipe (DBusLoop *loop)
{
  DBusError error;
  DBusWatch *watch;

  dbus_error_init (&error);

  if (!_dbus_full_duplex_pipe (&reload_pipe[0], &reload_pipe[1],
			       TRUE, &error))
    {
      _dbus_warn ("Unable to create reload pipe: %s\n",
		  error.message);
      dbus_error_free (&error);
      exit (1);
    }

  watch = _dbus_watch_new (reload_pipe[RELOAD_READ_END],
			   DBUS_WATCH_READABLE, TRUE,
			   handle_reload_watch, NULL, NULL);

  if (watch == NULL)
    {
      _dbus_warn ("Unable to create reload watch: %s\n",
		  error.message);
      dbus_error_free (&error);
      exit (1);
    }

336
  if (!_dbus_loop_add_watch (loop, watch))
337 338 339 340 341 342 343 344 345
    {
      _dbus_warn ("Unable to add reload watch to main loop: %s\n",
		  error.message);
      dbus_error_free (&error);
      exit (1);
    }

}

346
static void
347
close_reload_pipe (DBusWatch **watch)
348
{
349
    _dbus_loop_remove_watch (bus_context_get_loop (context), *watch);
350 351 352 353
    _dbus_watch_invalidate (*watch);
    _dbus_watch_unref (*watch);
    *watch = NULL;

354 355 356 357 358 359
    _dbus_close_socket (reload_pipe[RELOAD_READ_END], NULL);
    reload_pipe[RELOAD_READ_END] = -1;

    _dbus_close_socket (reload_pipe[RELOAD_WRITE_END], NULL);
    reload_pipe[RELOAD_WRITE_END] = -1;
}
360
#endif /* DBUS_UNIX */
361

362 363 364
int
main (int argc, char **argv)
{
365
  DBusError error;
366
  DBusString config_file;
367
  DBusString address;
368
  DBusString addr_fd;
369
  DBusString pid_fd;
370
  const char *prev_arg;
371 372
  DBusPipe print_addr_pipe;
  DBusPipe print_pid_pipe;
373
  int i;
374
  dbus_bool_t print_address;
375
  dbus_bool_t print_pid;
376
  BusContextFlags flags;
377

378
  if (!_dbus_string_init (&config_file))
379
    return 1;
380

381 382 383
  if (!_dbus_string_init (&address))
    return 1;

384 385
  if (!_dbus_string_init (&addr_fd))
    return 1;
386 387 388

  if (!_dbus_string_init (&pid_fd))
    return 1;
389

390
  print_address = FALSE;
391
  print_pid = FALSE;
392 393

  flags = BUS_CONTEXT_FLAG_WRITE_PID_FILE;
394

395 396 397
  prev_arg = NULL;
  i = 1;
  while (i < argc)
398
    {
399
      const char *arg = argv[i];
400

401 402 403
      if (strcmp (arg, "--help") == 0 ||
          strcmp (arg, "-h") == 0 ||
          strcmp (arg, "-?") == 0)
404 405 406
        {
          usage ();
        }
407
      else if (strcmp (arg, "--version") == 0)
408 409 410
        {
          version ();
        }
John (J5) Palmieri's avatar
John (J5) Palmieri committed
411
      else if (strcmp (arg, "--introspect") == 0)
412 413 414
        {
          introspect ();
        }
415
#ifdef DBUS_UNIX
416
      else if (strcmp (arg, "--nofork") == 0)
417 418 419 420
        {
          flags &= ~BUS_CONTEXT_FLAG_FORK_ALWAYS;
          flags |= BUS_CONTEXT_FLAG_FORK_NEVER;
        }
421
      else if (strcmp (arg, "--fork") == 0)
422 423 424 425
        {
          flags &= ~BUS_CONTEXT_FLAG_FORK_NEVER;
          flags |= BUS_CONTEXT_FLAG_FORK_ALWAYS;
        }
426
      else if (strcmp (arg, "--systemd-activation") == 0)
427 428 429
        {
          flags |= BUS_CONTEXT_FLAG_SYSTEMD_ACTIVATION;
        }
430 431 432 433 434
#endif
      else if (strcmp (arg, "--nopidfile") == 0)
        {
          flags &= ~BUS_CONTEXT_FLAG_WRITE_PID_FILE;
        }
435 436 437 438
      else if (strcmp (arg, "--system") == 0)
        {
          check_two_config_files (&config_file, "system");

439
          if (!_dbus_append_system_config_file (&config_file))
440 441 442 443 444 445
            exit (1);
        }
      else if (strcmp (arg, "--session") == 0)
        {
          check_two_config_files (&config_file, "session");

446
          if (!_dbus_append_session_config_file (&config_file))
447
            exit (1);
448 449 450 451 452 453
        }
      else if (strstr (arg, "--config-file=") == arg)
        {
          const char *file;

          check_two_config_files (&config_file, "config-file");
454

455 456 457 458 459 460 461 462 463 464
          file = strchr (arg, '=');
          ++file;

          if (!_dbus_string_append (&config_file, file))
            exit (1);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--config-file") == 0)
        {
          check_two_config_files (&config_file, "config-file");
465

466 467 468 469
          if (!_dbus_string_append (&config_file, arg))
            exit (1);
        }
      else if (strcmp (arg, "--config-file") == 0)
470 471 472
        {
          /* wait for next arg */
        }
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
      else if (strstr (arg, "--address=") == arg)
        {
          const char *file;

          check_two_addresses (&address, "address");

          file = strchr (arg, '=');
          ++file;

          if (!_dbus_string_append (&address, file))
            exit (1);
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--address") == 0)
        {
          check_two_addresses (&address, "address");

          if (!_dbus_string_append (&address, arg))
            exit (1);
        }
      else if (strcmp (arg, "--address") == 0)
494 495 496
        {
          /* wait for next arg */
        }
497 498 499 500 501
      else if (strstr (arg, "--print-address=") == arg)
        {
          const char *desc;

          check_two_addr_descriptors (&addr_fd, "print-address");
502

503 504 505 506 507 508 509 510 511 512 513 514
          desc = strchr (arg, '=');
          ++desc;

          if (!_dbus_string_append (&addr_fd, desc))
            exit (1);

          print_address = TRUE;
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--print-address") == 0)
        {
          check_two_addr_descriptors (&addr_fd, "print-address");
515

516 517 518 519 520 521
          if (!_dbus_string_append (&addr_fd, arg))
            exit (1);

          print_address = TRUE;
        }
      else if (strcmp (arg, "--print-address") == 0)
522 523 524
        {
          print_address = TRUE; /* and we'll get the next arg if appropriate */
        }
525 526 527 528 529
      else if (strstr (arg, "--print-pid=") == arg)
        {
          const char *desc;

          check_two_pid_descriptors (&pid_fd, "print-pid");
530

531 532 533 534 535 536 537 538 539 540 541 542
          desc = strchr (arg, '=');
          ++desc;

          if (!_dbus_string_append (&pid_fd, desc))
            exit (1);

          print_pid = TRUE;
        }
      else if (prev_arg &&
               strcmp (prev_arg, "--print-pid") == 0)
        {
          check_two_pid_descriptors (&pid_fd, "print-pid");
543

544 545
          if (!_dbus_string_append (&pid_fd, arg))
            exit (1);
546

547 548 549
          print_pid = TRUE;
        }
      else if (strcmp (arg, "--print-pid") == 0)
550 551 552
        {
          print_pid = TRUE; /* and we'll get the next arg if appropriate */
        }
553
      else
554 555 556
        {
          usage ();
        }
557

558
      prev_arg = arg;
559

560 561 562 563 564 565 566
      ++i;
    }

  if (_dbus_string_get_length (&config_file) == 0)
    {
      fprintf (stderr, "No configuration file specified.\n");
      usage ();
567
    }
568

569
  _dbus_pipe_invalidate (&print_addr_pipe);
570 571
  if (print_address)
    {
572
      _dbus_pipe_init_stdout (&print_addr_pipe);
573 574 575 576 577 578 579 580 581 582 583 584 585
      if (_dbus_string_get_length (&addr_fd) > 0)
        {
          long val;
          int end;
          if (!_dbus_string_parse_int (&addr_fd, 0, &val, &end) ||
              end != _dbus_string_get_length (&addr_fd) ||
              val < 0 || val > _DBUS_INT_MAX)
            {
              fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
                       _dbus_string_get_const_data (&addr_fd));
              exit (1);
            }

586
          _dbus_pipe_init (&print_addr_pipe, val);
587 588
        }
    }
589
  _dbus_string_free (&addr_fd);
590

591
  _dbus_pipe_invalidate (&print_pid_pipe);
592 593
  if (print_pid)
    {
594
      _dbus_pipe_init_stdout (&print_pid_pipe);
595 596 597 598 599 600 601 602 603 604 605 606 607
      if (_dbus_string_get_length (&pid_fd) > 0)
        {
          long val;
          int end;
          if (!_dbus_string_parse_int (&pid_fd, 0, &val, &end) ||
              end != _dbus_string_get_length (&pid_fd) ||
              val < 0 || val > _DBUS_INT_MAX)
            {
              fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
                       _dbus_string_get_const_data (&pid_fd));
              exit (1);
            }

608
          _dbus_pipe_init (&print_pid_pipe, val);
609 610
        }
    }
611
  _dbus_string_free (&pid_fd);
612

613
  if (!bus_selinux_pre_init ())
614
    {
615
      _dbus_warn ("SELinux pre-initialization failed\n");
616 617 618
      exit (1);
    }

619
  dbus_error_init (&error);
620
  context = bus_context_new (&config_file, flags,
621
                             &print_addr_pipe, &print_pid_pipe,
622
                             _dbus_string_get_length(&address) > 0 ? &address : NULL,
623
                             &error);
624
  _dbus_string_free (&config_file);
625
  if (context == NULL)
626
    {
627 628 629
      _dbus_warn ("Failed to start message bus: %s\n",
                  error.message);
      dbus_error_free (&error);
630
      exit (1);
631
    }
632

633 634 635
  /* bus_context_new() closes the print_addr_pipe and
   * print_pid_pipe
   */
636

637
#ifdef DBUS_UNIX
638
  setup_reload_pipe (bus_context_get_loop (context));
639

640 641 642
  /* POSIX signals are Unix-specific, and _dbus_set_signal_handler is
   * unimplemented (and probably unimplementable) on Windows, so there's
   * no point in trying to make the handler portable to non-Unix. */
643

644
  _dbus_set_signal_handler (SIGTERM, signal_handler);
645
#ifdef SIGHUP
646
  _dbus_set_signal_handler (SIGHUP, signal_handler);
647
#endif
648
#endif /* DBUS_UNIX */
649

650
  _dbus_verbose ("We are on D-Bus...\n");
651
  _dbus_loop_run (bus_context_get_loop (context));
652

653 654
  bus_context_shutdown (context);
  bus_context_unref (context);
655
  bus_selinux_shutdown ();
656

657 658
  return 0;
}