X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=secnet.c;h=756ab62b78aa27f3d7734ec671ef4cb11da0c697;hp=2bf50fd40992406108e297cb0dd63cbfe0d24244;hb=423936d35d7671fd017d2d611d418d1b8eeb6195;hpb=29672515f5b2864a1e5b849a209cefd2adfac757 diff --git a/secnet.c b/secnet.c index 2bf50fd..756ab62 100644 --- a/secnet.c +++ b/secnet.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "util.h" #include "conffile.h" @@ -25,6 +26,7 @@ static const char *sites_key="sites"; bool_t just_check_config=False; static char *userid=NULL; static uid_t uid=0; +static gid_t gid; bool_t background=True; static char *pidfile=NULL; bool_t require_root_privileges=False; @@ -34,16 +36,19 @@ static pid_t secnet_pid; /* Structures dealing with poll() call */ struct poll_interest { - beforepoll_fn *before; + beforepoll_fn *before; /* 0 if deregistered and waiting to be deleted */ afterpoll_fn *after; void *state; - int32_t max_nfds; int32_t nfds; cstring_t desc; - struct poll_interest *next; + LIST_ENTRY(poll_interest) entry; }; -static struct poll_interest *reg=NULL; -static int32_t total_nfds=10; +static LIST_HEAD(, poll_interest) reg = LIST_HEAD_INITIALIZER(®); + +static bool_t interest_isregistered(const struct poll_interest *i) +{ + return !!i->before; +} static bool_t finished=False; @@ -60,16 +65,17 @@ static void parse_options(int argc, char **argv) {"help", 0, 0, 2}, {"version", 0, 0, 1}, {"nodetach", 0, 0, 'n'}, + {"managed", 0, 0, 'm'}, {"silent", 0, 0, 'f'}, {"quiet", 0, 0, 'f'}, - {"debug", 1, 0, 'd'}, + {"debug", 0, 0, 'd'}, {"config", 1, 0, 'c'}, {"just-check-config", 0, 0, 'j'}, {"sites-key", 1, 0, 's'}, {0,0,0,0} }; - c=getopt_long(argc, argv, "vwdnjc:ft:s:", + c=getopt_long(argc, argv, "vwdnjc:ft:s:m", long_options, &option_index); if (c==-1) break; @@ -87,7 +93,8 @@ static void parse_options(int argc, char **argv) " -s, --sites-key=name configuration key that " "specifies active sites\n" " -n, --nodetach do not run in background\n" - " -d, --debug=item,... set debug options\n" + " -m, --managed running under a supervisor\n" + " -d, --debug output debug messages\n" " --help display this help and exit\n" " --version output version information " "and exit\n" @@ -122,6 +129,10 @@ static void parse_options(int argc, char **argv) background=False; break; + case 'm': + secnet_is_daemon=True; + break; + case 'c': if (optarg) configfile=safe_strdup(optarg,"config_filename"); @@ -141,6 +152,7 @@ static void parse_options(int argc, char **argv) break; case '?': + exit(1); break; default: @@ -181,17 +193,10 @@ static void setup(dict_t *config) /* Who are we supposed to run as? */ userid=dict_read_string(system,"userid",False,"system",loc); if (userid) { - do { - pw=getpwent(); - if (pw && strcmp(pw->pw_name,userid)==0) { - uid=pw->pw_uid; - break; - } - } while(pw); - endpwent(); - if (uid==0) { + if (!(pw=getpwnam(userid))) fatal("userid \"%s\" not found",userid); - } + uid=pw->pw_uid; + gid=pw->pw_gid; } /* Pidfile name */ @@ -225,23 +230,27 @@ static void setup(dict_t *config) } } -void register_for_poll(void *st, beforepoll_fn *before, - afterpoll_fn *after, int32_t max_nfds, cstring_t desc) +struct poll_interest *register_for_poll(void *st, beforepoll_fn *before, + afterpoll_fn *after, cstring_t desc) { struct poll_interest *i; - i=safe_malloc(sizeof(*i),"register_for_poll"); + NEW(i); i->before=before; i->after=after; i->state=st; - i->max_nfds=max_nfds; i->nfds=0; i->desc=desc; - assert(total_nfds < INT_MAX - max_nfds); - total_nfds+=max_nfds; - i->next=reg; - reg=i; - return; + LIST_INSERT_HEAD(®, i, entry); + return i; +} + +void deregister_for_poll(struct poll_interest *i) +{ + /* We cannot simply throw this away because we're reentrantly + * inside the main loop, which needs to remember which range of + * fds corresponds to this now-obsolete interest */ + i->before=0; } static void system_phase_hook(void *sst, uint32_t newphase) @@ -292,12 +301,11 @@ uint64_t now_global; static void run(void) { - struct poll_interest *i; - int rv, nfds, remain, idx; + struct poll_interest *i, *itmp; + int rv, nfds, idx; int timeout; - struct pollfd *fds; - - fds=safe_malloc(sizeof(*fds)*total_nfds, "run"); + struct pollfd *fds=0; + int allocdfds=0, shortfall=0; Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid); @@ -308,29 +316,54 @@ static void run(void) now_global=((uint64_t)tv_now_global.tv_sec*(uint64_t)1000)+ ((uint64_t)tv_now_global.tv_usec/(uint64_t)1000); idx=0; - for (i=reg; i; i=i->next) { - if(fds[idx].revents & POLLNVAL) { - fatal("run: poll (%s) set POLLNVAL", i->desc); + LIST_FOREACH(i, ®, entry) { + int check; + if (interest_isregistered(i)) { + for (check=0; checknfds; check++) { + if(fds[idx+check].revents & POLLNVAL) { + fatal("run: poll (%s#%d) set POLLNVAL", i->desc, check); + } + } + i->after(i->state, fds+idx, i->nfds); } - i->after(i->state, fds+idx, i->nfds); idx+=i->nfds; } - remain=total_nfds; + if (shortfall) { + allocdfds *= 2; + allocdfds += shortfall; + REALLOC_ARY(fds,allocdfds); + } + shortfall=0; idx=0; timeout=-1; - for (i=reg; i; i=i->next) { + LIST_FOREACH_SAFE(i, ®, entry, itmp) { + int remain=allocdfds-idx; nfds=remain; - rv=i->before(i->state, fds+idx, &nfds, &timeout); - if (rv!=0) { - /* XXX we need to handle this properly: increase the - nfds available */ - fatal("run: beforepoll_fn (%s) returns %d",i->desc,rv); + if (interest_isregistered(i)) { + rv=i->before(i->state, fds+idx, &nfds, &timeout); + if (rv!=0) { + if (rv!=ERANGE) + fatal("run: beforepoll_fn (%s) returns %d",i->desc,rv); + assert(nfds < INT_MAX/4 - shortfall); + shortfall += nfds-remain; + nfds=0; + timeout=0; + } + } else { + nfds=0; } if (timeout<-1) { - fatal("run: beforepoll_fn (%s) set timeout to %d",timeout); + fatal("run: beforepoll_fn (%s) set timeout to %d", + i->desc,timeout); + } + if (!interest_isregistered(i)) { + /* check this here, rather than earlier, so that we + handle the case where i->before() calls deregister */ + LIST_REMOVE(i, entry); + free(i); + continue; } idx+=nfds; - remain-=nfds; i->nfds=nfds; } do { @@ -350,7 +383,32 @@ static void run(void) free(fds); } +bool_t will_droppriv(void) +{ + assert(current_phase >= PHASE_SETUP); + return !!uid; +} + +/* Surrender privileges, if necessary */ static void droppriv(void) +{ + if (userid) { + if (setgid(gid)!=0) + fatal_perror("can't set gid to %ld",(long)gid); + if (initgroups(userid, gid) < 0) + fatal_perror("initgroups"); + if (setuid(uid)!=0) { + fatal_perror("can't set uid to \"%s\"",userid); + } + assert(getuid() == uid); + assert(geteuid() == uid); + assert(getgid() == gid); + assert(getegid() == gid); + } +} + +/* Become a daemon, if necessary */ +static void become_daemon(void) { FILE *pf=NULL; pid_t p; @@ -358,58 +416,47 @@ static void droppriv(void) add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL); - /* Open the pidfile for writing now: we may be unable to do so - once we drop privileges. */ - if (pidfile) { - pf=fopen(pidfile,"w"); - if (!pf) { - fatal_perror("cannot open pidfile \"%s\"",pidfile); - } - } - if (!background && pf) { - fprintf(pf,"%d\n",getpid()); - fclose(pf); - } - - /* Now drop privileges */ - if (uid!=0) { - if (setuid(uid)!=0) { - fatal_perror("can't set uid to \"%s\"",userid); - } - } - if (background) { + /* We only want to become a daemon if we are not one + already */ + if (background && !secnet_is_daemon) { p=fork(); if (p>0) { - if (pf) { - /* Parent process - write pidfile, exit */ - fprintf(pf,"%d\n",p); - fclose(pf); - } - exit(0); + /* Parent process - just exit */ + _exit(0); } else if (p==0) { /* Child process - all done, just carry on */ - if (pf) fclose(pf); - /* Close stdin and stdout; we don't need them any more. - stderr is redirected to the system/log facility */ - if (pipe(errfds)!=0) { - fatal_perror("can't create pipe for stderr"); - } - close(0); - close(1); - close(2); - dup2(errfds[1],0); - dup2(errfds[1],1); - dup2(errfds[1],2); secnet_is_daemon=True; - setsid(); - log_from_fd(errfds[0],"stderr",system_log); + if (setsid() < 0) + fatal_perror("setsid"); } else { /* Error */ fatal_perror("cannot fork"); exit(1); } } + if (secnet_is_daemon) { + /* stderr etc are redirected to the system/log facility */ + pipe_cloexec(errfds); + if (dup2(errfds[1],0) < 0 + || dup2(errfds[1],1) < 0 + || dup2(errfds[1],2) < 0) + fatal_perror("can't dup2 pipe"); + if (close(errfds[1]) < 0) + fatal_perror("can't close redundant pipe endpoint"); + log_from_fd(errfds[0],"stderr",system_log); + } secnet_pid=getpid(); + + /* Now we can write the pidfile */ + if (pidfile) { + pf=fopen(pidfile,"w"); + if (!pf) { + fatal_perror("cannot open pidfile \"%s\"",pidfile); + } + if (fprintf(pf,"%ld\n",(long)secnet_pid) < 0 + || fclose(pf) < 0) + fatal_perror("cannot write to pidfile \"%s\"",pidfile); + } } static signal_notify_fn finish,ignore_hup; @@ -428,6 +475,8 @@ int main(int argc, char **argv) { dict_t *config; + phase_hooks_init(); + enter_phase(PHASE_GETOPTS); parse_options(argc,argv); @@ -442,6 +491,9 @@ int main(int argc, char **argv) exit(0); } + enter_phase(PHASE_DAEMONIZE); + become_daemon(); + enter_phase(PHASE_GETRESOURCES); /* Appropriate phase hooks will have been run */