]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - sysdep/unix/main.c
Unix: Remove removed option from help
[thirdparty/bird.git] / sysdep / unix / main.c
index 533754ea3ea02622d2e1089213397aadf9ca45c6..921115b1a9cff12568e6ebe7a3bd0f7548fb27ff 100644 (file)
@@ -8,17 +8,26 @@
 
 #undef LOCAL_DEBUG
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+#include <libgen.h>
 
 #include "nest/bird.h"
 #include "lib/lists.h"
 #include "lib/resource.h"
 #include "lib/socket.h"
 #include "lib/event.h"
+#include "lib/timer.h"
 #include "lib/string.h"
 #include "nest/route.h"
 #include "nest/protocol.h"
  *     Debugging
  */
 
-#ifdef DEBUGGING
-static int debug_flag = 1;
-#else
-static int debug_flag = 0;
-#endif
-
 void
 async_dump(void)
 {
@@ -48,7 +51,7 @@ async_dump(void)
 
   rdump(&root_pool);
   sk_dump_all();
-  tm_dump_all();
+  // XXXX tm_dump_all();
   if_dump_all();
   neigh_dump_all();
   rta_dump_all();
@@ -58,17 +61,100 @@ async_dump(void)
   debug("\n");
 }
 
+/*
+ *     Dropping privileges
+ */
+
+#ifdef CONFIG_RESTRICTED_PRIVILEGES
+#include CONFIG_INCLUDE_SYSPRIV_H
+#else
+
+static inline void
+drop_uid(uid_t uid UNUSED)
+{
+  die("Cannot change user on this platform");
+}
+
+#endif
+
+static inline void
+drop_gid(gid_t gid)
+{
+  if (setgid(gid) < 0)
+    die("setgid: %m");
+}
+
 /*
  *     Reading the Configuration
  */
 
-static int conf_fd;
-static char *config_name = PATH_CONFIG;
+#ifdef PATH_IPROUTE_DIR
+
+static inline void
+add_num_const(char *name, int val)
+{
+  struct symbol *s = cf_get_symbol(name);
+  s->class = SYM_CONSTANT | T_INT;
+  s->def = cfg_allocz(sizeof(struct f_val));
+  SYM_TYPE(s) = T_INT;
+  SYM_VAL(s).i = val;
+}
+
+/* the code of read_iproute_table() is based on
+   rtnl_tab_initialize() from iproute2 package */
+static void
+read_iproute_table(char *file, char *prefix, int max)
+{
+  char buf[512], namebuf[512];
+  char *name;
+  int val;
+  FILE *fp;
+
+  strcpy(namebuf, prefix);
+  name = namebuf + strlen(prefix);
+
+  fp = fopen(file, "r");
+  if (!fp)
+    return;
+
+  while (fgets(buf, sizeof(buf), fp))
+  {
+    char *p = buf;
+
+    while (*p == ' ' || *p == '\t')
+      p++;
+
+    if (*p == '#' || *p == '\n' || *p == 0)
+      continue;
+
+    if (sscanf(p, "0x%x %s\n", &val, name) != 2 &&
+       sscanf(p, "0x%x %s #", &val, name) != 2 &&
+       sscanf(p, "%d %s\n", &val, name) != 2 &&
+       sscanf(p, "%d %s #", &val, name) != 2)
+      continue;
+
+    if (val < 0 || val > max)
+      continue;
+
+    for(p = name; *p; p++)
+      if ((*p < 'a' || *p > 'z') && (*p < '0' || *p > '9') && (*p != '_'))
+       *p = '_';
+
+    add_num_const(namebuf, val);
+  }
+
+  fclose(fp);
+}
+
+#endif // PATH_IPROUTE_DIR
+
+
+static char *config_name = PATH_CONFIG_FILE;
 
 static int
-cf_read(byte *dest, unsigned int len)
+cf_read(byte *dest, uint len, int fd)
 {
-  int l = read(conf_fd, dest, len);
+  int l = read(fd, dest, len);
   if (l < 0)
     cf_error("Read error");
   return l;
@@ -78,12 +164,22 @@ void
 sysdep_preconfig(struct config *c)
 {
   init_list(&c->logfiles);
+
+  c->latency_limit = UNIX_DEFAULT_LATENCY_LIMIT;
+  c->watchdog_warning = UNIX_DEFAULT_WATCHDOG_WARNING;
+
+#ifdef PATH_IPROUTE_DIR
+  read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
+  read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256);
+  read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256);
+  read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256);
+#endif
 }
 
 int
 sysdep_commit(struct config *new, struct config *old UNUSED)
 {
-  log_switch(debug_flag, &new->logfiles);
+  log_switch(0, &new->logfiles, new->syslog_name);
   return 0;
 }
 
@@ -94,16 +190,16 @@ unix_read_config(struct config **cp, char *name)
   int ret;
 
   *cp = conf;
-  conf_fd = open(name, O_RDONLY);
-  if (conf_fd < 0)
+  conf->file_fd = open(name, O_RDONLY);
+  if (conf->file_fd < 0)
     return 0;
   cf_read_hook = cf_read;
   ret = config_parse(conf);
-  close(conf_fd);
+  close(conf->file_fd);
   return ret;
 }
 
-static void
+static struct config *
 read_config(void)
 {
   struct config *conf;
@@ -111,11 +207,12 @@ read_config(void)
   if (!unix_read_config(&conf, config_name))
     {
       if (conf->err_msg)
-       die("%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
+       die("%s:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, conf->err_msg);
       else
        die("Unable to open configuration file %s: %m", config_name);
     }
-  config_commit(conf, RECONFIG_HARD);
+
+  return conf;
 }
 
 void
@@ -127,50 +224,121 @@ async_config(void)
   if (!unix_read_config(&conf, config_name))
     {
       if (conf->err_msg)
-       log(L_ERR "%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
+       log(L_ERR "%s:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, conf->err_msg);
       else
        log(L_ERR "Unable to open configuration file %s: %m", config_name);
       config_free(conf);
     }
   else
-    config_commit(conf, RECONFIG_HARD);
+    config_commit(conf, RECONFIG_HARD, 0);
 }
 
-void
-cmd_reconfig(char *name, int type)
+static struct config *
+cmd_read_config(char *name)
 {
   struct config *conf;
 
   if (!name)
     name = config_name;
+
   cli_msg(-2, "Reading configuration from %s", name);
   if (!unix_read_config(&conf, name))
     {
       if (conf->err_msg)
-       cli_msg(8002, "%s, line %d: %s", name, conf->err_lino, conf->err_msg);
+       cli_msg(8002, "%s:%d:%d %s", conf->err_file_name, conf->err_lino, conf->err_chno, conf->err_msg);
       else
        cli_msg(8002, "%s: %m", name);
       config_free(conf);
+      conf = NULL;
     }
-  else
+
+  return conf;
+}
+
+void
+cmd_check_config(char *name)
+{
+  struct config *conf = cmd_read_config(name);
+  if (!conf)
+    return;
+
+  cli_msg(20, "Configuration OK");
+  config_free(conf);
+}
+
+static void
+cmd_reconfig_msg(int r)
+{
+  switch (r)
     {
-      switch (config_commit(conf, type))
-       {
-       case CONF_DONE:
-         cli_msg(3, "Reconfigured.");
-         break;
-       case CONF_PROGRESS:
-         cli_msg(4, "Reconfiguration in progress.");
-         break;
-       case CONF_SHUTDOWN:
-         cli_msg(6, "Reconfiguration ignored, shutting down.");
-         break;
-       default:
-         cli_msg(5, "Reconfiguration already in progress, queueing new config");
-       }
+    case CONF_DONE:    cli_msg( 3, "Reconfigured"); break;
+    case CONF_PROGRESS: cli_msg( 4, "Reconfiguration in progress"); break;
+    case CONF_QUEUED:  cli_msg( 5, "Reconfiguration already in progress, queueing new config"); break;
+    case CONF_UNQUEUED:        cli_msg(17, "Reconfiguration already in progress, removing queued config"); break;
+    case CONF_CONFIRM: cli_msg(18, "Reconfiguration confirmed"); break;
+    case CONF_SHUTDOWN:        cli_msg( 6, "Reconfiguration ignored, shutting down"); break;
+    case CONF_NOTHING: cli_msg(19, "Nothing to do"); break;
+    default:           break;
+    }
+}
+
+/* Hack for scheduled undo notification */
+cli *cmd_reconfig_stored_cli;
+
+void
+cmd_reconfig_undo_notify(void)
+{
+  if (cmd_reconfig_stored_cli)
+    {
+      cli *c = cmd_reconfig_stored_cli;
+      cli_printf(c, CLI_ASYNC_CODE, "Config timeout expired, starting undo");
+      cli_write_trigger(c);
     }
 }
 
+void
+cmd_reconfig(char *name, int type, uint timeout)
+{
+  if (cli_access_restricted())
+    return;
+
+  struct config *conf = cmd_read_config(name);
+  if (!conf)
+    return;
+
+  int r = config_commit(conf, type, timeout);
+
+  if ((r >= 0) && (timeout > 0))
+    {
+      cmd_reconfig_stored_cli = this_cli;
+      cli_msg(-22, "Undo scheduled in %d s", timeout);
+    }
+
+  cmd_reconfig_msg(r);
+}
+
+void
+cmd_reconfig_confirm(void)
+{
+  if (cli_access_restricted())
+    return;
+
+  int r = config_confirm();
+  cmd_reconfig_msg(r);
+}
+
+void
+cmd_reconfig_undo(void)
+{
+  if (cli_access_restricted())
+    return;
+
+  cli_msg(-21, "Undo requested");
+
+  int r = config_undo();
+  cmd_reconfig_msg(r);
+}
+
 /*
  *     Command-Line Interface
  */
@@ -248,7 +416,7 @@ cli_get_command(cli *c)
 }
 
 static int
-cli_rx(sock *s, int size UNUSED)
+cli_rx(sock *s, uint size UNUSED)
 {
   cli_kick(s->data);
   return 0;
@@ -268,7 +436,7 @@ cli_err(sock *s, int err)
 }
 
 static int
-cli_connect(sock *s, int size UNUSED)
+cli_connect(sock *s, uint size UNUSED)
 {
   cli *c;
 
@@ -279,6 +447,7 @@ cli_connect(sock *s, int size UNUSED)
   s->err_hook = cli_err;
   s->data = c = cli_new(s);
   s->pool = c->pool;           /* We need to have all the socket buffers allocated in the cli pool */
+  s->fast_rx = 1;
   c->rx_pos = c->rx_buf;
   c->rx_aux = NULL;
   rmove(s, c->pool);
@@ -286,7 +455,7 @@ cli_connect(sock *s, int size UNUSED)
 }
 
 static void
-cli_init_unix(void)
+cli_init_unix(uid_t use_uid, gid_t use_gid)
 {
   sock *s;
 
@@ -295,14 +464,88 @@ cli_init_unix(void)
   s->type = SK_UNIX_PASSIVE;
   s->rx_hook = cli_connect;
   s->rbsize = 1024;
+  s->fast_rx = 1;
+
+  /* Return value intentionally ignored */
+  unlink(path_control_socket);
+
   if (sk_open_unix(s, path_control_socket) < 0)
-    die("Unable to create control socket %s", path_control_socket);
+    die("Cannot create control socket %s: %m", path_control_socket);
+
+  if (use_uid || use_gid)
+    if (chown(path_control_socket, use_uid, use_gid) < 0)
+      die("chown: %m");
+
+  if (chmod(path_control_socket, 0660) < 0)
+    die("chmod: %m");
+}
+
+/*
+ *     PID file
+ */
+
+static char *pid_file;
+static int pid_fd;
+
+static inline void
+open_pid_file(void)
+{
+  if (!pid_file)
+    return;
+
+  pid_fd = open(pid_file, O_WRONLY|O_CREAT, 0664);
+  if (pid_fd < 0)
+    die("Cannot create PID file %s: %m", pid_file);
+}
+
+static inline void
+write_pid_file(void)
+{
+  int pl, rv;
+  char ps[24];
+
+  if (!pid_file)
+    return;
+
+  /* We don't use PID file for uniqueness, so no need for locking */
+
+  pl = bsnprintf(ps, sizeof(ps), "%ld\n", (long) getpid());
+  if (pl < 0)
+    bug("PID buffer too small");
+
+  rv = ftruncate(pid_fd, 0);
+  if (rv < 0)
+    die("fruncate: %m");
+
+  rv = write(pid_fd, ps, pl);
+  if(rv < 0)
+    die("write: %m");
+
+  close(pid_fd);
+}
+
+static inline void
+unlink_pid_file(void)
+{
+  if (pid_file)
+    unlink(pid_file);
 }
 
+
 /*
  *     Shutdown
  */
 
+void
+cmd_shutdown(void)
+{
+  if (cli_access_restricted())
+    return;
+
+  cli_msg(7, "Shutdown requested");
+  order_shutdown();
+}
+
 void
 async_shutdown(void)
 {
@@ -313,8 +556,9 @@ async_shutdown(void)
 void
 sysdep_shutdown_done(void)
 {
+  unlink_pid_file();
   unlink(path_control_socket);
-  log_msg(L_FATAL "System shutdown completed");
+  log_msg(L_FATAL "Shutdown completed");
   exit(0);
 }
 
@@ -322,6 +566,10 @@ sysdep_shutdown_done(void)
  *     Signals
  */
 
+volatile int async_config_flag;
+volatile int async_dump_flag;
+volatile int async_shutdown_flag;
+
 static void
 handle_sighup(int sig UNUSED)
 {
@@ -343,6 +591,8 @@ handle_sigterm(int sig UNUSED)
   async_shutdown_flag = 1;
 }
 
+void watchdog_sigalrm(int sig UNUSED);
+
 static void
 signal_init(void)
 {
@@ -358,6 +608,9 @@ signal_init(void)
   sa.sa_handler = handle_sigterm;
   sa.sa_flags = SA_RESTART;
   sigaction(SIGTERM, &sa, NULL);
+  sa.sa_handler = watchdog_sigalrm;
+  sa.sa_flags = 0;
+  sigaction(SIGALRM, &sa, NULL);
   signal(SIGPIPE, SIG_IGN);
 }
 
@@ -365,56 +618,183 @@ signal_init(void)
  *     Parsing of command-line arguments
  */
 
-static char *opt_list = "c:dD:ps:";
+static char *opt_list = "bc:dD:ps:P:u:g:flRh";
+static int parse_and_exit;
+char *bird_name;
+static char *use_user;
+static char *use_group;
+static int run_in_foreground = 0;
+
+static void
+display_usage(void)
+{
+  fprintf(stderr, "Usage: %s [--version] [--help] [-c <config-file>] [OPTIONS]\n", bird_name);
+}
+
+static void
+display_help(void)
+{
+  display_usage();
+
+  fprintf(stderr,
+    "\n"
+    "Options: \n"
+    "  -c <config-file>     Use given configuration file instead of\n"
+    "                       "  PATH_CONFIG_FILE "\n"
+    "  -d                   Enable debug messages and run bird in foreground\n"
+    "  -D <debug-file>      Log debug messages to given file instead of stderr\n"
+    "  -f                   Run bird in foreground\n"
+    "  -g <group>           Use given group ID\n"
+    "  -h, --help           Display this information\n"
+    "  -l                   Look for a configuration file and a control socket\n"
+    "                       in the current working directory\n"
+    "  -p                   Test configuration file and exit without start\n"
+    "  -P <pid-file>        Create a PID file with given filename\n"
+    "  -R                   Apply graceful restart recovery after start\n"
+    "  -s <control-socket>  Use given filename for a control socket\n"
+    "  -u <user>            Drop privileges and use given user ID\n"
+    "  --version            Display version of BIRD\n");
+
+  exit(0);
+}
 
 static void
-usage(void)
+display_version(void)
 {
-  fprintf(stderr, "Usage: bird [-c <config-file>] [-d] [-D <debug-file>] [-s <control-socket>]\n");
-  exit(1);
+  fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
+  exit(0);
 }
 
-int parse_and_exit;
+static inline char *
+get_bird_name(char *s, char *def)
+{
+  char *t;
+  if (!s)
+    return def;
+  t = strrchr(s, '/');
+  if (!t)
+    return s;
+  if (!t[1])
+    return def;
+  return t+1;
+}
+
+static inline uid_t
+get_uid(const char *s)
+{
+  struct passwd *pw;
+  char *endptr;
+  long int rv;
+
+  if (!s)
+    return 0;
+
+  errno = 0;
+  rv = strtol(s, &endptr, 10);
+
+  if (!errno && !*endptr)
+    return rv;
+
+  pw = getpwnam(s);
+  if (!pw)
+    die("Cannot find user '%s'", s);
+
+  return pw->pw_uid;
+}
+
+static inline gid_t
+get_gid(const char *s)
+{
+  struct group *gr;
+  char *endptr;
+  long int rv;
+
+  if (!s)
+    return 0;
+
+  errno = 0;
+  rv = strtol(s, &endptr, 10);
+
+  if (!errno && !*endptr)
+    return rv;
+
+  gr = getgrnam(s);
+  if (!gr)
+    die("Cannot find group '%s'", s);
+
+  return gr->gr_gid;
+}
 
 static void
 parse_args(int argc, char **argv)
 {
+  int config_changed = 0;
+  int socket_changed = 0;
   int c;
 
+  bird_name = get_bird_name(argv[0], "bird");
   if (argc == 2)
     {
       if (!strcmp(argv[1], "--version"))
-       {
-         fprintf(stderr, "BIRD version " BIRD_VERSION "\n");
-         exit(0);
-       }
+       display_version();
       if (!strcmp(argv[1], "--help"))
-       usage();
+       display_help();
     }
   while ((c = getopt(argc, argv, opt_list)) >= 0)
     switch (c)
       {
       case 'c':
        config_name = optarg;
+       config_changed = 1;
        break;
       case 'd':
-       debug_flag |= 1;
+       log_init_debug("");
+       run_in_foreground = 1;
        break;
       case 'D':
        log_init_debug(optarg);
-       debug_flag |= 2;
        break;
       case 'p':
        parse_and_exit = 1;
        break;
       case 's':
        path_control_socket = optarg;
+       socket_changed = 1;
+       break;
+      case 'P':
+       pid_file = optarg;
+       break;
+      case 'u':
+       use_user = optarg;
+       break;
+      case 'g':
+       use_group = optarg;
+       break;
+      case 'f':
+       run_in_foreground = 1;
+       break;
+      case 'l':
+       if (!config_changed)
+         config_name = xbasename(config_name);
+       if (!socket_changed)
+         path_control_socket = xbasename(path_control_socket);
+       break;
+      case 'R':
+       graceful_restart_recovery();
+       break;
+      case 'h':
+       display_help();
        break;
       default:
-       usage();
+       fputc('\n', stderr);
+       display_usage();
+       exit(1);
       }
   if (optind < argc)
-    usage();
+   {
+     display_usage();
+     exit(1);
+   }
 }
 
 /*
@@ -430,29 +810,46 @@ main(int argc, char **argv)
 #endif
 
   parse_args(argc, argv);
-  if (debug_flag == 1)
-    log_init_debug("");
-  log_init(debug_flag, 1);
-
-  test_old_bird(path_control_socket);
+  log_switch(1, NULL, NULL);
 
-  DBG("Initializing.\n");
+  net_init();
   resource_init();
+  timer_init();
   olock_init();
   io_init();
   rt_init();
   if_init();
+//  roa_init();
+  config_init();
+
+  uid_t use_uid = get_uid(use_user);
+  gid_t use_gid = get_gid(use_group);
+
+  if (!parse_and_exit)
+  {
+    test_old_bird(path_control_socket);
+    cli_init_unix(use_uid, use_gid);
+  }
+
+  if (use_gid)
+    drop_gid(use_gid);
+
+  if (use_uid)
+    drop_uid(use_uid);
+
+  if (!parse_and_exit)
+    open_pid_file();
 
   protos_build();
   proto_build(&proto_unix_kernel);
   proto_build(&proto_unix_iface);
 
-  read_config();
+  struct config *conf = read_config();
 
   if (parse_and_exit)
     exit(0);
 
-  if (!debug_flag)
+  if (!run_in_foreground)
     {
       pid_t pid = fork();
       if (pid < 0)
@@ -467,14 +864,21 @@ main(int argc, char **argv)
       dup2(0, 2);
     }
 
+  main_thread_init();
+
+  write_pid_file();
+
   signal_init();
 
-  cli_init_unix();
+  config_commit(conf, RECONFIG_HARD, 0);
+
+  graceful_restart_init();
 
 #ifdef LOCAL_DEBUG
   async_dump_flag = 1;
 #endif
 
+  log(L_INFO "Started");
   DBG("Entering I/O loop.\n");
 
   io_loop();