0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-10-18 18:08:45 +00:00
bird/sysdep/unix/main_helper.c
Pavel Tvrdík 856250c27f Birdtest: Purge 'int main()' from lib/birdlib.a #2
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...
2015-08-05 12:36:40 +02:00

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();
}