5f2a6a9ff3
for even only medium sized route table output. Fix a strange garbled output problem in the client. The latter seems to be caused by some library doing tcflush while there is still command output pending. So the best fix here is to do fflush and then tcdrain. Note that this problem occurs only under certain load situations and is not too easy to reproduce. (by Andreas)
450 lines
7.3 KiB
C
450 lines
7.3 KiB
C
/*
|
|
* BIRD Internet Routing Daemon -- Unix Entry Point
|
|
*
|
|
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
#undef LOCAL_DEBUG
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <signal.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
|
|
static int debug_flag = 1;
|
|
#else
|
|
static 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");
|
|
}
|
|
|
|
/*
|
|
* Reading the Configuration
|
|
*/
|
|
|
|
static int conf_fd;
|
|
static char *config_name = PATH_CONFIG;
|
|
|
|
static int
|
|
cf_read(byte *dest, unsigned int len)
|
|
{
|
|
int l = read(conf_fd, dest, len);
|
|
if (l < 0)
|
|
cf_error("Read error");
|
|
return l;
|
|
}
|
|
|
|
void
|
|
sysdep_preconfig(struct config *c)
|
|
{
|
|
init_list(&c->logfiles);
|
|
}
|
|
|
|
int
|
|
sysdep_commit(struct config *new, struct config *old)
|
|
{
|
|
log_switch(debug_flag, &new->logfiles);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
unix_read_config(struct config **cp, char *name)
|
|
{
|
|
struct config *conf = config_alloc(name);
|
|
|
|
*cp = conf;
|
|
conf_fd = open(name, O_RDONLY);
|
|
if (conf_fd < 0)
|
|
return 0;
|
|
cf_read_hook = cf_read;
|
|
return config_parse(conf);
|
|
}
|
|
|
|
static void
|
|
read_config(void)
|
|
{
|
|
struct config *conf;
|
|
|
|
if (!unix_read_config(&conf, config_name))
|
|
{
|
|
if (conf->err_msg)
|
|
die("%s, line %d: %s", config_name, conf->err_lino, conf->err_msg);
|
|
else
|
|
die("Unable to open configuration file %s: %m", config_name);
|
|
}
|
|
config_commit(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", config_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);
|
|
}
|
|
|
|
void
|
|
cmd_reconfig(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", name, conf->err_lino, conf->err_msg);
|
|
else
|
|
cli_msg(8002, "%s: %m", name);
|
|
config_free(conf);
|
|
}
|
|
else
|
|
{
|
|
switch (config_commit(conf))
|
|
{
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Command-Line Interface
|
|
*/
|
|
|
|
static sock *cli_sk;
|
|
static char *path_control_socket = PATH_CONTROL_SOCKET;
|
|
|
|
int
|
|
cli_write(cli *c)
|
|
{
|
|
sock *s = c->priv;
|
|
|
|
if (c->tx_pos)
|
|
{
|
|
struct cli_out *o = c->tx_pos;
|
|
s->tbuf = o->outpos;
|
|
if (sk_send(s, o->wpos - o->outpos) > 0)
|
|
{
|
|
c->tx_pos = o->next;
|
|
ev_schedule(c->event);
|
|
}
|
|
}
|
|
return !c->tx_pos;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
cli_rx(sock *s, int size)
|
|
{
|
|
cli_kick(s->data);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
cli_tx(sock *s)
|
|
{
|
|
cli *c = s->data;
|
|
|
|
if (cli_write(c))
|
|
cli_written(c);
|
|
}
|
|
|
|
static 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);
|
|
sk_close(s);
|
|
}
|
|
|
|
static int
|
|
cli_connect(sock *s, int size)
|
|
{
|
|
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;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
cli_init_unix(void)
|
|
{
|
|
sock *s;
|
|
|
|
cli_init();
|
|
s = cli_sk = sk_new(cli_pool);
|
|
s->type = SK_UNIX_PASSIVE;
|
|
s->rx_hook = cli_connect;
|
|
s->rbsize = 1024;
|
|
if (sk_open_unix(s, path_control_socket) < 0)
|
|
die("Unable to create control socket %s", path_control_socket);
|
|
}
|
|
|
|
/*
|
|
* Shutdown
|
|
*/
|
|
|
|
void
|
|
async_shutdown(void)
|
|
{
|
|
DBG("Shutting down...\n");
|
|
order_shutdown();
|
|
}
|
|
|
|
void
|
|
sysdep_shutdown_done(void)
|
|
{
|
|
unlink(PATH_CONTROL_SOCKET);
|
|
die("System shutdown completed");
|
|
}
|
|
|
|
/*
|
|
* Signals
|
|
*/
|
|
|
|
static void
|
|
handle_sighup(int sig)
|
|
{
|
|
DBG("Caught SIGHUP...\n");
|
|
async_config_flag = 1;
|
|
}
|
|
|
|
static void
|
|
handle_sigusr(int sig)
|
|
{
|
|
DBG("Caught SIGUSR...\n");
|
|
async_dump_flag = 1;
|
|
}
|
|
|
|
static void
|
|
handle_sigterm(int sig)
|
|
{
|
|
DBG("Caught SIGTERM...\n");
|
|
async_shutdown_flag = 1;
|
|
}
|
|
|
|
static 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);
|
|
signal(SIGPIPE, SIG_IGN);
|
|
}
|
|
|
|
/*
|
|
* Parsing of command-line arguments
|
|
*/
|
|
|
|
static char *opt_list = "c:dD:s:";
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: bird [-c <config-file>] [-d] [-D <debug-file>] [-s <control-socket>]\n");
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
parse_args(int argc, char **argv)
|
|
{
|
|
int c;
|
|
|
|
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 's':
|
|
path_control_socket = optarg;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
if (optind < argc)
|
|
usage();
|
|
}
|
|
|
|
/*
|
|
* Hic Est main()
|
|
*/
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
#ifdef HAVE_LIBDMALLOC
|
|
if (!getenv("DMALLOC_OPTIONS"))
|
|
dmalloc_debug(0x2f03d00);
|
|
#endif
|
|
|
|
parse_args(argc, argv);
|
|
if (debug_flag == 1)
|
|
log_init_debug("");
|
|
log_init(debug_flag, 1);
|
|
|
|
DBG("Initializing.\n");
|
|
resource_init();
|
|
olock_init();
|
|
io_init();
|
|
rt_init();
|
|
if_init();
|
|
|
|
protos_build();
|
|
proto_build(&proto_unix_kernel);
|
|
proto_build(&proto_unix_iface);
|
|
|
|
read_config();
|
|
|
|
if (!debug_flag)
|
|
{
|
|
pid_t pid = fork();
|
|
if (pid < 0)
|
|
die("fork: %m");
|
|
if (pid)
|
|
return 0;
|
|
setsid();
|
|
}
|
|
|
|
signal_init();
|
|
|
|
cli_init_unix();
|
|
|
|
#ifdef LOCAL_DEBUG
|
|
async_dump_flag = 1;
|
|
#endif
|
|
|
|
DBG("Entering I/O loop.\n");
|
|
|
|
io_loop();
|
|
bug("I/O loop died");
|
|
}
|