--- /dev/null
+/*
+ * BIRD Internet Routing Daemon -- Helper for main.c
+ *
+ * (c) 1998--2000 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#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/string.h"
+#include "nest/route.h"
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "nest/cli.h"
+#include "nest/locks.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+
+#include "unix.h"
+#include "krt.h"
+
+#include "lib/main_helper.h"
+
+/*
+ * Debugging
+ */
+
+#ifdef DEBUGGING
+int debug_flag = 1;
+#else
+int debug_flag = 0;
+#endif
+
+void
+async_dump(void)
+{
+ debug("INTERNAL STATE DUMP\n\n");
+
+ rdump(&root_pool);
+ sk_dump_all();
+ tm_dump_all();
+ if_dump_all();
+ neigh_dump_all();
+ rta_dump_all();
+ rt_dump_all();
+ protos_dump_all();
+
+ debug("\n");
+}
+
+/*
+ * Dropping privileges
+ */
+
+#ifdef CONFIG_RESTRICTED_PRIVILEGES
+#include "lib/syspriv.h"
+#else
+
+void
+drop_uid(uid_t uid)
+{
+ die("Cannot change user on this platform");
+}
+
+#endif
+
+void
+drop_gid(gid_t gid)
+{
+ if (setgid(gid) < 0)
+ die("setgid: %m");
+}
+
+/*
+ * Reading the Configuration
+ */
+
+#ifdef PATH_IPROUTE_DIR
+
+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 */
+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
+
+
+char *config_name = PATH_CONFIG_FILE;
+
+int
+cf_read(byte *dest, uint len, int fd)
+{
+ int l = read(fd, dest, len);
+ if (l < 0)
+ cf_error("Read error");
+ return l;
+}
+
+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, new->syslog_name);
+ return 0;
+}
+
+int
+unix_read_config(struct config **cp, char *name)
+{
+ struct config *conf = config_alloc(name);
+ int ret;
+
+ *cp = conf;
+ 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->file_fd);
+ return ret;
+}
+
+struct config *
+read_config(void)
+{
+ struct config *conf;
+
+ if (!unix_read_config(&conf, config_name))
+ {
+ if (conf->err_msg)
+ die("%s, line %d: %s", conf->err_file_name, conf->err_lino, conf->err_msg);
+ else
+ die("Unable to open configuration file %s: %m", config_name);
+ }
+
+ return conf;
+}
+
+void
+async_config(void)
+{
+ struct config *conf;
+
+ log(L_INFO "Reconfiguration requested by SIGHUP");
+ if (!unix_read_config(&conf, config_name))
+ {
+ if (conf->err_msg)
+ log(L_ERR "%s, line %d: %s", conf->err_file_name, conf->err_lino, 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, 0);
+}
+
+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", conf->err_file_name, conf->err_lino, conf->err_msg);
+ else
+ cli_msg(8002, "%s: %m", name);
+ config_free(conf);
+ conf = NULL;
+ }
+
+ 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);
+}
+
+void
+cmd_reconfig_msg(int r)
+{
+ switch (r)
+ {
+ 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, int 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
+ */
+
+sock *cli_sk;
+char *path_control_socket = PATH_CONTROL_SOCKET;
+
+
+void
+cli_write(cli *c)
+{
+ sock *s = c->priv;
+
+ while (c->tx_pos)
+ {
+ struct cli_out *o = c->tx_pos;
+
+ int len = o->wpos - o->outpos;
+ s->tbuf = o->outpos;
+ o->outpos = o->wpos;
+
+ if (sk_send(s, len) <= 0)
+ return;
+
+ c->tx_pos = o->next;
+ }
+
+ /* Everything is written */
+ s->tbuf = NULL;
+ cli_written(c);
+}
+
+void
+cli_write_trigger(cli *c)
+{
+ sock *s = c->priv;
+
+ if (s->tbuf == NULL)
+ cli_write(c);
+}
+
+void
+cli_tx(sock *s)
+{
+ cli_write(s->data);
+}
+
+int
+cli_get_command(cli *c)
+{
+ sock *s = c->priv;
+ byte *t = c->rx_aux ? : s->rbuf;
+ byte *tend = s->rpos;
+ byte *d = c->rx_pos;
+ byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
+
+ while (t < tend)
+ {
+ if (*t == '\r')
+ t++;
+ else if (*t == '\n')
+ {
+ t++;
+ c->rx_pos = c->rx_buf;
+ c->rx_aux = t;
+ *d = 0;
+ return (d < dend) ? 1 : -1;
+ }
+ else if (d < dend)
+ *d++ = *t++;
+ }
+ c->rx_aux = s->rpos = s->rbuf;
+ c->rx_pos = d;
+ return 0;
+}
+
+int
+cli_rx(sock *s, int size UNUSED)
+{
+ cli_kick(s->data);
+ return 0;
+}
+
+void
+cli_err(sock *s, int err)
+{
+ if (config->cli_debug)
+ {
+ if (err)
+ log(L_INFO "CLI connection dropped: %s", strerror(err));
+ else
+ log(L_INFO "CLI connection closed");
+ }
+ cli_free(s->data);
+}
+
+int
+cli_connect(sock *s, int size UNUSED)
+{
+ cli *c;
+
+ if (config->cli_debug)
+ log(L_INFO "CLI connect");
+ s->rx_hook = cli_rx;
+ s->tx_hook = cli_tx;
+ 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);
+ return 1;
+}
+
+void
+cli_init_unix(uid_t use_uid, gid_t use_gid)
+{
+ sock *s;
+
+ cli_init();
+ s = cli_sk = sk_new(cli_pool);
+ 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("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
+ */
+
+char *pid_file;
+int pid_fd;
+
+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);
+}
+
+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);
+}
+
+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)
+{
+ DBG("Shutting down...\n");
+ order_shutdown();
+}
+
+void
+sysdep_shutdown_done(void)
+{
+ unlink_pid_file();
+ unlink(path_control_socket);
+ log_msg(L_FATAL "Shutdown completed");
+ exit(0);
+}
+
+/*
+ * Signals
+ */
+
+void
+handle_sighup(int sig UNUSED)
+{
+ DBG("Caught SIGHUP...\n");
+ async_config_flag = 1;
+}
+
+void
+handle_sigusr(int sig UNUSED)
+{
+ DBG("Caught SIGUSR...\n");
+ async_dump_flag = 1;
+}
+
+void
+handle_sigterm(int sig UNUSED)
+{
+ DBG("Caught SIGTERM...\n");
+ async_shutdown_flag = 1;
+}
+
+void watchdog_sigalrm(int sig UNUSED);
+
+void
+signal_init(void)
+{
+ struct sigaction sa;
+
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = handle_sigusr;
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &sa, NULL);
+ sa.sa_handler = handle_sighup;
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGHUP, &sa, NULL);
+ 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);
+}
+
+/*
+ * Parsing of command-line arguments
+ */
+
+char *opt_list = "c:dD:ps:P:u:g:fR";
+int parse_and_exit;
+char *bird_name;
+char *use_user;
+char *use_group;
+int run_in_foreground = 0;
+
+void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [-c <config-file>] [-d] [-D <debug-file>] [-p] [-s <control-socket>] [-P <pid-file>] [-u <user>] [-g <group>] [-f] [-R]\n", bird_name);
+ exit(1);
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+void
+parse_args(int argc, char **argv)
+{
+ 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);
+ }
+ if (!strcmp(argv[1], "--help"))
+ usage();
+ }
+ while ((c = getopt(argc, argv, opt_list)) >= 0)
+ switch (c)
+ {
+ case 'c':
+ config_name = optarg;
+ break;
+ case 'd':
+ debug_flag |= 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;
+ 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 'R':
+ graceful_restart_recovery();
+ break;
+ default:
+ usage();
+ }
+ if (optind < argc)
+ usage();
+}