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>
- if (secnet_is_daemon) {
/* Messages go to the system log interface */
bp=strlen(buff);
assert(bp < MESSAGE_BUFLEN);
/* Messages go to the system log interface */
bp=strlen(buff);
assert(bp < MESSAGE_BUFLEN);
{"help", 0, 0, 2},
{"version", 0, 0, 1},
{"nodetach", 0, 0, 'n'},
{"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'},
{"silent", 0, 0, 'f'},
{"quiet", 0, 0, 'f'},
{"debug", 0, 0, 'd'},
- 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;
long_options, &option_index);
if (c==-1)
break;
" -s, --sites-key=name configuration key that "
"specifies active sites\n"
" -n, --nodetach do not run in background\n"
" -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 "
" -d, --debug output debug messages\n"
" --help display this help and exit\n"
" --version output version information "
+ case 'm':
+ secnet_is_daemon=True;
+ break;
+
case 'c':
if (optarg)
configfile=safe_strdup(optarg,"config_filename");
case 'c':
if (optarg)
configfile=safe_strdup(optarg,"config_filename");
/* Who are we supposed to run as? */
userid=dict_read_string(system,"userid",False,"system",loc);
if (userid) {
/* 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;
fatal("userid \"%s\" not found",userid);
uid=pw->pw_uid;
gid=pw->pw_gid;
+/* Surrender privileges, if necessary */
static void droppriv(void)
{
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 (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);
fatal_perror("initgroups");
if (setuid(uid)!=0) {
fatal_perror("can't set uid to \"%s\"",userid);
assert(getgid() == gid);
assert(getegid() == gid);
}
assert(getgid() == gid);
assert(getegid() == gid);
}
+}
+
+/* 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) {
- 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 */
} 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);
- setsid();
- log_from_fd(errfds[0],"stderr",system_log);
+ if (setsid() < 0)
+ fatal_perror("setsid");
} else {
/* Error */
fatal_perror("cannot fork");
exit(1);
}
}
} 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);
+ }
+
+ /* 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;
}
static signal_notify_fn finish,ignore_hup;
+ enter_phase(PHASE_DAEMONIZE);
+ become_daemon();
+
enter_phase(PHASE_GETRESOURCES);
/* Appropriate phase hooks will have been run */
enter_phase(PHASE_GETRESOURCES);
/* Appropriate phase hooks will have been run */
/* The secnet program goes through a number of phases in its lifetime.
Module code may arrange to be called just as various phases are
/* 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.
+
+ 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 */
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,
PHASE_GETRESOURCES, /* Obtain all external resources */
PHASE_DROPPRIV, /* Last chance for privileged operations */
PHASE_RUN,
<key>ProgramArguments</key>
<array>
<string>/usr/local/sbin/secnet</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/sbin/secnet</string>
</array>
<key>WorkingDirectory</key>
<string>/</string>
</array>
<key>WorkingDirectory</key>
<string>/</string>
"PHASE_GETOPTS",
"PHASE_READCONFIG",
"PHASE_SETUP",
"PHASE_GETOPTS",
"PHASE_READCONFIG",
"PHASE_SETUP",
"PHASE_GETRESOURCES",
"PHASE_DROPPRIV",
"PHASE_RUN",
"PHASE_GETRESOURCES",
"PHASE_DROPPRIV",
"PHASE_RUN",