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