#include "common.h"
#include "daemon.h"
+pid_t overlordpid;
+
+static pid_t checkpid= -1;
+static sig_atomic_t needcheck= 1;
+
static void checkstalepipes(void) {
/* There is an unimportant race here. If there is a stale pipe but
* another pair of processes with the same pids is about to create a
static void sighandler_chld(int x) {
pid_t r;
- int status, es;
+ int status, es, ar;
es= errno;
for (;;) {
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 (WIFSIGNALED(status)) {
- if (WCOREDUMP(status))
- syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
+ if (r==checkpid) {
+ if (WIFEXITED(status)) {
+ if (!WEXITSTATUS(status)) {
+ syslog(LOG_NOTICE,"no longer the uservd - exiting");
+ _exit(0);
+ } 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);
+ } else {
+ syslog(LOG_ERR,"check pid %ld %s due to signal %s",
+ (long)checkpid,
+ WCOREDUMP(status) ? "dumped core" : "died",
+ strsignal(WTERMSIG(status)));
+ }
+ } else {
+ syslog(LOG_ERR,"check pid %ld died due to unknown reason, code %d",
+ (long)checkpid,status);
+ }
+ checkpid= -1;
+ ar= alarm(USERVD_MYSELF_CHECK);
+ if (ar<0) { syslog(LOG_CRIT,"set alarm for next check: %m"); exit(5); }
+ } else {
+ if (WIFSIGNALED(status)) {
+ syslog(LOG_ERR,"call pid %ld %s due to signal %s",
+ (long)r,
+ WCOREDUMP(status) ? "dumped core" : "died",
strsignal(WTERMSIG(status)));
- else
- syslog(LOG_ERR,"call pid %ld died due to signal %s",
- (long)r,strsignal(WTERMSIG(status)));
- } else if (!WIFEXITED(status)) {
- syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
- (long)r,status);
- } 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();
+ } else if (!WIFEXITED(status)) {
+ syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %d",
+ (long)r,status);
+ } 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();
+ }
}
}
errno= es;
return;
}
+static void sighandler_alrm(int x) {
+ needcheck= 1;
+}
+
static void blocksignals(int how) {
int r;
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGCHLD);
+ sigaddset(&set,SIGALRM);
r= sigprocmask(how,&set,0); assert(!r);
}
+static void NONRETURNING docheck(void) {
+#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 sigaction sig;
+ struct sockaddr_un ssockname;
+
+ openlog(USERVDCHECK_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
+
+ sigemptyset(&sig.sa_mask);
+ sig.sa_flags= 0;
+ sig.sa_handler= SIG_IGN;
+ if (sigaction(SIGPIPE,&sig,0)) { syslog(LOG_ERR,"ignore sigpipe"); exit(1); }
+
+ sig.sa_handler= SIG_DFL;
+ if (sigaction(SIGALRM,&sig,0)) { syslog(LOG_ERR,"default sigalarm"); exit(1); }
+
+ sfd= socket(AF_UNIX,SOCK_STREAM,0);
+ if (!sfd) { syslog(LOG_ERR,"ignore sigpipe"); exit(1); }
+
+ assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
+ ssockname.sun_family= AF_UNIX;
+ strcpy(ssockname.sun_path,RENDEZVOUS);
+
+ 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); }
+ syslog(LOG_ERR,"unable to connect to uservd daemon: %m"); exit(1);
+ }
+
+ r= alarm(USERVD_MYSELF_TIMEOUT);
+ if (r<0) { syslog(LOG_ERR,"set alarm for read: %m"); exit(1); }
+ remain= sizeof(opening_mbuf); p= (unsigned char*)&opening_mbuf;
+ while (remain) {
+ r= read(sfd,p,remain);
+ if (r<0) { syslog(LOG_ERR,"read from server: %m"); exit(1); }
+ if (r==0) { syslog(LOG_ERR,"unexpected EOF from server"); exit(1); }
+ remain-= r; p+= r;
+ }
+ if (opening_mbuf.magic != OPENING_MAGIC) {
+ syslog(LOG_NOTICE,"magic number mismatch");
+ exit(0);
+ }
+ if (memcmp(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE)) {
+ syslog(LOG_NOTICE,"protocol checksum mismatch");
+ exit(0);
+ }
+ if (opening_mbuf.overlordpid != overlordpid) {
+ syslog(LOG_NOTICE,"overlord pid mismatch");
+ exit(0);
+ }
+ syslog(LOG_NOTICE,"check - same daemon still running");
+#endif
+ exit(1);
+}
+
int main(int argc, char *const *argv) {
int mfd, sfd, csocklen, e;
- struct sigaction childact;
+ struct sigaction sigact;
struct sockaddr_un ssockname, csockname;
pid_t child;
if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
- openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
+ openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
if (chdir(VARDIR)) { syslog(LOG_CRIT,"cannot change to " VARDIR ": %m"); exit(4); }
checkstalepipes();
+ overlordpid= getpid();
+ if (overlordpid==-1) { syslog(LOG_CRIT,"cannot getpid: %m"); exit(4); }
+
mfd= socket(AF_UNIX,SOCK_STREAM,0);
if (mfd<0) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
if (listen(mfd,5))
{ syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
- childact.sa_handler= sighandler_chld;
- sigemptyset(&childact.sa_mask);
- childact.sa_flags= SA_NOCLDSTOP;
- if (sigaction(SIGCHLD,&childact,0))
+ sigemptyset(&sigact.sa_mask);
+ sigaddset(&sigact.sa_mask,SIGCHLD);
+ sigaddset(&sigact.sa_mask,SIGALRM);
+ 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); }
+
+ sigact.sa_handler= sighandler_alrm;
+ if (sigaction(SIGALRM,&sigact,0))
+ { syslog(LOG_CRIT,"cannot setup sigalrm handler: %m"); exit(4); }
+
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();
+ needcheck= 0;
+ }
csocklen= sizeof(csockname);
blocksignals(SIG_UNBLOCK);
sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);