chiark / gitweb /
e32c9a10db0e2757cef1891af07a76dc1b21b81d
[userv.git] / overlord.c
1 /*
2  * userv - overlord.c
3  * daemon main program, collects request and forks handlers
4  *
5  * Copyright (C)1996-1997 Ian Jackson
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with userv; if not, write to the Free Software
19  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #include <unistd.h>
23 #include <signal.h>
24 #include <syslog.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <fnmatch.h>
30 #include <sys/wait.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/stat.h>
34 #include <sys/socket.h>
35 #include <dirent.h>
36 #include <sys/un.h>
37
38 #include "config.h"
39 #include "common.h"
40 #include "daemon.h"
41
42 pid_t overlordpid;
43
44 static pid_t checkpid= -1;
45 static sig_atomic_t needcheck= 1;
46
47 static void checkstalepipes(void) {
48   /* There is an unimportant race here.  If there is a stale pipe but
49    * another pair of processes with the same pids is about to create a
50    * new one we can check that the pipe is stale before they recreate
51    * it but then only remove it afterwards; then we remove the pipe
52    * they're actually using causing that invocation to fail with
53    * ENOENT on the pipe.  However, this can only happen if things are
54    * already shafted, because we check for stale pipes at startup
55    * before any children have been started, and then only when a child
56    * dies unhelpfully - and we actually have to have some stale pipes
57    * for the race to exist.
58    */
59   DIR *dir;
60   struct dirent *de;
61   struct stat stab;
62   time_t now;
63   unsigned long timediff;
64   int r;
65   
66   if (time(&now) == -1) { syslog(LOG_ERR,"get current time: %m"); return; }
67   dir= opendir(".");
68   if (!dir) { syslog(LOG_ERR,"open directory " VARDIR ": %m"); return; }
69   while ((de= readdir(dir))) {
70     if (fnmatch(PIPEPATTERN,de->d_name,FNM_PATHNAME|FNM_PERIOD)) continue;
71     r= lstat(de->d_name,&stab); if (r && errno==ENOENT) continue;
72     if (r) { syslog(LOG_ERR,"could not stat `" VARDIR "/%s': %m",de->d_name); continue; }
73     timediff= (unsigned long)now - (unsigned long)stab.st_ctime;
74     if (timediff >= (~0UL>>1) || timediff < 3600) continue;
75     if (unlink(de->d_name) && errno!=ENOENT)
76       syslog(LOG_ERR,"could not remove stale pipe `%s': %m",de->d_name);
77   }
78   if (closedir(dir)) syslog(LOG_ERR,"close directory " VARDIR ": %m");
79 }
80
81 static void sighandler_chld(int x) {
82   pid_t r;
83   int status, es, ar;
84
85   es= errno;
86   for (;;) {
87     r= waitpid((pid_t)-1,&status,WNOHANG);
88     if (!r || (r==-1 && errno==ECHILD)) break;
89     if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
90     if (r==checkpid) {
91       if (WIFEXITED(status)) {
92         if (!WEXITSTATUS(status)) {
93           syslog(LOG_NOTICE,"no longer the uservd - exiting");
94           _exit(0);
95         } else if (WEXITSTATUS(status)!=1) {
96           syslog(LOG_ERR,"check pid %ld exited with status %d",
97                  (long)checkpid,WEXITSTATUS(status));
98         }
99       } else if (WIFSIGNALED(status)) {
100         if (WTERMSIG(status) == SIGALRM && !WCOREDUMP(status)) {
101           syslog(LOG_NOTICE,"check timed out; no longer the uservd - exiting");
102           _exit(0);
103         } else {
104           syslog(LOG_ERR,"check pid %ld %s due to signal %s",
105                  (long)checkpid,
106                  WCOREDUMP(status) ? "dumped core" : "died",
107                  strsignal(WTERMSIG(status)));
108         }
109       } else {
110         syslog(LOG_ERR,"check pid %ld died due to unknown reason, code %d",
111                (long)checkpid,status);
112       }
113       checkpid= -1;
114       ar= alarm(USERVD_MYSELF_CHECK);
115       if (ar<0) { syslog(LOG_CRIT,"set alarm for next check: %m"); exit(5); }
116     } else {
117       if (WIFSIGNALED(status)) {
118         syslog(LOG_ERR,"call pid %ld %s due to signal %s",
119                (long)r,
120                WCOREDUMP(status) ? "dumped core" : "died",
121                strsignal(WTERMSIG(status)));
122       } else if (!WIFEXITED(status)) {
123         syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %d",
124                (long)r,status);
125       } else if (WEXITSTATUS(status)>12) {
126         if (WEXITSTATUS(status)>24)
127           syslog(LOG_ERR,"call pid %ld exited with status %d >24",
128                  (long)r,WEXITSTATUS(status));
129         checkstalepipes();
130       }
131     }
132   }
133   errno= es;
134   return;
135 }
136
137 static void sighandler_alrm(int x) {
138   needcheck= 1;
139 }
140
141 static void blocksignals(int how) {
142   int r;
143   sigset_t set;
144
145   sigemptyset(&set);
146   sigaddset(&set,SIGCHLD);
147   sigaddset(&set,SIGALRM);
148   r= sigprocmask(how,&set,0); assert(!r);
149 }
150
151 static void NONRETURNING docheck(void) {
152 #ifndef DEBUG
153   /* This subprocess exits with status 0 if the parent should die,
154    * 1 if it should not, and something else if it fails horribly.
155    */
156   int sfd, r, remain;
157   unsigned char *p;
158   struct opening_msg opening_mbuf;
159   struct sigaction sig;
160   struct sockaddr_un ssockname;
161
162   openlog(USERVDCHECK_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
163
164   sigemptyset(&sig.sa_mask);
165   sig.sa_flags= 0;
166   sig.sa_handler= SIG_IGN;
167   if (sigaction(SIGPIPE,&sig,0)) { syslog(LOG_ERR,"ignore sigpipe"); exit(1); }
168
169   sig.sa_handler= SIG_DFL;
170   if (sigaction(SIGALRM,&sig,0)) { syslog(LOG_ERR,"default sigalarm"); exit(1); }
171
172   sfd= socket(AF_UNIX,SOCK_STREAM,0);
173   if (!sfd) { syslog(LOG_ERR,"ignore sigpipe"); exit(1); }
174
175   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
176   ssockname.sun_family= AF_UNIX;
177   strcpy(ssockname.sun_path,RENDEZVOUS);
178
179   r= connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname));
180   if (r) {
181     if (errno == ECONNREFUSED || errno == ENOENT)
182       { syslog(LOG_NOTICE,"uservd daemon is not running: %m"); exit(0); }
183     syslog(LOG_ERR,"unable to connect to uservd daemon: %m"); exit(1);
184   }
185
186   r= alarm(USERVD_MYSELF_TIMEOUT);
187   if (r<0) { syslog(LOG_ERR,"set alarm for read: %m"); exit(1); }
188   remain= sizeof(opening_mbuf); p= (unsigned char*)&opening_mbuf;
189   while (remain) {
190     r= read(sfd,p,remain);
191     if (r<0) { syslog(LOG_ERR,"read from server: %m"); exit(1); }
192     if (r==0) { syslog(LOG_ERR,"unexpected EOF from server"); exit(1); }
193     remain-= r; p+= r;
194   }
195   if (opening_mbuf.magic != OPENING_MAGIC) {
196     syslog(LOG_NOTICE,"magic number mismatch");
197     exit(0);
198   }
199   if (memcmp(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE)) {
200     syslog(LOG_NOTICE,"protocol checksum mismatch");
201     exit(0);
202   }
203   if (opening_mbuf.overlordpid != overlordpid) {
204     syslog(LOG_NOTICE,"overlord pid mismatch");
205     exit(0);
206   }
207   syslog(LOG_NOTICE,"check - same daemon still running");
208 #endif
209   exit(1);
210 }
211
212 int main(int argc, char *const *argv) {
213   int mfd, sfd, csocklen, e;
214   struct sigaction sigact;
215   struct sockaddr_un ssockname, csockname;
216   pid_t child;
217
218 #ifdef NDEBUG
219   abort(); /* Do not disable assertions in this security-critical code ! */
220 #endif
221
222   if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
223
224   openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
225
226   if (chdir(VARDIR)) { syslog(LOG_CRIT,"cannot change to " VARDIR ": %m"); exit(4); }
227   checkstalepipes();
228
229   overlordpid= getpid();
230   if (overlordpid==-1) { syslog(LOG_CRIT,"cannot getpid: %m"); exit(4); }
231
232   mfd= socket(AF_UNIX,SOCK_STREAM,0);
233   if (mfd<0) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
234
235   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
236   ssockname.sun_family= AF_UNIX;
237   strcpy(ssockname.sun_path,RENDEZVOUS);
238   unlink(RENDEZVOUS);
239   if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
240     { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
241   if (listen(mfd,5))
242     { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
243
244   sigemptyset(&sigact.sa_mask);
245   sigaddset(&sigact.sa_mask,SIGCHLD);
246   sigaddset(&sigact.sa_mask,SIGALRM);
247   sigact.sa_flags= SA_NOCLDSTOP;
248
249   sigact.sa_handler= sighandler_chld;
250   if (sigaction(SIGCHLD,&sigact,0))
251     { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
252
253   sigact.sa_handler= sighandler_alrm;
254   if (sigaction(SIGALRM,&sigact,0))
255     { syslog(LOG_CRIT,"cannot setup sigalrm handler: %m"); exit(4); }
256
257   syslog(LOG_NOTICE,"started");
258   for (;;) {
259     if (needcheck) {
260       assert(checkpid==-1);
261       checkpid= fork();
262       if (checkpid==-1) { syslog(LOG_CRIT,"fork for check: %m"); exit(5); }
263       if (!checkpid) docheck();
264       needcheck= 0;
265     }
266     csocklen= sizeof(csockname);
267     blocksignals(SIG_UNBLOCK);
268     sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
269     e= errno;
270     blocksignals(SIG_BLOCK);
271     if (sfd<0) {
272       errno= e;
273       if (errno == EINTR) continue;
274       if (errno == ENOMEM || errno == EPROTO || errno == EAGAIN) {
275         syslog(LOG_ERR,"unable to accept connection: %m");
276         continue;
277       } else {
278         syslog(LOG_CRIT,"unable to accept new connections: %m");
279         exit(5);
280       }
281     }
282     child= nondebug_fork();
283     if (child == (pid_t)-1) {
284       syslog(LOG_ERR,"unable to fork server: %m");
285       close(sfd);
286       continue;
287     }
288     if (!child) {
289       close(mfd);
290       closelog();
291       blocksignals(SIG_UNBLOCK);
292       servicerequest(sfd);
293     }
294     close(sfd);
295   }
296 }