chiark / gitweb /
New --managed option for use when running under a daemon supervisor.
authorRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 10 Dec 2011 22:35:47 +0000 (22:35 +0000)
committerRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 10 Dec 2011 22:35:47 +0000 (22:35 +0000)
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 <rjk@terraraq.org.uk>
log.c
secnet.c
secnet.h
uk.org.greenend.secnet.plist
util.c

diff --git a/log.c b/log.c
index 6f4b7380198e7ad37dbf367d36b43b39a704e979..3a50a27ce47ab301ae317353f800fed9a2986b8b 100644 (file)
--- 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);
index 775983432821477912ab2e7ff9b878bc935cc354..465a93fc260404cad1eb2070bf8df446013e87a9 100644 (file)
--- 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 */
     
index e7eb161d2ff0114e5edea55840cd1d1a9abb4583..06beb05df27fd9c2bb8221052a7d90f48176ec22 100644 (file)
--- 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,
index f80184b3edf2e3cee42608247f2484e9ddb400bd..1de6ca47a37c1cf400a965b517527ed879b3cb86 100644 (file)
@@ -14,7 +14,7 @@
        <key>ProgramArguments</key>
        <array>
                <string>/usr/local/sbin/secnet</string>
-               <string>-n</string>
+               <string>-m</string>
        </array>
        <key>WorkingDirectory</key>
        <string>/</string>
diff --git a/util.c b/util.c
index fff5b6d13e5e0f74544723ea4ff9e8f9013634f5..63fe76f4451a80e2a6c266fffb8c1aabd878d20f 100644 (file)
--- 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",