X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=userv.git;a=blobdiff_plain;f=overlord.c;h=2ae825021c67f6e975ed0476688da2fe6cda6da8;hp=e32c9a10db0e2757cef1891af07a76dc1b21b81d;hb=5c38f6a9b5774073832e2b483b0c01b4f3261cb5;hpb=2c8651e76d5629948778fb1b3d15ccc591d14da0 diff --git a/overlord.c b/overlord.c index e32c9a1..2ae8250 100644 --- a/overlord.c +++ b/overlord.c @@ -2,7 +2,7 @@ * 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 @@ -25,13 +25,15 @@ #include #include #include +#include #include #include #include #include -#include #include #include +#include +#include #include #include @@ -41,8 +43,8 @@ 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 @@ -66,7 +68,7 @@ static void checkstalepipes(void) { if (time(&now) == -1) { syslog(LOG_ERR,"get current time: %m"); return; } dir= opendir("."); if (!dir) { syslog(LOG_ERR,"open directory " VARDIR ": %m"); return; } - while ((de= readdir(dir))) { + while ((errno=0, de= readdir(dir))) { if (fnmatch(PIPEPATTERN,de->d_name,FNM_PATHNAME|FNM_PERIOD)) continue; r= lstat(de->d_name,&stab); if (r && errno==ENOENT) continue; if (r) { syslog(LOG_ERR,"could not stat `" VARDIR "/%s': %m",de->d_name); continue; } @@ -75,31 +77,37 @@ static void checkstalepipes(void) { if (unlink(de->d_name) && errno!=ENOENT) syslog(LOG_ERR,"could not remove stale pipe `%s': %m",de->d_name); } + if (errno) syslog(LOG_ERR,"read directory " VARDIR ": %m"); if (closedir(dir)) syslog(LOG_ERR,"close directory " VARDIR ": %m"); } static void sighandler_chld(int x) { pid_t r; - int status, es, ar; + int status, es; 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 (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, @@ -111,8 +119,7 @@ static void sighandler_chld(int x) { (long)checkpid,status); } checkpid= -1; - ar= alarm(USERVD_MYSELF_CHECK); - if (ar<0) { syslog(LOG_CRIT,"set alarm for next check: %m"); exit(5); } + alarm(USERVD_MYSELF_CHECK); } else { if (WIFSIGNALED(status)) { syslog(LOG_ERR,"call pid %ld %s due to signal %s", @@ -122,11 +129,14 @@ static void sighandler_chld(int x) { } 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; } } } @@ -134,10 +144,19 @@ static void sighandler_chld(int x) { 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; @@ -145,10 +164,12 @@ static void blocksignals(int how) { 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. @@ -156,6 +177,8 @@ static void NONRETURNING docheck(void) { 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; @@ -178,13 +201,15 @@ static void NONRETURNING docheck(void) { 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); } - r= alarm(USERVD_MYSELF_TIMEOUT); - if (r<0) { syslog(LOG_ERR,"set alarm for read: %m"); exit(1); } + alarm(USERVD_MYSELF_TIMEOUT); remain= sizeof(opening_mbuf); p= (unsigned char*)&opening_mbuf; while (remain) { r= read(sfd,p,remain); @@ -193,53 +218,87 @@ static void NONRETURNING docheck(void) { 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, e, r, becomedaemon; + socklen_t csocklen; 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); @@ -247,20 +306,60 @@ int main(int argc, char *const *argv) { 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"); + alarm(USERVD_CHECKFORK_RETRY); + break; + } else if (errno!=EINTR) { + syslog(LOG_CRIT,"fork for check: %m"); exit(5); + } + } needcheck= 0; } csocklen= sizeof(csockname);