[PATCH 3/3] New --managed option for use when running under a daemon supervisor.

Richard Kettlewell rjk at terraraq.org.uk
Sat Jul 23 10:32:20 BST 2011


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 at terraraq.org.uk>
---
 log.c                        |    2 +-
 secnet.c                     |   87 ++++++++++++++++++++++-------------------
 secnet.h                     |   16 +++++---
 uk.org.greenend.secnet.plist |    2 +-
 util.c                       |    1 +
 5 files changed, 60 insertions(+), 48 deletions(-)

diff --git a/log.c b/log.c
index 6f4b738..1c8d64a 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 (secnet_is_daemon && 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 f9808b7..71e40dc 100644
--- a/secnet.c
+++ b/secnet.c
@@ -60,6 +60,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'},
@@ -69,7 +70,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;
@@ -87,6 +88,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 "
@@ -122,6 +124,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");
@@ -354,7 +360,7 @@ static void run(void)
     free(fds);
 }
 
-static void droppriv(void)
+static void become_daemon(void)
 {
     FILE *pf=NULL;
     pid_t p;
@@ -362,58 +368,56 @@ 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);
-	    }
+	    /* 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);
 	} 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 void droppriv(void) {
+    if (uid!=0) {	
+	if (setuid(uid)!=0) {
+	    fatal_perror("can't set uid to \"%s\"",userid);
+	}
+    }
 }
 
 static signal_notify_fn finish,ignore_hup;
@@ -446,6 +450,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 b60972f..9f9befa 100644
--- a/secnet.h
+++ b/secnet.h
@@ -184,17 +184,21 @@ 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. */
 
 #define PHASE_INIT          0
 #define PHASE_GETOPTS       1  /* Process command-line arguments */
 #define PHASE_READCONFIG    2  /* Parse and process configuration file */
 #define PHASE_SETUP         3  /* Process information in configuration */
-#define PHASE_GETRESOURCES  4  /* Obtain all external resources */
-#define PHASE_DROPPRIV      5  /* Last chance for privileged operations */
-#define PHASE_RUN           6
-#define PHASE_SHUTDOWN      7  /* About to die; delete key material, etc. */
-#define NR_PHASES           8
+#define PHASE_DAEMONIZE     4  /* Become a daemon (if necessary) */
+#define PHASE_GETRESOURCES  5  /* Obtain all external resources */
+#define PHASE_DROPPRIV      6  /* Last chance for privileged operations */
+#define PHASE_RUN           7
+#define PHASE_SHUTDOWN      8  /* About to die; delete key material, etc. */
+#define NR_PHASES           9
 
 typedef void hook_fn(void *self, uint32_t newphase);
 bool_t add_hook(uint32_t phase, hook_fn *f, void *state);
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 @@
 	<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 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",
-- 
1.6.4.2




More information about the sgo-software-discuss mailing list