mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-10 02:51:54 +00:00
856250c27f
All origin static functions from sysdep/unix/main.c were rewrited to non-static. All origin static global variables from sysdep/unix/main.c was rewrited to extern. It is possible create executables bird, birdc and birdcl as well as run unit tests without mockuping After remake new BIRD's build system will be this commit reverting...
750 lines
13 KiB
C
750 lines
13 KiB
C
/*
|
|
* 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 1
|
|
#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"
|
|
|
|
/*
|
|
* 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_find_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 */
|
|
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;
|
|
|
|
/* 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();
|
|
}
|