From: Richard Kettlewell Date: Sat, 10 Dec 2011 22:35:47 +0000 (+0000) Subject: New --managed option for use when running under a daemon supervisor. X-Git-Tag: v0.2.0~2 X-Git-Url: https://git.distorted.org.uk/~mdw/secnet/commitdiff_plain/7b1a9fb74228564b5ee07b48c042ee7b9a2260f1 New --managed option for use when running under a daemon supervisor. The effect is secnet is told that it is running as a daemon right from the start, so it knows to follow the logging rules for daemons but not to fork. The conflation of daemonization with dropping privilege is also unpicked by this patch. Most importantly this ensures that errors from PHASE_GETRESOURCES operations such as 'route' commands is sent to the logfile (or syslog). Signed-off-by: Richard Kettlewell --- diff --git a/log.c b/log.c index 6f4b738..3a50a27 100644 --- a/log.c +++ b/log.c @@ -22,7 +22,7 @@ static void vMessage(uint32_t class, const char *message, va_list args) size_t bp; char *nlp; - if (secnet_is_daemon) { + if (system_log) { /* Messages go to the system log interface */ bp=strlen(buff); assert(bp < MESSAGE_BUFLEN); diff --git a/secnet.c b/secnet.c index 7759834..465a93f 100644 --- a/secnet.c +++ b/secnet.c @@ -62,6 +62,7 @@ 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", 0, 0, 'd'}, @@ -71,7 +72,7 @@ static void parse_options(int argc, char **argv) {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; @@ -89,6 +90,7 @@ 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" + " -m, --managed running under a supervisor\n" " -d, --debug output debug messages\n" " --help display this help and exit\n" " --version output version information " @@ -124,6 +126,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"); @@ -184,7 +190,7 @@ static void setup(dict_t *config) /* Who are we supposed to run as? */ userid=dict_read_string(system,"userid",False,"system",loc); if (userid) { - if(!(pw=getpwnam(userid))) + if (!(pw=getpwnam(userid))) fatal("userid \"%s\" not found",userid); uid=pw->pw_uid; gid=pw->pw_gid; @@ -349,32 +355,13 @@ static void run(void) free(fds); } +/* Surrender privileges, if necessary */ static void droppriv(void) { - FILE *pf=NULL; - pid_t p; - int errfds[2]; - - 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 (userid) { if (setgid(gid)!=0) fatal_perror("can't set gid to %ld",(long)gid); - if(initgroups(userid, gid) < 0) + if (initgroups(userid, gid) < 0) fatal_perror("initgroups"); if (setuid(uid)!=0) { fatal_perror("can't set uid to \"%s\"",userid); @@ -384,39 +371,60 @@ static void droppriv(void) assert(getgid() == gid); assert(getegid() == gid); } - if (background) { +} + +/* Become a daemon, if necessary */ +static void become_daemon(void) +{ + FILE *pf=NULL; + pid_t p; + int errfds[2]; + + add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL); + + /* 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 */ + if (pipe(errfds)!=0) { + fatal_perror("can't create pipe for stderr"); + } + 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; @@ -449,6 +457,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 */ diff --git a/secnet.h b/secnet.h index e7eb161..06beb05 100644 --- a/secnet.h +++ b/secnet.h @@ -184,13 +184,17 @@ extern void register_for_poll(void *st, beforepoll_fn *before, /* The secnet program goes through a number of phases in its lifetime. Module code may arrange to be called just as various phases are - entered. */ + entered. + + Remember to update the table in util.c if changing the set of + phases. */ enum phase { PHASE_INIT, PHASE_GETOPTS, /* Process command-line arguments */ PHASE_READCONFIG, /* Parse and process configuration file */ PHASE_SETUP, /* Process information in configuration */ + PHASE_DAEMONIZE, /* Become a daemon (if necessary) */ PHASE_GETRESOURCES, /* Obtain all external resources */ PHASE_DROPPRIV, /* Last chance for privileged operations */ PHASE_RUN, diff --git a/uk.org.greenend.secnet.plist b/uk.org.greenend.secnet.plist index f80184b..1de6ca4 100644 --- a/uk.org.greenend.secnet.plist +++ b/uk.org.greenend.secnet.plist @@ -14,7 +14,7 @@ ProgramArguments /usr/local/sbin/secnet - -n + -m WorkingDirectory / diff --git a/util.c b/util.c index fff5b6d..63fe76f 100644 --- a/util.c +++ b/util.c @@ -166,6 +166,7 @@ static const char *phases[NR_PHASES]={ "PHASE_GETOPTS", "PHASE_READCONFIG", "PHASE_SETUP", + "PHASE_DAEMONIZE", "PHASE_GETRESOURCES", "PHASE_DROPPRIV", "PHASE_RUN",