#include <sys/socket.h>
#include <arpa/inet.h>
#include <pwd.h>
+#include <grp.h>
#include "util.h"
#include "conffile.h"
#include "process.h"
+#if __APPLE__
+/* apple's poll() does not work on char devs */
+# define USE_SELECT 1
+#endif
+
/* XXX should be from autoconf */
static const char *configfile="/etc/secnet/secnet.conf";
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;
beforepoll_fn *before;
afterpoll_fn *after;
void *state;
- uint32_t max_nfds;
- uint32_t nfds;
+ int32_t max_nfds;
+ int32_t nfds;
cstring_t desc;
struct poll_interest *next;
};
static struct poll_interest *reg=NULL;
-static uint32_t total_nfds=10;
+static int32_t total_nfds=10;
static bool_t finished=False;
{"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;
" -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"
background=False;
break;
+ case 'm':
+ secnet_is_daemon=True;
+ break;
+
case 'c':
if (optarg)
configfile=safe_strdup(optarg,"config_filename");
break;
case '?':
+ exit(1);
break;
default:
/* 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 */
}
void register_for_poll(void *st, beforepoll_fn *before,
- afterpoll_fn *after, uint32_t max_nfds, cstring_t desc)
+ afterpoll_fn *after, int32_t max_nfds, cstring_t desc)
{
struct poll_interest *i;
}
}
+#if USE_SELECT
+static int fakepoll(struct pollfd *fds, int nfds, int timeout) {
+ fd_set infds[1], outfds[1];
+ int maxfd = -1, i, rc;
+ struct timeval tvtimeout;
+ FD_ZERO(infds);
+ FD_ZERO(outfds);
+ for(i = 0; i < nfds; ++i) {
+ if(fds[i].events & POLLIN)
+ FD_SET(fds[i].fd, infds);
+ if(fds[i].events & POLLOUT)
+ FD_SET(fds[i].fd, outfds);
+ if(fds[i].fd > maxfd)
+ maxfd = fds[i].fd;
+ }
+ if(timeout != -1) {
+ tvtimeout.tv_sec = timeout / 1000;
+ tvtimeout.tv_usec = 1000 * (timeout % 1000);
+ }
+ rc = select(maxfd + 1, infds, outfds, NULL,
+ timeout == -1 ? NULL : &tvtimeout);
+ if(rc >= 0) {
+ for(i = 0; i < nfds; ++i) {
+ int revents = 0;
+ if(FD_ISSET(fds[i].fd, infds))
+ revents |= POLLIN;
+ if(FD_ISSET(fds[i].fd, outfds))
+ revents |= POLLOUT;
+ fds[i].revents = revents;
+ }
+ }
+ return rc;
+}
+#endif
+
+struct timeval tv_now_global;
+uint64_t now_global;
+
static void run(void)
{
- struct timeval tv_now;
- uint64_t now;
struct poll_interest *i;
int rv, nfds, remain, idx;
int timeout;
Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid);
do {
- if (gettimeofday(&tv_now, NULL)!=0) {
+ if (gettimeofday(&tv_now_global, NULL)!=0) {
fatal_perror("main loop: gettimeofday");
}
- now=((uint64_t)tv_now.tv_sec*(uint64_t)1000)+
- ((uint64_t)tv_now.tv_usec/(uint64_t)1000);
+ 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) {
- i->after(i->state, fds+idx, i->nfds, &tv_now, &now);
+ int check;
+ for (check=0; check<i->nfds; 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);
idx+=i->nfds;
}
remain=total_nfds;
timeout=-1;
for (i=reg; i; i=i->next) {
nfds=remain;
- rv=i->before(i->state, fds+idx, &nfds, &timeout, &tv_now, &now);
+ rv=i->before(i->state, fds+idx, &nfds, &timeout);
if (rv!=0) {
/* XXX we need to handle this properly: increase the
nfds available */
}
do {
if (finished) break;
+#if USE_SELECT
+ rv=fakepoll(fds, idx, timeout);
+#else
rv=poll(fds, idx, timeout);
+#endif
if (rv<0) {
if (errno!=EINTR) {
fatal_perror("run: poll");
free(fds);
}
+/* 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;
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 */
+ 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;
exit(0);
}
+ enter_phase(PHASE_DAEMONIZE);
+ become_daemon();
+
enter_phase(PHASE_GETRESOURCES);
/* Appropriate phase hooks will have been run */