bird/sysdep/unix/main.c
Martin Mares e1ddd99377 Changed handling of incoming connections, so that we can send data
from the send hook without worrying about existence of socket buffers.

Also, don't forget to copy peer addresses.
2000-04-26 13:26:11 +00:00

420 lines
6.9 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.
*/
#define LOCAL_DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/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
*/
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(&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;
c->tx_pos = o->next;
s->tbuf = o->outpos;
return sk_send(s, o->wpos - o->outpos);
}
return 1;
}
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 (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;
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 int debug_flag = 1; /* FIXME: Turn off for production use */
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;
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);
log(L_INFO "Launching BIRD " BIRD_VERSION "...");
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();
signal_init();
cli_init_unix();
ev_run_list(&global_event_list);
async_dump();
DBG("Entering I/O loop.\n");
io_loop();
bug("I/O loop died");
}