home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
acld-1.11.tar.gz
/
acld-1.11.tar
/
acld-1.11
/
acld.c
< prev
next >
Wrap
C/C++ Source or Header
|
2012-02-07
|
23KB
|
1,056 lines
/*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012\n\
The Regents of the University of California. All rights reserved.\n";
static const char rcsid[] =
"@(#) $Id: acld.c 806 2012-02-08 03:40:06Z leres $ (LBL)";
#endif
/*
* acld - manage ACLs on a router
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "gnuc.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
#endif
#include "acld.h"
#include "cf.h"
#ifdef HAVE_CFORCE
#include "cforcep.h"
#endif
#include "child.h"
#include "server.h"
#include "setsignal.h"
#define MAX(x, y) ((x) > (y) ? (x) : (y))
/* Globals */
int debug; /* turn on debugging and don't fork */
int verbose; /* not really used for anything! */
int reload; /* kill child and reload config */
int foreground;
const char *prog;
const char *configfn = "/usr/local/etc/acld.conf";
const char *logfile;
FILE *lf;
struct s2v astate2str[] = {
{ "connected", ASTATE_CONNECTED },
{ "loggedin", ASTATE_LOGGEDIN },
{ "notconnected", ASTATE_NOTCONNECTED },
{ "readacl", ASTATE_READACL },
{ "readerror", ASTATE_READERROR },
{ "readresponse", ASTATE_READRESPONSE },
{ "readroute", ASTATE_READROUTE },
{ "sentattr", ASTATE_SENTATTR },
{ "sentlistacl", ASTATE_SENTLISTACL },
{ "sentlistroute", ASTATE_SENTLISTROUTE },
{ "sentlogin", ASTATE_SENTLOGIN },
{ NULL, 0 }
};
struct s2v rstate2str[] = {
{ "child", RSTATE_CHILD },
{ "done", RSTATE_DONE },
{ "pending", RSTATE_PENDING },
{ "readcomment", RSTATE_READCOMMENT },
{ "readresponse", RSTATE_READRESPONSE },
{ NULL, 0 }
};
/* Locals */
static struct state state;
static int backlog = 16; /* listen() backlog */
static int sawterm;
static const char *pidfile = "/var/run/acld/acld.pid";
/* Externs */
extern int optind;
extern int opterr;
extern char *optarg;
/* Forwards */
int expect(struct state *);
int main(int, char **);
void handleclientreqs(struct state *);
void initiateclient(struct state *, int, enum clienttype, int);
int opensocket(struct state *, struct addr *, u_short);
int reapdeadclients(struct state *);
void settimeout(struct state *);
RETSIGTYPE sigchild(int);
RETSIGTYPE sighup(int);
#ifdef notdef
RETSIGTYPE sigpipe(int);
#endif
RETSIGTYPE sigterm(int);
int update_fd_set(struct state *, fd_set *, fd_set *);
__dead void usage(void) __attribute__((noreturn));
int
main(int argc, char **argv)
{
int n, nfds, op, done;
#ifdef HAVE_BRO_INIT
int usessl;
#endif
char *cp;
time_t t, idlet;
FILE *fp;
ssize_t cc;
struct req *rp;
struct state *sp;
struct cf *cf;
struct client *cl, *nextcl;
fd_set readfds, writefds;
struct stat sbuf;
if (argv[0] == NULL)
prog = "acld";
else if ((cp = strrchr(argv[0], '/')) != NULL)
prog = cp + 1;
else
prog = argv[0];
openlog(prog, 0, LOG_DAEMON);
opterr = 0;
while ((op = getopt(argc, argv, "c:dDfo:P:v")) != EOF)
switch (op) {
case 'c':
configfn = optarg;
break;
case 'd':
++debug;
break;
#ifdef HAVE_BROCCOLI
case 'D':
/* Undocumented */
++bro_debug_messages;
break;
#endif
case 'f':
++foreground;
break;
case 'o':
logfile = optarg;
break;
case 'P':
pidfile = optarg;
break;
case 'v':
++verbose;
break;
default:
usage();
/* NOTREACHED */
}
if (argc != optind)
usage();
if (logfile != NULL)
checklf();
else
lf = stderr;
if (!foreground && daemon(0, 1) < 0) {
lg(LOG_ERR, "daemon: %s", strerror(errno));
exit(EX_OSERR);
}
/* Update pidfile */
fp = fopen(pidfile, "w");
if (fp == NULL)
lg(LOG_ERR, "daemon: %s: %s", pidfile, strerror(errno));
else {
fprintf(fp, "%d\n", (int)getpid());
fclose(fp);
}
lg(LOG_INFO, "version %s starting", version);
(void)setsignal(SIGTERM, sigterm);
(void)setsignal(SIGINT, sigterm);
(void)setsignal(SIGHUP, sighup);
(void)setsignal(SIGCHLD, sigchild);
(void)setsignal(SIGPIPE, SIG_IGN);
sp = &state;
sp->state = ASTATE_NOTCONNECTED;
sp->rfd = -1;
sp->wfd = -1;
sp->s = -1;
sp->ro = -1;
sp->web = -1;
#ifdef HAVE_BROCCOLI
sp->b = -1;
// sp->b_ro = -1;
// sp->b_web = -1;
#endif
reload = 1;
idlet = time(NULL);
sp->f_ayt = serverayt;
sp->f_compact = servercompact;
sp->f_droprestore = childdroprestore;
sp->f_kill = childkill;
sp->f_listacl = childlistacl;
sp->f_listroute = childlistroute;
sp->f_login = childlogin;
sp->f_nullzero = childnullzero;
sp->f_send = childsend;
sp->f_sendattr = childsendattr;
sp->f_sync = serversync;
/* Initial config file parsing */
sp->cf = parsecf(configfn);
#ifdef HAVE_BRO_INIT
/* Broccoli */
if (sp->cf->c_portbro > 0) {
if (!bro_init(NULL)) {
lg(LOG_ERR, "Failed to initialize broccoli; exiting");
exit(EX_CONFIG);
}
usessl = 0;
if (!bro_conf_get_int("/broccoli/use_ssl", &usessl))
usessl = 0;
lg(LOG_DEBUG, "broinit: %susing SSL", usessl ? "" : "not ");
}
#endif
#ifdef HAVE_CFORCE
/* cForce */
if (sp->cf->c_cforceaddr != NULL)
cforceinit(sp);
#endif
/* nullzero routes */
routeinit(sp);
for (;;) {
if (sawterm) {
lg(LOG_NOTICE, "exiting");
/* Explicitly terminate the child */
if (sp->pid > 0 &&
kill(sp->pid, SIGTERM) < 0 && errno != ESRCH)
lg(LOG_ERR, "kill %d: %s",
(int)sp->pid, strerror(errno));
/* Simulate /bin/sh exit status */
exit(128 + SIGTERM);
}
if (reload) {
/* Terminate the child */
if (sp->wfd >= 0 && sp->state == ASTATE_LOGGEDIN)
(sp->f_kill)(sp);
/* Reload config file if it's changed */
if (sp->cf != NULL) {
/* Get the config file mod time */
if (stat(configfn, &sbuf) < 0)
lg(LOG_ERR, "stat %s: %s",
configfn, strerror(errno));
else {
t = (sbuf.st_mtime > sbuf.st_ctime) ?
sbuf.st_mtime : sbuf.st_ctime;
if (t > sp->cf->c_time) {
lg(LOG_INFO, "reloading %s",
configfn);
freecf(sp->cf);
routefree(sp);
sp->cf = NULL;
}
}
}
reload = 0;
timerset(&sp->t_login, 1);
}
/* Read the config file */
if (sp->cf == NULL && (sp->cf = parsecf(configfn)) == NULL)
exit(EX_CONFIG);
/* Create new expect process */
if (sp->cf->c_script != NULL && sp->wfd < 0 && expect(sp) < 0) {
lg(LOG_ERR, "expect child: %s",
strerror(errno));
exit(EX_OSERR);
}
/* Internal processing */
if (
#ifdef HAVE_CFORCE
sp->cf->c_cforceaddr != NULL ||
#endif
sp->wfd >= 0) {
switch (sp->state) {
case ASTATE_CONNECTED:
#ifdef HAVE_CFORCE
case ASTATE_NOTCONNECTED:
#endif
if (timerdue(&sp->t_login))
(sp->f_login)(sp);
break;
case ASTATE_LOGGEDIN:
if (!sp->sentattrs)
(sp->f_sendattr)(sp);
else if (!sp->listedallacls)
(sp->f_listacl)(sp);
else if (!sp->listedroutes)
(sp->f_listroute)(sp);
break;
default:
break;
}
}
/* Open the listen socket */
cf = sp->cf;
if (sp->s < 0)
sp->s = opensocket(sp, &cf->c_bindaddr, cf->c_port);
/* Optional read/only request socket */
if (sp->ro < 0 && cf->c_portro > 0)
sp->ro = opensocket(sp, &cf->c_bindaddr, cf->c_portro);
/* Optional web registration request socket */
if (sp->web < 0 && cf->c_portweb > 0)
sp->web = opensocket(sp, &cf->c_bindaddr,
cf->c_portweb);
#ifdef HAVE_BROCCOLI
/* Open the broccoli socket */
if (sp->b < 0 && cf->c_portbro > 0)
sp->b = opensocket(sp, &cf->c_broaddr, cf->c_portbro);
#endif
/* Update fd_sets */
nfds = update_fd_set(sp, &readfds, &writefds);
/* Wait for stuff to do (or timeout) */
settimeout(sp);
n = select(nfds, &readfds, &writefds, NULL, &sp->timeout);
if (n < 0) {
/* Don't choke if we get ptraced */
if (errno == EINTR)
continue;
lg(LOG_ERR, "select: %s", strerror(errno));
/*
* There's a race between the client and
* our call to select(). It's possible for
* the client to close his connection just
* before we call select(). When this happens
* we get EBADF. If we can find the client
* and clean him up, we can continue on.
* Otherwise, exit.
*/
if (errno == EBADF && reapdeadclients(sp) > 0)
continue;
exit(EX_OSERR);
}
/* Time for maintenance */
// XXX this is broken
// t = time(NULL);
if (n == 0) {
/* Did the child fall asleep? */
if (((cf->c_expect != NULL && sp->wfd < 0) ||
sp->state != ASTATE_LOGGEDIN) &&
timerdue(&sp->t_ayt)) {
(sp->f_kill)(sp);
continue;
}
/* Only do maintenance if there's nothing else to do */
if (sp->req == NULL && sp->reqclient == NULL) {
idlet = t;
if (timerdue(&sp->t_compact))
(sp->f_compact)(sp);
else if (timerdue(&sp->t_sync))
(sp->f_sync)(sp);
else if (timerdue(&sp->t_ayt))
(sp->f_ayt)(sp);
}
}
#ifdef notdef
/* Make sure we're occasionally handling idle tasks */
if ((t - idlet) > SELECT_SECS) {
lg(LOG_ERR, "idle failure; aborting");
abort();
}
#endif
/* Did any clients say anything? */
/* XXX limit about of client data to throttle them? */
for (cl = sp->clients; cl != NULL; cl = nextcl) {
/* Ok to free clients inside this loop */
nextcl = cl->next;
if (cl->c >= 0 && FD_ISSET(cl->c, &readfds)) {
#ifdef HAVE_BROCCOLI
if (cl->broccoli && cl->bc != NULL) {
if (!broinput(cl)) {
lg(LOG_ERR,
"client #%d broccoli: exit",
cl->n);
freeclient(sp, cl);
}
continue;
}
#endif
cc = ioread(cl->c, &cl->rbuf);
if (cc < 0) {
lg(LOG_ERR, "client #%d read: %s",
cl->n, strerror(errno));
freeclient(sp, cl);
continue;
}
if (cc == 0) {
lg(LOG_INFO,
"client #%d disconnected", cl->n);
freeclient(sp, cl);
continue;
}
}
}
/* Check for new clients */
if (sp->s >= 0 && FD_ISSET(sp->s, &readfds))
initiateclient(sp, sp->s, CTYPE_PRIV, 0);
if (sp->ro >= 0 && FD_ISSET(sp->ro, &readfds))
initiateclient(sp, sp->ro, CTYPE_RO, 0);
if (sp->web >= 0 && FD_ISSET(sp->web, &readfds))
initiateclient(sp, sp->web, CTYPE_WEB, 0);
#ifdef HAVE_BROCCOLI
if (sp->b >= 0 && FD_ISSET(sp->b, &readfds))
initiateclient(sp, sp->b, CTYPE_PRIV, 1);
#endif
/* Did the child say something? */
if (sp->rfd >= 0 && FD_ISSET(sp->rfd, &readfds)) {
cc = ioread(sp->rfd, &sp->rbuf);
if (cc < 0) {
lg(LOG_ERR, "child read: %s", strerror(errno));
continue;
}
if (cc == 0) {
lg(LOG_DEBUG, "child disconnect");
sp->state = ASTATE_NOTCONNECTED;
sp->cmd = NULL;
(sp->f_kill)(sp);
continue;
}
}
/* Process child input */
done = 0;
while (sp->rbuf.len > 0 && !done) {
cl = sp->reqclient;
done = childinput(sp, cl);
/* Remove server request from list if completed */
if (sp->req != NULL && sp->req->state == RSTATE_DONE) {
/* Remove from request list */
rp = sp->req;
sp->req = rp->next;
rp->next = NULL;
freereq(rp);
continue;
}
/* Remove client request from list if completed */
if (cl != NULL && cl->req != NULL &&
cl->req->state == RSTATE_DONE) {
/* Remove from request list */
rp = cl->req;
cl->req = rp->next;
rp->next = NULL;
freereq(rp);
sp->reqclient = NULL;
continue;
}
}
/*
* Process input from each client input, possibly
* appending a request to the end of the client's
* queue.
*/
for (cl = sp->clients; cl != NULL; cl = cl->next)
while (iohaveline(&cl->rbuf))
clientinput(sp, cl);
/* Reset keepalive if necessary */
if (sp->state == ASTATE_LOGGEDIN &&
cf->c_ayt_secs > 0 && timercheck(&sp->t_ayt) < 0)
timerset(&sp->t_ayt, cf->c_ayt_secs);
/* Handle server requests */
while (sp->state == ASTATE_LOGGEDIN &&
(rp = sp->req) != NULL && rp->state == RSTATE_PENDING) {
serverprocess(sp, rp);
if (rp->state == RSTATE_DONE) {
/* Remove from request list */
rp = sp->req;
sp->req = rp->next;
rp->next = NULL;
freereq(rp);
}
}
/* Handle client requests */
if (sp->req == NULL && sp->reqclient == NULL)
handleclientreqs(sp);
/* Drain client output */
for (cl = sp->clients; cl != NULL; cl = nextcl) {
/* Ok to free clients inside this loop */
nextcl = cl->next;
if (cl->c >= 0 && FD_ISSET(cl->c, &writefds) &&
cl->wbuf.len > 0) {
#ifdef HAVE_BROCCOLI
if (cl->broccoli) {
lg(LOG_ERR, "broclient output");
abort();
}
#endif
if (iowrite(cl->c, &cl->wbuf) < 0) {
lg(LOG_ERR, "client #%d write: %s",
cl->n, strerror(errno));
freeclient(sp, cl);
continue;
}
/* If this client is done free him */
if (cl->close && cl->wbuf.len <= 0) {
freeclient(sp, cl);
continue;
}
}
}
}
}
struct state *
getstate(void)
{
return (&state);
}
/*
* Round robin to give each client a fair chance. Once a request
* blocks on the client, move that client to the end of the client
* list.
*/
void
handleclientreqs(struct state *sp)
{
struct client *cl, *cl2, *lastcl;
struct req *rp;
lastcl = NULL;
for (cl = sp->clients; cl != NULL; cl = cl->next) {
while (cl->req != NULL && cl->req->state == RSTATE_PENDING) {
sp->reqclient = cl;
clientprocess(sp, cl, cl->req);
/* If we blocked, move client to the end and bail */
if (cl->req->state != RSTATE_DONE) {
if (cl->next != NULL) {
if (lastcl != NULL)
lastcl->next = cl->next;
else
sp->clients = cl->next;
cl2 = cl->next;
while (cl2->next != NULL)
cl2 = cl2->next;
cl2->next = cl;
cl->next = NULL;
}
return;
}
/* Remove this request from the list */
rp = cl->req;
cl->req = rp->next;
rp->next = NULL;
freereq(rp);
sp->reqclient = NULL;
/* Look at next request for this client */
}
lastcl = cl;
}
}
struct req hello = {
NULL,
REQ_UNKNOWN,
RSTATE_PENDING,
RFLAG_CONTINUE, /* request flags */
"acld", /* client command name */
NULL, /* acllist pointer */
NULL, /* ACL name */
0, /* cookie */
{ 0, 0 }, /* arrival timestamp */
{ 0, 0 }, /* completion timestamp */
{ }, /* ACL */
{ }, /* route */
{ 0 }, /* whitelist */
0, /* argument count */
NULL, /* argument vector */
NULL, /* comment */
{ "ready for action\r\n", 0, sizeof(hello.payload.buf) - 1 },
};
/* XXX limit number of clients? */
void
initiateclient(struct state *sp, int s, enum clienttype ctype, int broccoli)
{
int c;
struct client *cl;
socklen_t salen;
u_int16_t port;
struct sockaddr sa;
struct addr addr;
memset(&sa, 0, sizeof(sa));
salen = sizeof(sa);
c = accept(s, &sa, &salen);
if (c < 0) {
lg(LOG_ERR, "accept: %s", strerror(errno));
if (errno == EBADF)
exit(EX_OSERR);
return;
}
sa2addr(&sa, &addr, &port);
nbio(c);
cl = newclient(sp, c);
cl->type = ctype;
lg(LOG_DEBUG, "client #%d connect from %s.%hu (%s)",
cl->n, addr2str(&addr), port, val2str(ctype2str, cl->type));
#ifdef HAVE_BROCCOLI
if (broccoli) {
++cl->broccoli;
if (!broinit(sp, cl))
freeclient(sp, cl);
return;
}
#endif
/* Update completion timestamp */
getts(&hello.cts);
clientsend(cl, &hello);
}
/* Create the expect process */
int
expect(struct state *sp)
{
pid_t pid;
int xerrno;
int wfds[2], rfds[2];
char *argv[3], *envp[1];
if (access(sp->cf->c_script, R_OK) < 0) {
lg(LOG_ERR, "expect: %s: %s",
sp->cf->c_script, strerror(errno));
exit(EX_OSFILE);
}
if (access(sp->cf->c_expect, X_OK) < 0) {
lg(LOG_ERR, "expect: %s: %s",
sp->cf->c_expect, strerror(errno));
exit(EX_OSFILE);
}
if (pipe(rfds) < 0)
return (-1);
if (pipe(wfds) < 0) {
xerrno = errno;
(void)close(rfds[0]);
(void)close(rfds[1]);
errno = xerrno;
return (-1);
}
pid = fork();
if (pid < 0) {
xerrno = errno;
(void)close(rfds[0]);
(void)close(rfds[1]);
(void)close(wfds[0]);
(void)close(wfds[1]);
errno = xerrno;
return (-1);
}
if (pid == 0) {
/* Child */
/* XXX close all descriptors?? */
/* Setup stdin, stdout and stderr */
if (rfds[0] != STDIN_FILENO) {
(void)dup2(rfds[0], STDIN_FILENO);
(void)close(rfds[0]);
}
(void)close(rfds[1]);
if (wfds[1] != STDOUT_FILENO) {
(void)dup2(wfds[1], STDOUT_FILENO);
(void)close(wfds[1]);
}
(void)dup2(STDOUT_FILENO, STDERR_FILENO);
(void)close(wfds[0]);
argv[0] = sp->cf->c_expect;
argv[1] = sp->cf->c_script;
argv[2] = NULL;
envp[0] = NULL;
(void)execve(sp->cf->c_expect, argv, envp);
lg(LOG_ERR, "execve: %s", strerror(errno));
exit(EX_OSERR);
}
/* Parent */
(void)close(rfds[0]);
(void)close(wfds[1]);
/* The child's write is our read, etc. */
sp->rfd = wfds[0];
sp->wfd = rfds[1];
sp->pid = pid;
lg(LOG_DEBUG, "expect: spawed child %d", (int)pid);
return (0);
}
/* Append the new request to end of list */
void
appendreq(struct req **reqp, struct req *rp)
{
struct req *rp2;
if (*reqp == NULL)
*reqp = rp;
else {
rp2 = *reqp;
while (rp2->next != NULL)
rp2 = rp2->next;
rp2->next = rp;
}
}
void
freereq(struct req *rp)
{
int i;
struct state *sp;
struct acllist *ap;
if (rp == NULL)
return;
if (rp->next != NULL) {
lg(LOG_ERR, "freereq: rp->next not NULL");
exit(EX_SOFTWARE);
}
if (rp->aclname != NULL)
free(rp->aclname);
if (rp->comment != NULL)
free(rp->comment);
if (rp->payload.size > 0 )
iofree(&rp->payload);
if (rp->av != NULL)
freeargv(rp->av);
if (rp->acl.port1 != NULL)
free(rp->acl.port1);
if (rp->acl.port2 != NULL)
free(rp->acl.port2);
if (rp->acl.raw != NULL)
free(rp->acl.raw);
if (rp->nullzero.raw != NULL)
free(rp->nullzero.raw);
free(rp);
/* Clean up compaction state */
sp = &state; /* XXX */
for (i = 0, ap = sp->cf->c_acllist; i < sp->cf->c_acllistlen; ++i, ++ap)
if (ap->compactreq == rp) {
ap->compactclient = NULL;
ap->compactreq = NULL;
break;
}
}
void
nbio(int s)
{
int flags;
if ((flags = fcntl(s, F_GETFL, 0)) < 0) {
lg(LOG_ERR, "nbio: F_GETFL: %s", strerror(errno));
return;
}
flags |= O_NONBLOCK;
if ((flags = fcntl(s, F_SETFL, flags)) < 0) {
lg(LOG_ERR, "nbio: F_SETFL: %s", strerror(errno));
return;
}
}
int
opensocket(struct state *sp, struct addr *addr, u_short port)
{
int s;
struct sockaddr sa;
static int on = 1;
char buf[64];
addr2sa(addr, port, &sa);
(void)snprintf(buf, sizeof(buf), "%s.%hu", addr2str(addr), port);
s = socket(PF_INET, SOCK_STREAM, 0);
if (s < 0) {
lg(LOG_ERR, "socket: %s: %s", buf, strerror(errno));
exit(EX_OSERR);
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof (on)) < 0) {
lg(LOG_ERR, "SO_REUSEADDR: %s: %s", buf, strerror(errno));
exit(EX_OSERR);
}
if (bind(s, &sa, sizeof(sa)) < 0) {
lg(LOG_ERR, "bind: %s: %s", buf, strerror(errno));
exit(EX_OSERR);
}
if (listen(s, backlog) < 0) {
lg(LOG_ERR, "listen: %s: %s", buf, strerror(errno));
exit(EX_OSERR);
}
return (s);
}
int
reapdeadclients(struct state *sp)
{
int numbad;
struct client *cl, *nextcl;
numbad = 0;
for (cl = sp->clients; cl != NULL; cl = nextcl) {
/* Ok to free clients inside this loop */
nextcl = cl->next;
if (cl->c < 0)
continue;
if (fcntl(cl->c, F_GETFL, 0) >= 0 || errno != EBADF)
continue;
lg(LOG_ERR, "reapdeadclients: reaping client #%d", cl->n);
freeclient(sp, cl);
++numbad;
}
return (numbad);
}
/* Set our select timeout */
void
settimeout(struct state *sp)
{
int t, secs;
secs = sp->cf->c_select_secs;
t = timercheck(&sp->t_login);
if (t >= 0 && secs > t)
secs = t;
t = timercheck(&sp->t_sync);
if (t >= 0 && secs > t)
secs = t;
t = timercheck(&sp->t_compact);
if (t >= 0 && secs > t)
secs = t;
t = timercheck(&sp->t_ayt);
if (t >= 0 && secs > t)
secs = t;
/* Don't screw around if we have things to do */
if (sp->state == ASTATE_LOGGEDIN &&
(!sp->sentattrs || !sp->listedallacls || !sp->listedroutes))
secs = 0;
/* Always sleep for at least 10ms */
if (secs > 0) {
sp->timeout.tv_sec = secs;
sp->timeout.tv_usec = 0;
} else {
sp->timeout.tv_sec = 0;
sp->timeout.tv_usec = 10 * 1000;
}
}
/* Reap exiting child */
RETSIGTYPE
sigchild(int signo)
{
pid_t pid;
DECLWAITSTATUS status;
pid = waitpid(0, &status, WNOHANG);
return RETSIGVAL;
}
RETSIGTYPE
sighup(int signo)
{
++reload;
return RETSIGVAL;
}
#ifdef notdef
RETSIGTYPE
sigpipe(int signo)
{
struct state *sp;
sp = &state;
if (sp->c >= 0) {
close(sp->c);
sp->c = -1;
}
return RETSIGVAL;
}
#endif
RETSIGTYPE
sigterm(int signo)
{
struct state *sp;
struct client *cl;
sp = &state;
for (cl = sp->clients; cl != NULL; cl = cl->next) {
close(cl->c);
cl->c = -1;
}
++sawterm;
return RETSIGVAL;
}
int
update_fd_set(struct state *sp, fd_set *rp, fd_set *wp)
{
int nfds;
struct client *cl;
FD_ZERO(rp);
FD_ZERO(wp);
/* Normal socket */
nfds = sp->s;
FD_SET(sp->s, rp);
if (sp->rfd >= 0) {
FD_SET(sp->rfd, rp);
nfds = MAX(nfds, sp->rfd);
}
/* R/O socket */
if (sp->ro >= 0) {
FD_SET(sp->ro, rp);
nfds = MAX(nfds, sp->ro);
}
/* Web registration socket */
if (sp->web >= 0) {
FD_SET(sp->web, rp);
nfds = MAX(nfds, sp->web);
}
#ifdef HAVE_BROCCOLI
/* broccoli socket */
if (sp->b >= 0) {
FD_SET(sp->b, rp);
nfds = MAX(nfds, sp->b);
}
#endif
/* Client sockets */
for (cl = sp->clients; cl != NULL; cl = cl->next) {
if (cl->c >= 0) {
FD_SET(cl->c, rp);
nfds = MAX(nfds, cl->c);
if (cl->wbuf.len > 0)
FD_SET(cl->c, wp);
}
}
++nfds;
return (nfds);
}
__dead void
usage(void)
{
fprintf(stderr, "%s version %s\n", prog, version);
#ifdef HAVE_BROCCOLI
fprintf(stderr, "Broccoli version %s\n", broccoli_version);
#endif
fprintf(stderr,
"Usage: %s [-dfv] [-c config] [-o logfile] [-P pidfile]\n", prog);
exit(EX_USAGE);
}