chiark / gitweb /
Moved xmalloc etc. into both.c
[userv.git] / overlord.c
1 /*
2  * userv - overlord.c
3  * daemon main program, collects request and forks handlers
4  *
5  * Copyright (C)1996-1997,1999 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/stat.h>
33 #include <sys/socket.h>
34 #include <fcntl.h>
35 #include <time.h>
36 #include <dirent.h>
37 #include <sys/un.h>
38
39 #include "config.h"
40 #include "common.h"
41 #include "daemon.h"
42
43 pid_t overlordpid;
44
45 static pid_t checkpid= -1, detachpid= -1;
46 static sig_atomic_t needcheck= 1; /* 2 means we half-expect the server to be down */
47
48 static void checkstalepipes(void) {
49   /* There is an unimportant race here.  If there is a stale pipe but
50    * another pair of processes with the same pids is about to create a
51    * new one we can check that the pipe is stale before they recreate
52    * it but then only remove it afterwards; then we remove the pipe
53    * they're actually using causing that invocation to fail with
54    * ENOENT on the pipe.  However, this can only happen if things are
55    * already shafted, because we check for stale pipes at startup
56    * before any children have been started, and then only when a child
57    * dies unhelpfully - and we actually have to have some stale pipes
58    * for the race to exist.
59    */
60   DIR *dir;
61   struct dirent *de;
62   struct stat stab;
63   time_t now;
64   unsigned long timediff;
65   int r;
66   
67   if (time(&now) == -1) { syslog(LOG_ERR,"get current time: %m"); return; }
68   dir= opendir(".");
69   if (!dir) { syslog(LOG_ERR,"open directory " VARDIR ": %m"); return; }
70   while ((de= readdir(dir))) {
71     if (fnmatch(PIPEPATTERN,de->d_name,FNM_PATHNAME|FNM_PERIOD)) continue;
72     r= lstat(de->d_name,&stab); if (r && errno==ENOENT) continue;
73     if (r) { syslog(LOG_ERR,"could not stat `" VARDIR "/%s': %m",de->d_name); continue; }
74     timediff= (unsigned long)now - (unsigned long)stab.st_ctime;
75     if (timediff >= (~0UL>>1) || timediff < 3600) continue;
76     if (unlink(de->d_name) && errno!=ENOENT)
77       syslog(LOG_ERR,"could not remove stale pipe `%s': %m",de->d_name);
78   }
79   if (closedir(dir)) syslog(LOG_ERR,"close directory " VARDIR ": %m");
80 }
81
82 static void sighandler_chld(int x) {
83   pid_t r;
84   int status, es, ar;
85
86   es= errno;
87   for (;;) {
88     r= waitpid((pid_t)-1,&status,WNOHANG);
89     if (!r || (r==-1 && errno==ECHILD)) break;
90     if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
91     if (r==detachpid) {
92       if (WIFEXITED(status) && WEXITSTATUS(status)==4) _exit(4);
93       fprintf(stderr,"uservd: detaching child failed with unexpected code %d\n",status);
94       exit(6);
95     }
96     if (r==checkpid) {
97       if (WIFEXITED(status)) {
98         if (!WEXITSTATUS(status)) {
99           syslog(LOG_WARNING,"no longer the uservd - exiting");
100           _exit(2);
101         } else if (WEXITSTATUS(status)!=1) {
102           syslog(LOG_ERR,"check pid %ld exited with status %d",
103                  (long)checkpid,WEXITSTATUS(status));
104         }
105       } else if (WIFSIGNALED(status)) {
106         if (WTERMSIG(status) == SIGALRM && !WCOREDUMP(status)) {
107           syslog(LOG_WARNING,"check timed out; no longer the uservd - exiting");
108           _exit(2);
109         } else {
110           syslog(LOG_ERR,"check pid %ld %s due to signal %s",
111                  (long)checkpid,
112                  WCOREDUMP(status) ? "dumped core" : "died",
113                  strsignal(WTERMSIG(status)));
114         }
115       } else {
116         syslog(LOG_ERR,"check pid %ld died due to unknown reason, code %d",
117                (long)checkpid,status);
118       }
119       checkpid= -1;
120       ar= alarm(USERVD_MYSELF_CHECK);
121       if (ar<0) { syslog(LOG_CRIT,"set alarm for next check: %m"); exit(5); }
122     } else {
123       if (WIFSIGNALED(status)) {
124         syslog(LOG_ERR,"call pid %ld %s due to signal %s",
125                (long)r,
126                WCOREDUMP(status) ? "dumped core" : "died",
127                strsignal(WTERMSIG(status)));
128       } else if (!WIFEXITED(status)) {
129         syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %d",
130                (long)r,status);
131       } else if (WEXITSTATUS(status)==10) {
132         needcheck= 2;
133       } else if (WEXITSTATUS(status)>12) {
134         if (WEXITSTATUS(status)>24)
135           syslog(LOG_ERR,"call pid %ld exited with status %d >24",
136                  (long)r,WEXITSTATUS(status));
137         checkstalepipes();
138         needcheck= 1;
139       }
140     }
141   }
142   errno= es;
143   return;
144 }
145
146 static void sighandler_usr1(int x) {
147   _exit(0);
148 }
149
150 static void sighandler_alrm(int x) {
151   needcheck= 1;
152 }
153
154 static void sighandler_termint(int sig) {
155   syslog(LOG_NOTICE,"terminating due to signal %s",strsignal(sig));
156   _exit(1);
157 }
158
159 static void blocksignals(int how) {
160   int r;
161   sigset_t set;
162
163   sigemptyset(&set);
164   sigaddset(&set,SIGCHLD);
165   sigaddset(&set,SIGALRM);
166   sigaddset(&set,SIGTERM);
167   sigaddset(&set,SIGINT);
168   r= sigprocmask(how,&set,0); assert(!r);
169 }
170
171 static void NONRETURNING docheck(int needwanted) {
172 #ifndef DEBUG
173   /* This subprocess exits with status 0 if the parent should die,
174    * 1 if it should not, and something else if it fails horribly.
175    */
176   int sfd, r, remain;
177   unsigned char *p;
178   struct opening_msg opening_mbuf;
179   struct request_msg request_mbuf;
180   unsigned long endmagic;
181   struct sigaction sig;
182   struct sockaddr_un ssockname;
183
184   openlog(USERVDCHECK_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
185
186   sigemptyset(&sig.sa_mask);
187   sig.sa_flags= 0;
188   sig.sa_handler= SIG_IGN;
189   if (sigaction(SIGPIPE,&sig,0)) { syslog(LOG_ERR,"ignore sigpipe"); exit(1); }
190
191   sig.sa_handler= SIG_DFL;
192   if (sigaction(SIGALRM,&sig,0)) { syslog(LOG_ERR,"default sigalarm"); exit(1); }
193
194   sfd= socket(AF_UNIX,SOCK_STREAM,0);
195   if (!sfd) { syslog(LOG_ERR,"ignore sigpipe"); exit(1); }
196
197   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
198   ssockname.sun_family= AF_UNIX;
199   strcpy(ssockname.sun_path,RENDEZVOUS);
200
201   r= connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname));
202   if (r) {
203     if (errno == ECONNREFUSED || errno == ENOENT) {
204       if (needwanted != 2)
205         syslog(LOG_WARNING,"real uservd daemon is not running: %m");
206       exit(0);
207     }
208     syslog(LOG_ERR,"unable to connect to uservd daemon: %m"); exit(1);
209   }
210
211   r= alarm(USERVD_MYSELF_TIMEOUT);
212   if (r<0) { syslog(LOG_ERR,"set alarm for read: %m"); exit(1); }
213   remain= sizeof(opening_mbuf); p= (unsigned char*)&opening_mbuf;
214   while (remain) {
215     r= read(sfd,p,remain);
216     if (r<0) { syslog(LOG_ERR,"read from server: %m"); exit(1); }
217     if (r==0) { syslog(LOG_ERR,"unexpected EOF from server"); exit(1); }
218     remain-= r; p+= r;
219   }
220   if (opening_mbuf.magic != OPENING_MAGIC) {
221     syslog(LOG_WARNING,"magic number mismatch");
222     exit(0);
223   }
224   if (memcmp(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE)) {
225     syslog(LOG_WARNING,"protocol checksum mismatch");
226     exit(0);
227   }
228   if (opening_mbuf.overlordpid != overlordpid) {
229     syslog(LOG_WARNING,"overlord pid mismatch");
230     exit(0);
231   }
232   memset(&request_mbuf,0,sizeof(request_mbuf));
233   request_mbuf.magic= REQUEST_MAGIC;
234   request_mbuf.clientpid= -1;
235   request_mbuf.serviceuserlen= 0;
236   request_mbuf.servicelen= 0;
237   request_mbuf.loginnamelen= 0;
238   request_mbuf.spoofed= 0;
239   request_mbuf.cwdlen= 0;
240   request_mbuf.overridelen= -1;
241   request_mbuf.callinguid= -1;
242   request_mbuf.ngids= 0;
243   request_mbuf.nreadfds= 0;
244   request_mbuf.nwritefds= 0;
245   request_mbuf.nargs= 0;
246   request_mbuf.nvars= 0;
247   r= write(sfd,&request_mbuf,sizeof(request_mbuf));
248   if (r==sizeof(request_mbuf)) {
249     endmagic= REQUEST_END_MAGIC;
250     write(sfd,&endmagic,sizeof(endmagic));
251   }
252   syslog(LOG_NOTICE,"uservd[%ld] is running",(long)overlordpid);
253 #endif
254   exit(1);
255 }
256
257 static void NONRETURNING startupsyscallerr(const char *what) {
258   fprintf(stderr,
259           "uservd: system call failed during startup:\n"
260           "uservd: %s: %s\n",
261           what,strerror(errno));
262   exit(4);
263 }
264
265 int main(int argc, char *const *argv) {
266   int mfd, sfd, nfd, csocklen, e, r, becomedaemon;
267   struct sigaction sigact;
268   struct sockaddr_un ssockname, csockname;
269   pid_t child, parentpid, sid;
270
271 #ifdef NDEBUG
272   abort(); /* Do not disable assertions in this security-critical code ! */
273 #endif
274
275   becomedaemon= 0;
276   
277   if (argv[1] && !strcmp(argv[1],"-daemon")) {
278     becomedaemon= 1;
279     argv++; argc--;
280   }
281   if (argc>1) { fputs("usage: uservd [-daemon]\n",stderr); exit(3); }
282
283   openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,USERVD_LOGFACILITY);
284
285   if (chdir(VARDIR)) startupsyscallerr("cannot change to " VARDIR);
286   checkstalepipes();
287
288   overlordpid= parentpid= getpid();
289   if (parentpid==-1) startupsyscallerr("cannot getpid");
290
291   mfd= socket(AF_UNIX,SOCK_STREAM,0);
292   if (mfd<0) startupsyscallerr("cannot create master socket");
293
294   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUS));
295   ssockname.sun_family= AF_UNIX;
296   strcpy(ssockname.sun_path,RENDEZVOUS);
297   unlink(RENDEZVOUS);
298   r= bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname));
299   if (r) startupsyscallerr("cannot bind master socket");
300   if (listen(mfd,5)) startupsyscallerr("cannot listen on master socket");
301
302   sigemptyset(&sigact.sa_mask);
303   sigaddset(&sigact.sa_mask,SIGCHLD);
304   sigaddset(&sigact.sa_mask,SIGALRM);
305   sigact.sa_flags= SA_NOCLDSTOP;
306
307   sigact.sa_handler= sighandler_chld;
308   if (sigaction(SIGCHLD,&sigact,0)) startupsyscallerr("cannot setup sigchld handler");
309
310   sigact.sa_handler= sighandler_alrm;
311   if (sigaction(SIGALRM,&sigact,0)) startupsyscallerr("cannot setup sigalrm handler");
312
313   if (becomedaemon) {
314     sigact.sa_handler= sighandler_usr1;
315     if (sigaction(SIGUSR1,&sigact,0)) startupsyscallerr("cannot setup sigusr1 handler");
316     
317     detachpid= fork(); if (detachpid==-1) startupsyscallerr("cannot fork to detach");
318     if (detachpid) {
319       pause();
320       fputs("uservd: pause unexpectedly returned during detach\n",stderr);
321       exit(4);
322     }
323     sigact.sa_handler= SIG_DFL;
324     if (sigaction(SIGUSR1,&sigact,0)) startupsyscallerr("cannot restore sigusr1");
325   }
326
327   sigact.sa_handler= sighandler_termint;
328   if (sigaction(SIGTERM,&sigact,0)) startupsyscallerr("cannot setup sigterm handler");
329   if (sigaction(SIGINT,&sigact,0)) startupsyscallerr("cannot setup sigint handler");
330
331   if (becomedaemon) {
332     nfd= open("/dev/null",O_RDWR);
333     if (nfd<0) startupsyscallerr("cannot open /dev/null");
334     sid= setsid(); if (sid == -1) startupsyscallerr("cannot create new session");
335     overlordpid= getpid();
336     if (overlordpid == -1) startupsyscallerr("getpid after detach");
337     if (dup2(nfd,0)<0 || dup2(nfd,1)<0)
338       startupsyscallerr("cannot dup /dev/null for stdin/out");
339     r= kill(parentpid,SIGUSR1); if (r) startupsyscallerr("send SIGUSR1 to detach");
340     r= dup2(nfd,2);
341     if (r<0) { syslog(LOG_CRIT,"cannot dup /dev/null for stderr: %m"); exit(5); }
342     close(nfd);
343   }
344
345   syslog(LOG_NOTICE,"started");
346
347   for (;;) {
348     if (needcheck) {
349       while (checkpid==-1) {
350         checkpid= fork();
351         if (checkpid!=-1) {
352           if (!checkpid) docheck(needcheck);
353           break;
354         } else if (errno==EAGAIN) {
355           syslog(LOG_ERR,"fork for check - will wait and retry: %m");
356           r= alarm(USERVD_CHECKFORK_RETRY);
357           if (r<0) { syslog(LOG_CRIT,"set alarm for retry check: %m"); exit(5); }
358           break;
359         } else if (errno!=EINTR) {
360           syslog(LOG_CRIT,"fork for check: %m"); exit(5);
361         }
362       }
363       needcheck= 0;
364     }
365     csocklen= sizeof(csockname);
366     blocksignals(SIG_UNBLOCK);
367     sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
368     e= errno;
369     blocksignals(SIG_BLOCK);
370     if (sfd<0) {
371       errno= e;
372       if (errno == EINTR) continue;
373       if (errno == ENOMEM || errno == EPROTO || errno == EAGAIN) {
374         syslog(LOG_ERR,"unable to accept connection: %m");
375         continue;
376       } else {
377         syslog(LOG_CRIT,"unable to accept new connections: %m");
378         exit(5);
379       }
380     }
381     child= nondebug_fork();
382     if (child == (pid_t)-1) {
383       syslog(LOG_ERR,"unable to fork server: %m");
384       close(sfd);
385       continue;
386     }
387     if (!child) {
388       close(mfd);
389       closelog();
390       blocksignals(SIG_UNBLOCK);
391       servicerequest(sfd);
392     }
393     close(sfd);
394   }
395 }