X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=sysdep%2Funix%2Fmain.c;h=921115b1a9cff12568e6ebe7a3bd0f7548fb27ff;hb=d7e8f00e7e35daff9bcf96aa455ebc6f932d0882;hp=fd921acebf3dbb54d87516abfd90620459f01a15;hpb=acc93efd4c754cc995ee8edf52ce0bc45511062e;p=thirdparty%2Fbird.git diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index fd921aceb..921115b1a 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -8,17 +8,26 @@ #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/timer.h" #include "lib/string.h" #include "nest/route.h" #include "nest/protocol.h" @@ -35,12 +44,6 @@ * 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,6 +61,29 @@ 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 */ @@ -67,10 +93,11 @@ async_dump(void) static inline void add_num_const(char *name, int val) { - struct symbol *s = cf_find_symbol(name); - s->class = SYM_NUMBER; - s->def = NULL; - s->aux = 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 @@ -99,7 +126,7 @@ read_iproute_table(char *file, char *prefix, int max) 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 && @@ -122,13 +149,12 @@ read_iproute_table(char *file, char *prefix, int max) #endif // PATH_IPROUTE_DIR -static int conf_fd; -static char *config_name = PATH_CONFIG; +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; @@ -139,8 +165,11 @@ 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_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); @@ -150,7 +179,7 @@ sysdep_preconfig(struct config *c) int sysdep_commit(struct config *new, struct config *old UNUSED) { - log_switch(debug_flag, &new->logfiles, new->syslog_name); + log_switch(0, &new->logfiles, new->syslog_name); return 0; } @@ -161,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; @@ -178,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 @@ -194,53 +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 (cli_access_restricted()) - return; - 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 */ @@ -318,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; @@ -338,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; @@ -349,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); @@ -356,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; @@ -365,9 +464,74 @@ cli_init_unix(void) s->type = SK_UNIX_PASSIVE; s->rx_hook = cli_connect; s->rbsize = 1024; - sk_open_unix(s, path_control_socket); + 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 + */ + +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 */ @@ -392,6 +556,7 @@ async_shutdown(void) void sysdep_shutdown_done(void) { + unlink_pid_file(); unlink(path_control_socket); log_msg(L_FATAL "Shutdown completed"); exit(0); @@ -401,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) { @@ -422,6 +591,8 @@ handle_sigterm(int sig UNUSED) async_shutdown_flag = 1; } +void watchdog_sigalrm(int sig UNUSED); + static void signal_init(void) { @@ -437,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); } @@ -444,15 +618,51 @@ 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 -usage(void) +display_usage(void) { - fprintf(stderr, "Usage: %s [-c ] [-d] [-D ] [-p] [-s ]\n", bird_name); - exit(1); + fprintf(stderr, "Usage: %s [--version] [--help] [-c ] [OPTIONS]\n", bird_name); +} + +static void +display_help(void) +{ + display_usage(); + + fprintf(stderr, + "\n" + "Options: \n" + " -c Use given configuration file instead of\n" + " " PATH_CONFIG_FILE "\n" + " -d Enable debug messages and run bird in foreground\n" + " -D Log debug messages to given file instead of stderr\n" + " -f Run bird in foreground\n" + " -g 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 Create a PID file with given filename\n" + " -R Apply graceful restart recovery after start\n" + " -s Use given filename for a control socket\n" + " -u Drop privileges and use given user ID\n" + " --version Display version of BIRD\n"); + + exit(0); +} + +static void +display_version(void) +{ + fprintf(stderr, "BIRD version " BIRD_VERSION "\n"); + exit(0); } static inline char * @@ -469,46 +679,122 @@ get_bird_name(char *s, char *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); + } } /* @@ -524,33 +810,46 @@ main(int argc, char **argv) #endif parse_args(argc, argv); - if (debug_flag == 1) - log_init_debug(""); - log_switch(debug_flag, NULL, NULL); + log_switch(1, NULL, NULL); - if (!parse_and_exit) - test_old_bird(path_control_socket); - - 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) - cli_init_unix(); + 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) @@ -565,8 +864,16 @@ main(int argc, char **argv) dup2(0, 2); } + main_thread_init(); + + write_pid_file(); + signal_init(); + config_commit(conf, RECONFIG_HARD, 0); + + graceful_restart_init(); + #ifdef LOCAL_DEBUG async_dump_flag = 1; #endif