main.c 19.5 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 Palmieri's avatar
John 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
#include "apparmor.h"
43
#include "audit.h"
44

45 46 47 48
#ifdef DBUS_UNIX
#include <dbus/dbus-sysdeps-unix.h>
#endif

49 50
static BusContext *context;

51 52
#ifdef DBUS_UNIX

53 54 55
/* Despite its name and its unidirectional nature, this is actually
 * a socket pair. */
static DBusSocket reload_pipe[2];
56 57 58
#define RELOAD_READ_END 0
#define RELOAD_WRITE_END 1

59
static void close_reload_pipe (DBusWatch **);
60

61 62 63 64 65 66
typedef enum
 {
   ACTION_RELOAD = 'r',
   ACTION_QUIT = 'q'
 } SignalAction;

67 68 69
static void
signal_handler (int sig)
{
70 71 72 73
  /* Signal handlers that might set errno must save and restore the errno
   * that the interrupted function might have been relying on. */
  int saved_errno = errno;

74 75 76
  switch (sig)
    {
    case SIGHUP:
77 78
      {
        DBusString str;
79 80 81
        char action[2] = { ACTION_RELOAD, '\0' };

        _dbus_string_init_const (&str, action);
82
        if ((reload_pipe[RELOAD_WRITE_END].fd > 0) &&
83 84
            !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
          {
85 86 87 88 89 90
            /* 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
91 92 93 94 95 96 97 98 99 100
             * 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";

101 102
            if (write (STDERR_FILENO, message, strlen (message)) !=
                (ssize_t) strlen (message))
103 104 105
              {
                /* ignore failure to write out a warning */
              }
106 107
          }
      }
108
      break;
109 110 111 112 113 114

    case SIGTERM:
      {
        DBusString str;
        char action[2] = { ACTION_QUIT, '\0' };
        _dbus_string_init_const (&str, action);
115
        if ((reload_pipe[RELOAD_WRITE_END].fd < 0) ||
116 117
            !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
          {
118 119 120 121
            /* 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(). */
122
            static const char message[] =
123 124
              "Unable to write termination signal to pipe - buffer full?\n"
              "Will exit instead.\n";
125

126 127
            if (write (STDERR_FILENO, message, strlen (message)) !=
                (ssize_t) strlen (message))
128 129 130
              {
                /* ignore failure to write out a warning */
              }
131
            _exit (1);
132 133 134
          }
      }
      break;
135 136 137 138 139

    default:
      /* can't happen unless this signal handler gets used for a wrong
       * signal, but keep -Wswitch-default happy */
      break;
140
    }
141 142

  errno = saved_errno;
143
}
144
#endif /* DBUS_UNIX */
145

146 147
static void usage (void) _DBUS_GNUC_NORETURN;

148 149 150
static void
usage (void)
{
151 152 153 154 155 156 157 158 159 160 161
  fprintf (stderr,
      DBUS_DAEMON_NAME
      " [--version]"
      " [--session]"
      " [--system]"
      " [--config-file=FILE]"
      " [--print-address[=DESCRIPTOR]]"
      " [--print-pid[=DESCRIPTOR]]"
      " [--introspect]"
      " [--address=ADDRESS]"
      " [--nopidfile]"
162 163 164
      " [--nosyslog]"
      " [--syslog]"
      " [--syslog-only]"
165
      " [--nofork]"
166 167 168 169 170
#ifdef DBUS_UNIX
      " [--fork]"
      " [--systemd-activation]"
#endif
      "\n");
171 172 173
  exit (1);
}

174 175
static void version (void) _DBUS_GNUC_NORETURN;

176 177 178
static void
version (void)
{
John Palmieri's avatar
John Palmieri committed
179
  printf ("D-Bus Message Bus Daemon %s\n"
180 181 182
          "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",
183
          DBUS_VERSION_STRING);
184 185 186
  exit (0);
}

187 188
static void introspect (void) _DBUS_GNUC_NORETURN;

John Palmieri's avatar
John Palmieri committed
189 190 191 192
static void
introspect (void)
{
  DBusString xml;
193
  const char *v_STRING;
John Palmieri's avatar
John Palmieri committed
194 195 196 197

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

198
  if (!bus_driver_generate_introspect_string (&xml, TRUE, NULL))
John Palmieri's avatar
John Palmieri committed
199 200 201 202 203 204
    {
      _dbus_string_free (&xml);
      goto oom;
    }

  v_STRING = _dbus_string_get_const_data (&xml);
205
  printf ("%s\n", v_STRING);
John Palmieri's avatar
John Palmieri committed
206 207

  exit (0);
208

John Palmieri's avatar
John Palmieri committed
209
 oom:
210
  _dbus_warn ("Can not introspect - Out of memory");
John Palmieri's avatar
John Palmieri committed
211 212
  exit (1);
}
213

214 215 216 217 218 219 220
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",
221
               extra_arg, _dbus_string_get_const_data (config_file));
222 223 224
      exit (1);
    }
}
225

226 227 228 229 230 231 232 233 234 235 236 237
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);
    }
}

238 239 240 241 242 243 244 245 246 247 248 249
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);
    }
}

250 251 252 253 254 255 256 257 258 259 260 261
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);
    }
}

262
#ifdef DBUS_UNIX
263 264 265 266 267 268 269
static dbus_bool_t
handle_reload_watch (DBusWatch    *watch,
		     unsigned int  flags,
		     void         *data)
{
  DBusError error;
  DBusString str;
270 271
  char *action_str;
  char action = '\0';
272 273 274 275

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

276
  if ((reload_pipe[RELOAD_READ_END].fd > 0) &&
277
      _dbus_read_socket (reload_pipe[RELOAD_READ_END], &str, 1) != 1)
278
    {
279
      _dbus_warn ("Couldn't read from reload pipe.");
280
      close_reload_pipe (&watch);
281
      return TRUE;
282
    }
283 284 285 286 287 288

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

291 292 293 294
  /* 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.
   */
295
  dbus_error_init (&error);
296 297

  switch (action)
298
    {
299 300 301 302 303 304
    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));
305
          _dbus_warn ("Unable to reload configuration: %s",
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
                      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;
330
    }
331

332 333 334 335 336 337 338 339 340 341 342
  return TRUE;
}

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

  dbus_error_init (&error);

343 344
  if (!_dbus_socketpair (&reload_pipe[0], &reload_pipe[1],
                         TRUE, &error))
345
    {
346
      _dbus_warn ("Unable to create reload pipe: %s",
347 348 349 350 351
		  error.message);
      dbus_error_free (&error);
      exit (1);
    }

352
  watch = _dbus_watch_new (_dbus_socket_get_pollable (reload_pipe[RELOAD_READ_END]),
353 354
                           DBUS_WATCH_READABLE, TRUE,
                           handle_reload_watch, NULL, NULL);
355 356 357

  if (watch == NULL)
    {
358
      _dbus_warn ("Unable to create reload watch: %s",
359 360 361 362 363
		  error.message);
      dbus_error_free (&error);
      exit (1);
    }

364
  if (!_dbus_loop_add_watch (loop, watch))
365
    {
366
      _dbus_warn ("Unable to add reload watch to main loop: %s",
367 368 369 370 371 372 373
		  error.message);
      dbus_error_free (&error);
      exit (1);
    }

}

374
static void
375
close_reload_pipe (DBusWatch **watch)
376
{
377
    _dbus_loop_remove_watch (bus_context_get_loop (context), *watch);
378 379 380 381
    _dbus_watch_invalidate (*watch);
    _dbus_watch_unref (*watch);
    *watch = NULL;

382
    _dbus_close_socket (reload_pipe[RELOAD_READ_END], NULL);
383
    _dbus_socket_invalidate (&reload_pipe[RELOAD_READ_END]);
384 385

    _dbus_close_socket (reload_pipe[RELOAD_WRITE_END], NULL);
386
    _dbus_socket_invalidate (&reload_pipe[RELOAD_WRITE_END]);
387
}
388
#endif /* DBUS_UNIX */
389

390 391 392
int
main (int argc, char **argv)
{
393
  DBusError error;
394
  DBusString config_file;
395
  DBusString address;
396
  DBusString addr_fd;
397
  DBusString pid_fd;
398
  const char *prev_arg;
399 400
  DBusPipe print_addr_pipe;
  DBusPipe print_pid_pipe;
401
  int i;
402
  dbus_bool_t print_address;
403
  dbus_bool_t print_pid;
404
  BusContextFlags flags;
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
#ifdef DBUS_UNIX
  const char *error_str;

  /* Redirect stdin from /dev/null since we will never need it, and
   * redirect stdout and stderr to /dev/null if not already open.
   *
   * We should do this as the very first thing, to ensure that when we
   * create other file descriptors (for example for epoll, inotify or
   * a socket), they never get assigned as fd 0, 1 or 2. If they were,
   * which could happen if our caller had (incorrectly) closed those
   * standard fds, they'd get closed when we daemonize - for example,
   * closing our listening socket would stop us listening, and closing
   * a Linux epoll socket would cause the main loop to fail. */
  if (!_dbus_ensure_standard_fds (DBUS_FORCE_STDIN_NULL, &error_str))
    {
      fprintf (stderr,
               "dbus-daemon: fatal error setting up standard fds: %s: %s\n",
               error_str, _dbus_strerror (errno));
      return 1;
    }
#endif
426

427
  if (!_dbus_string_init (&config_file))
428
    return 1;
429

430 431 432
  if (!_dbus_string_init (&address))
    return 1;

433 434
  if (!_dbus_string_init (&addr_fd))
    return 1;
435 436 437

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

439
  print_address = FALSE;
440
  print_pid = FALSE;
441 442

  flags = BUS_CONTEXT_FLAG_WRITE_PID_FILE;
443

444 445 446
  prev_arg = NULL;
  i = 1;
  while (i < argc)
447
    {
448
      const char *arg = argv[i];
449

450 451 452
      if (strcmp (arg, "--help") == 0 ||
          strcmp (arg, "-h") == 0 ||
          strcmp (arg, "-?") == 0)
453 454 455
        {
          usage ();
        }
456
      else if (strcmp (arg, "--version") == 0)
457 458 459
        {
          version ();
        }
John Palmieri's avatar
John Palmieri committed
460
      else if (strcmp (arg, "--introspect") == 0)
461 462 463
        {
          introspect ();
        }
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
      else if (strcmp (arg, "--nosyslog") == 0)
        {
          flags &= ~BUS_CONTEXT_FLAG_SYSLOG_ALWAYS;
          flags |= BUS_CONTEXT_FLAG_SYSLOG_NEVER;
        }
      else if (strcmp (arg, "--syslog") == 0)
        {
          flags &= ~BUS_CONTEXT_FLAG_SYSLOG_NEVER;
          flags |= BUS_CONTEXT_FLAG_SYSLOG_ALWAYS;
        }
      else if (strcmp (arg, "--syslog-only") == 0)
        {
          flags &= ~BUS_CONTEXT_FLAG_SYSLOG_NEVER;
          flags |= (BUS_CONTEXT_FLAG_SYSLOG_ALWAYS|BUS_CONTEXT_FLAG_SYSLOG_ONLY);
        }
479
      else if (strcmp (arg, "--nofork") == 0)
480 481 482 483
        {
          flags &= ~BUS_CONTEXT_FLAG_FORK_ALWAYS;
          flags |= BUS_CONTEXT_FLAG_FORK_NEVER;
        }
484
#ifdef DBUS_UNIX
485
      else if (strcmp (arg, "--fork") == 0)
486 487 488 489
        {
          flags &= ~BUS_CONTEXT_FLAG_FORK_NEVER;
          flags |= BUS_CONTEXT_FLAG_FORK_ALWAYS;
        }
490
      else if (strcmp (arg, "--systemd-activation") == 0)
491 492 493
        {
          flags |= BUS_CONTEXT_FLAG_SYSTEMD_ACTIVATION;
        }
494 495 496 497 498
#endif
      else if (strcmp (arg, "--nopidfile") == 0)
        {
          flags &= ~BUS_CONTEXT_FLAG_WRITE_PID_FILE;
        }
499 500 501 502
      else if (strcmp (arg, "--system") == 0)
        {
          check_two_config_files (&config_file, "system");

503
          if (!_dbus_get_system_config_file (&config_file))
504 505 506 507 508 509
            exit (1);
        }
      else if (strcmp (arg, "--session") == 0)
        {
          check_two_config_files (&config_file, "session");

510
          if (!_dbus_get_session_config_file (&config_file))
511
            exit (1);
512 513 514 515 516 517
        }
      else if (strstr (arg, "--config-file=") == arg)
        {
          const char *file;

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

519 520 521 522 523 524 525 526 527 528
          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");
529

530 531 532 533
          if (!_dbus_string_append (&config_file, arg))
            exit (1);
        }
      else if (strcmp (arg, "--config-file") == 0)
534 535 536
        {
          /* wait for next arg */
        }
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
      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)
558 559 560
        {
          /* wait for next arg */
        }
561 562 563 564 565
      else if (strstr (arg, "--print-address=") == arg)
        {
          const char *desc;

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

567 568 569 570 571 572 573 574 575 576 577 578
          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");
579

580 581 582 583 584 585
          if (!_dbus_string_append (&addr_fd, arg))
            exit (1);

          print_address = TRUE;
        }
      else if (strcmp (arg, "--print-address") == 0)
586 587 588
        {
          print_address = TRUE; /* and we'll get the next arg if appropriate */
        }
589 590 591 592 593
      else if (strstr (arg, "--print-pid=") == arg)
        {
          const char *desc;

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

595 596 597 598 599 600 601 602 603 604 605 606
          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");
607

608 609
          if (!_dbus_string_append (&pid_fd, arg))
            exit (1);
610

611 612 613
          print_pid = TRUE;
        }
      else if (strcmp (arg, "--print-pid") == 0)
614 615 616
        {
          print_pid = TRUE; /* and we'll get the next arg if appropriate */
        }
617
      else
618 619 620
        {
          usage ();
        }
621

622
      prev_arg = arg;
623

624 625 626 627 628 629 630
      ++i;
    }

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

633
  _dbus_pipe_invalidate (&print_addr_pipe);
634 635
  if (print_address)
    {
636
      _dbus_pipe_init_stdout (&print_addr_pipe);
637 638 639 640 641 642 643 644 645 646 647 648 649
      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);
            }

650
          _dbus_pipe_init (&print_addr_pipe, val);
651 652
        }
    }
653
  _dbus_string_free (&addr_fd);
654

655
  _dbus_pipe_invalidate (&print_pid_pipe);
656 657
  if (print_pid)
    {
658
      _dbus_pipe_init_stdout (&print_pid_pipe);
659 660 661 662 663 664 665 666 667 668 669 670 671
      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);
            }

672
          _dbus_pipe_init (&print_pid_pipe, val);
673 674
        }
    }
675
  _dbus_string_free (&pid_fd);
676

677
  if (!bus_selinux_pre_init ())
678
    {
679
      _dbus_warn ("SELinux pre-initialization failed");
680 681 682
      exit (1);
    }

683 684
  if (!bus_apparmor_pre_init ())
    {
685
      _dbus_warn ("AppArmor pre-initialization failed: out of memory");
686 687 688
      exit (1);
    }

689
  dbus_error_init (&error);
690
  context = bus_context_new (&config_file, flags,
691
                             &print_addr_pipe, &print_pid_pipe,
692
                             _dbus_string_get_length(&address) > 0 ? &address : NULL,
693
                             &error);
694
  _dbus_string_free (&config_file);
695
  _dbus_string_free (&address);
696
  if (context == NULL)
697
    {
698
      _dbus_warn ("Failed to start message bus: %s",
699 700
                  error.message);
      dbus_error_free (&error);
701
      exit (1);
702
    }
703

704 705 706
  /* bus_context_new() closes the print_addr_pipe and
   * print_pid_pipe
   */
707

708
#ifdef DBUS_UNIX
709
  setup_reload_pipe (bus_context_get_loop (context));
710

711 712 713
  /* 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. */
714

715
  _dbus_set_signal_handler (SIGTERM, signal_handler);
716
  _dbus_set_signal_handler (SIGHUP, signal_handler);
717
#endif /* DBUS_UNIX */
718

719
  _dbus_verbose ("We are on D-Bus...\n");
720
  _dbus_loop_run (bus_context_get_loop (context));
721

722 723
  bus_context_shutdown (context);
  bus_context_unref (context);
724
  bus_selinux_shutdown ();
725
  bus_apparmor_shutdown ();
726
  bus_audit_shutdown ();
727

728 729
  return 0;
}