From: Jan Moskyto Matejka Date: Mon, 18 Apr 2016 10:06:41 +0000 (+0200) Subject: Merged master into birdtest. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f9f4edceb893ad3b967df083a44740decb1aa6e2;p=thirdparty%2Fbird.git Merged master into birdtest. Files with hash functions inside lib/ were added in two separate commits. Using master version. Merged sysdep/unix/main.* and sysdep/unix/main_* to include changes from both branches. --- f9f4edceb893ad3b967df083a44740decb1aa6e2 diff --cc sysdep/unix/main_helper.c index 42a9e6cb8,000000000..eb32849bf mode 100644,000000..100644 --- a/sysdep/unix/main_helper.c +++ b/sysdep/unix/main_helper.c @@@ -1,751 -1,0 +1,753 @@@ +/* + * BIRD Internet Routing Daemon -- Helper for main.c + * + * (c) 1998--2000 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#undef LOCAL_DEBUG + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ] [-d] [-D ] [-p] [-s ] [-P ] [-u ] [-g ] [-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(); +}