* userv - overlord.c
* daemon main program, collects request and forks handlers
*
- * Copyright (C)1996-1997 Ian Jackson
+ * Copyright (C)1996-1997,1999 Ian Jackson
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
#include <fnmatch.h>
#include <sys/wait.h>
#include <sys/types.h>
-#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
+#include <fcntl.h>
+#include <time.h>
#include <dirent.h>
#include <sys/un.h>
pid_t overlordpid;
-static pid_t checkpid= -1;
-static sig_atomic_t needcheck= 1;
+static pid_t checkpid= -1, detachpid= -1;
+static sig_atomic_t needcheck= 1; /* 2 means we half-expect the server to be down */
static void checkstalepipes(void) {
/* There is an unimportant race here. If there is a stale pipe but
r= waitpid((pid_t)-1,&status,WNOHANG);
if (!r || (r==-1 && errno==ECHILD)) break;
if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
+ if (r==detachpid) {
+ if (WIFEXITED(status) && WEXITSTATUS(status)==4) _exit(4);
+ fprintf(stderr,"uservd: detaching child failed with unexpected code %d\n",status);
+ exit(6);
+ }
if (r==checkpid) {
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status)) {
- syslog(LOG_NOTICE,"no longer the uservd - exiting");
- _exit(0);
+ syslog(LOG_WARNING,"no longer the uservd - exiting");
+ _exit(2);
} else if (WEXITSTATUS(status)!=1) {
syslog(LOG_ERR,"check pid %ld exited with status %d",
(long)checkpid,WEXITSTATUS(status));
}
} else if (WIFSIGNALED(status)) {
if (WTERMSIG(status) == SIGALRM && !WCOREDUMP(status)) {
- syslog(LOG_NOTICE,"check timed out; no longer the uservd - exiting");
- _exit(0);
+ syslog(LOG_WARNING,"check timed out; no longer the uservd - exiting");
+ _exit(2);
} else {
syslog(LOG_ERR,"check pid %ld %s due to signal %s",
(long)checkpid,
} else if (!WIFEXITED(status)) {
syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %d",
(long)r,status);
+ } else if (WEXITSTATUS(status)==10) {
+ needcheck= 2;
} else if (WEXITSTATUS(status)>12) {
if (WEXITSTATUS(status)>24)
syslog(LOG_ERR,"call pid %ld exited with status %d >24",
(long)r,WEXITSTATUS(status));
checkstalepipes();
+ needcheck= 1;
}
}
}
return;
}
+static void sighandler_usr1(int x) {
+ _exit(0);
+}
+
static void sighandler_alrm(int x) {
needcheck= 1;
}
+static void sighandler_termint(int sig) {
+ syslog(LOG_NOTICE,"terminating due to signal %s",strsignal(sig));
+ _exit(1);
+}
+
static void blocksignals(int how) {
int r;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGCHLD);
sigaddset(&set,SIGALRM);
+ sigaddset(&set,SIGTERM);
+ sigaddset(&set,SIGINT);
r= sigprocmask(how,&set,0); assert(!r);
}
-static void NONRETURNING docheck(void) {
+static void NONRETURNING docheck(int needwanted) {
#ifndef DEBUG
/* This subprocess exits with status 0 if the parent should die,
* 1 if it should not, and something else if it fails horribly.
int sfd, r, remain;
unsigned char *p;
struct opening_msg opening_mbuf;
+ struct request_msg request_mbuf;
+ unsigned long endmagic;
struct sigaction sig;
struct sockaddr_un ssockname;
r= connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname));
if (r) {
- if (errno == ECONNREFUSED || errno == ENOENT)
- { syslog(LOG_NOTICE,"uservd daemon is not running: %m"); exit(0); }
+ if (errno == ECONNREFUSED || errno == ENOENT) {
+ if (needwanted != 2)
+ syslog(LOG_WARNING,"real uservd daemon is not running: %m");
+ exit(0);
+ }
syslog(LOG_ERR,"unable to connect to uservd daemon: %m"); exit(1);
}
remain-= r; p+= r;
}
if (opening_mbuf.magic != OPENING_MAGIC) {
- syslog(LOG_NOTICE,"magic number mismatch");
+ syslog(LOG_WARNING,"magic number mismatch");
exit(0);
}
if (memcmp(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE)) {
- syslog(LOG_NOTICE,"protocol checksum mismatch");
+ syslog(LOG_WARNING,"protocol checksum mismatch");
exit(0);
}
if (opening_mbuf.overlordpid != overlordpid) {
- syslog(LOG_NOTICE,"overlord pid mismatch");
+ syslog(LOG_WARNING,"overlord pid mismatch");
exit(0);
}
- syslog(LOG_NOTICE,"check - same daemon still running");
+ memset(&request_mbuf,0,sizeof(request_mbuf));
+ request_mbuf.magic= REQUEST_MAGIC;
+ request_mbuf.clientpid= -1;
+ request_mbuf.serviceuserlen= 0;
+ request_mbuf.servicelen= 0;
+ request_mbuf.loginnamelen= 0;
+ request_mbuf.spoofed= 0;
+ request_mbuf.cwdlen= 0;
+ request_mbuf.overridelen= -1;
+ request_mbuf.callinguid= -1;
+ request_mbuf.ngids= 0;
+ request_mbuf.nreadfds= 0;
+ request_mbuf.nwritefds= 0;
+ request_mbuf.nargs= 0;
+ request_mbuf.nvars= 0;
+ r= write(sfd,&request_mbuf,sizeof(request_mbuf));
+ if (r==sizeof(request_mbuf)) {
+ endmagic= REQUEST_END_MAGIC;
+ write(sfd,&endmagic,sizeof(endmagic));
+ }
+ syslog(LOG_NOTICE,"uservd[%ld] is running",(long)overlordpid);
#endif
exit(1);
}
+static void NONRETURNING startupsyscallerr(const char *what) {
+ fprintf(stderr,
+ "uservd: system call failed during startup:\n"
+ "uservd: %s: %s\n",
+ what,strerror(errno));
+ exit(4);
+}
+
int main(int argc, char *const *argv) {
- int mfd, sfd, csocklen, e;
+ int mfd, sfd, nfd, csocklen, e, r, becomedaemon;
struct sigaction sigact;
struct sockaddr_un ssockname, csockname;
- pid_t child;
+ pid_t child, parentpid, sid;
#ifdef NDEBUG
abort(); /* Do not disable assertions in this security-critical code ! */
#endif
- if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
+ becomedaemon= 0;
+
+ if (argv[1] && !strcmp(argv[1],"-daemon")) {
+ becomedaemon= 1;
+ argv++; argc--;
+ }
+ if (argc>1) { fputs("usage: uservd [-daemon]\n",stderr); exit(3); }
openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
- if (chdir(VARDIR)) { syslog(LOG_CRIT,"cannot change to " VARDIR ": %m"); exit(4); }
+ if (chdir(VARDIR)) startupsyscallerr("cannot change to " VARDIR);
checkstalepipes();
- overlordpid= getpid();
- if (overlordpid==-1) { syslog(LOG_CRIT,"cannot getpid: %m"); exit(4); }
+ overlordpid= parentpid= getpid();
+ if (parentpid==-1) startupsyscallerr("cannot getpid");
mfd= socket(AF_UNIX,SOCK_STREAM,0);
- if (mfd<0) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
+ if (mfd<0) startupsyscallerr("cannot create master socket");
assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
ssockname.sun_family= AF_UNIX;
strcpy(ssockname.sun_path,RENDEZVOUS);
unlink(RENDEZVOUS);
- if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
- { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
- if (listen(mfd,5))
- { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
+ r= bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname));
+ if (r) startupsyscallerr("cannot bind master socket");
+ if (listen(mfd,5)) startupsyscallerr("cannot listen on master socket");
sigemptyset(&sigact.sa_mask);
sigaddset(&sigact.sa_mask,SIGCHLD);
sigact.sa_flags= SA_NOCLDSTOP;
sigact.sa_handler= sighandler_chld;
- if (sigaction(SIGCHLD,&sigact,0))
- { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
+ if (sigaction(SIGCHLD,&sigact,0)) startupsyscallerr("cannot setup sigchld handler");
sigact.sa_handler= sighandler_alrm;
- if (sigaction(SIGALRM,&sigact,0))
- { syslog(LOG_CRIT,"cannot setup sigalrm handler: %m"); exit(4); }
+ if (sigaction(SIGALRM,&sigact,0)) startupsyscallerr("cannot setup sigalrm handler");
+
+ if (becomedaemon) {
+ sigact.sa_handler= sighandler_usr1;
+ if (sigaction(SIGUSR1,&sigact,0)) startupsyscallerr("cannot setup sigusr1 handler");
+
+ detachpid= fork(); if (detachpid==-1) startupsyscallerr("cannot fork to detach");
+ if (detachpid) {
+ pause();
+ fputs("uservd: pause unexpectedly returned during detach\n",stderr);
+ exit(4);
+ }
+ sigact.sa_handler= SIG_DFL;
+ if (sigaction(SIGUSR1,&sigact,0)) startupsyscallerr("cannot restore sigusr1");
+ }
+
+ sigact.sa_handler= sighandler_termint;
+ if (sigaction(SIGTERM,&sigact,0)) startupsyscallerr("cannot setup sigterm handler");
+ if (sigaction(SIGINT,&sigact,0)) startupsyscallerr("cannot setup sigint handler");
+
+ if (becomedaemon) {
+ nfd= open("/dev/null",O_RDWR);
+ if (nfd<0) startupsyscallerr("cannot open /dev/null");
+ sid= setsid(); if (sid == -1) startupsyscallerr("cannot create new session");
+ overlordpid= getpid();
+ if (overlordpid == -1) startupsyscallerr("getpid after detach");
+ if (dup2(nfd,0)<0 || dup2(nfd,1)<0)
+ startupsyscallerr("cannot dup /dev/null for stdin/out");
+ r= kill(parentpid,SIGUSR1); if (r) startupsyscallerr("send SIGUSR1 to detach");
+ r= dup2(nfd,2);
+ if (r<0) { syslog(LOG_CRIT,"cannot dup /dev/null for stderr: %m"); exit(5); }
+ close(nfd);
+ }
syslog(LOG_NOTICE,"started");
+
for (;;) {
if (needcheck) {
- assert(checkpid==-1);
- checkpid= fork();
- if (checkpid==-1) { syslog(LOG_CRIT,"fork for check: %m"); exit(5); }
- if (!checkpid) docheck();
+ while (checkpid==-1) {
+ checkpid= fork();
+ if (checkpid!=-1) {
+ if (!checkpid) docheck(needcheck);
+ break;
+ } else if (errno==EAGAIN) {
+ syslog(LOG_ERR,"fork for check - will wait and retry: %m");
+ r= alarm(USERVD_CHECKFORK_RETRY);
+ if (r<0) { syslog(LOG_CRIT,"set alarm for retry check: %m"); exit(5); }
+ break;
+ } else if (errno!=EINTR) {
+ syslog(LOG_CRIT,"fork for check: %m"); exit(5);
+ }
+ }
needcheck= 0;
}
csocklen= sizeof(csockname);