chiark / gitweb /
Fix pcsum.h.new.
[userv.git] / daemon.c
1 /*
2  * userv - daemon.c
3  * daemon main program
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 <stdio.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <wait.h>
26 #include <assert.h>
27 #include <signal.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <syslog.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/types.h>
35 #include <sys/fcntl.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/un.h>
39 #include <limits.h>
40 #include <ctype.h>
41
42 #include "config.h"
43 #include "common.h"
44 #include "daemon.h"
45 #include "lib.h"
46 #include "tokens.h"
47
48 /* NB: defaults for the execution state are not set here, but in
49  * the RESET_CONFIGURATION #define in daemon.h. */
50 gid_t *gidarray=0;
51 char **argarray=0;
52 char *((*defvararray)[2])=0;
53 struct fdstate *fdarray=0;
54 int fdarraysize=0, fdarrayused=0;
55 int restfdwantstate= tokv_word_rejectfd, restfdwantrw= 0;
56 struct request_msg request_mbuf;
57 char *serviceuser=0, *service=0, *logname=0, *cwd=0;
58 char *overridedata=0, *userrcfile=0;
59 char *serviceuser_dir=0, *serviceuser_shell=0, *callinguser_shell;
60 uid_t serviceuser_uid=-1;
61 gid_t serviceuser_gid=-1;
62 char *execpath=0, **execargs=0;
63 int execute, setenvironment, suppressargs, disconnecthup, ehandling;
64 int ehlogfacility=0, ehloglevel=0, ehfilekeep=0, syslogopenfacility=-1;
65 FILE *ehfile=0;
66 char *ehfilename=0;
67
68 static FILE *swfile= 0, *srfile= 0;
69 static pid_t child= -1, childtokill= -1;
70 static const char **grouparray;
71
72 static void sigchildhandler(int x) {
73   pid_t r;
74   int status, es;
75
76   es= errno;
77   for (;;) {
78     r= waitpid((pid_t)-1,&status,WNOHANG);
79     if (!r || (r==-1 && errno==ECHILD)) break;
80     if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
81     if (WIFSIGNALED(status))
82       if (WCOREDUMP(status))
83         syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
84                strsignal(WTERMSIG(status)));
85       else
86         syslog(LOG_ERR,"call pid %ld died due to signal %s",
87                (long)r,strsignal(WTERMSIG(status)));
88     else if (!WIFEXITED(status))
89       syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
90              (long)r,status);
91     else if (WEXITSTATUS(status)>24)
92       syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
93              (long)r,WEXITSTATUS(status));
94   }
95   errno= es;
96   return;
97 }
98
99 static void xfread(void *p, size_t sz) {
100   size_t nr;
101   nr= fread(p,1,sz,srfile); if (nr == sz) return;
102   if (ferror(srfile)) syscallerror("reading from client");
103   assert(feof(srfile));
104   syslog(LOG_DEBUG,"client went away (unexpected EOF)");
105   swfile= 0;
106   disconnect(12);
107 }
108
109 static void xfwrite(const void *p, size_t sz, FILE *file) {
110   size_t nr;
111   nr= fwrite(p,1,sz,file); if (nr == sz) return;
112   syscallerror("writing to client");
113 }
114
115 static char *xfreadsetstring(int l) {
116   char *s;
117   s= xmalloc(l+1);
118   xfread(s,sizeof(*s)*l);
119   s[l]= 0;
120   return s;
121 }
122
123 static char *xfreadstring(void) {
124   int l;
125   xfread(&l,sizeof(l));
126   return xfreadsetstring(l);
127 }
128
129 static void xfflush(FILE *file) {
130   if (fflush(file)) syscallerror("flush client socket");
131 }
132
133 void ensurefdarray(int fd) {
134   if (fd < fdarrayused) return;
135   if (fd >= fdarraysize) {
136     fdarraysize= ((fd+2)<<1);
137     fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize);
138   }
139   while (fd >= fdarrayused) {
140     fdarray[fdarrayused].iswrite= -1;
141     fdarray[fdarrayused].realfd= -1;
142     fdarray[fdarrayused].wantstate= restfdwantstate;
143     fdarray[fdarrayused].wantrw= restfdwantrw;
144     fdarrayused++;
145   }
146 }
147
148 void ensurelogopen(int wantfacility) {
149   if (syslogopenfacility==wantfacility) return;
150   if (syslogopenfacility!=-1) closelog();
151   openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,wantfacility);
152   syslogopenfacility= wantfacility;
153 }
154
155 void senderrmsgstderr(const char *errmsg) {
156   struct progress_msg progress_mbuf;
157   unsigned long ul;
158   int l;
159
160   l= strlen(errmsg);
161   memset(&progress_mbuf,0,sizeof(progress_mbuf));
162   progress_mbuf.magic= PROGRESS_MAGIC;
163   progress_mbuf.type= pt_errmsg;
164   progress_mbuf.data.errmsg.messagelen= l;
165   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
166   xfwrite(errmsg,l,swfile);
167   ul= PROGRESS_ERRMSG_END_MAGIC;
168   xfwrite(&ul,sizeof(ul),swfile);
169   xfflush(swfile);
170 }
171
172 void miscerror(const char *what) {
173   syslog(LOG_ERR,"failure: %s",what);
174   disconnect(16);
175 }
176
177 void syscallerror(const char *what) {
178   int e;
179
180   e= errno;
181   syslog(LOG_ERR,"system call failure: %s: %s",what,strerror(e));
182   disconnect(18);
183 }
184
185 static void NONRETURNING generalfailure(const char *prefix, int reserveerrno,
186                                         int errnoval, const char *fmt, va_list al) {
187   char errmsg[MAX_ERRMSG_LEN];
188
189   if (prefix) {
190     strnycpy(errmsg,prefix,sizeof(errmsg));
191     strnytcat(errmsg,": ",sizeof(errmsg));
192   } else {
193     errmsg[0]= 0;
194   }
195   vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al);
196   if (reserveerrno) {
197     strnytcat(errmsg,": ",sizeof(errmsg));
198     strnytcat(errmsg,strerror(errnoval),sizeof(errmsg));
199   }
200   senderrmsgstderr(errmsg);
201   syslog(LOG_DEBUG,"service failed (%s)",errmsg);
202   disconnect(12);
203 }
204
205 static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) {
206   va_list al;
207
208   va_start(al,fmt);
209   generalfailure(0,0,0,fmt,al);
210 }  
211
212 static void NONRETURNPRINTFFORMAT(1,2) syscallfailure(const char *fmt, ...) {
213   va_list al;
214   int e;
215
216   e= errno;
217   va_start(al,fmt);
218   generalfailure("system call failed",ERRMSG_RESERVE_ERRNO,e,fmt,al);
219 }
220
221 void NONRETURNING disconnect(int exitstatus) {
222   /* This function can sometimes indirectly call itself (eg,
223    * xfwrite, syscallerror can cause it to be called).  So, all
224    * the global variables indicating need for action are reset
225    * before the action is taken so that if it fails it isn't
226    * attempted again.
227    */
228   struct progress_msg progress_mbuf;
229   FILE *swfilereal;
230   pid_t orgtokill;
231   int r;
232   
233   if (childtokill!=-1 && disconnecthup) {
234     orgtokill= childtokill;
235     childtokill= -1;
236     if (disconnecthup) {
237       r= kill(-orgtokill,SIGHUP);
238       if (r && errno!=EPERM && errno!=ESRCH)
239         syscallerror("sending SIGHUP to service process group");
240     }
241     child= -1;
242   }
243   if (swfile) {
244     swfilereal= swfile;
245     swfile= 0;
246     memset(&progress_mbuf,0,sizeof(progress_mbuf));
247     progress_mbuf.magic= PROGRESS_MAGIC;
248     progress_mbuf.type= pt_failed;
249     xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfilereal);
250     xfflush(swfilereal);
251   }
252
253   _exit(exitstatus);
254 }
255
256 static void NONRETURNING syscallservfail(const char *msg) {
257   fputs("uservd(service): ",stderr);
258   perror(msg);
259   _exit(-1);
260 }
261
262 static void servresetsig(int signo) {
263   struct sigaction sig;
264   
265   sig.sa_handler= SIG_DFL;
266   sigemptyset(&sig.sa_mask);
267   sig.sa_flags= 0;
268   if (sigaction(signo,&sig,0)) syscallservfail("reset signal handler");
269 }
270
271 static int synchread(int fd, int ch) {
272   char synchmsg;
273   int r;
274   
275   for (;;) {
276     r= read(fd,&synchmsg,1);
277     if (r==1) break;
278     if (r==0) { errno= ECONNRESET; return -1; }
279     assert(r<0);
280     if (errno!=EINTR) return -1;
281   };
282   if (synchmsg != ch) { errno= EPROTO; return -1; }
283   return 0;
284 }
285
286 static const char *see_logname(void) { return serviceuser; }
287 static const char *see_home(void) { return serviceuser_dir; }
288 static const char *see_shell(void) { return serviceuser_shell; }
289
290 static const char *see_path(void) {
291   return serviceuser_uid ?
292     "/usr/local/bin:/bin:/usr/bin" :
293     "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin";
294 }
295
296 static const char *see_service(void) { return service; }
297 static const char *see_c_cwd(void) { return cwd; }
298 static const char *see_c_logname(void) { return logname; }
299 static const char *see_c_uid(void) {
300   static char buf[CHAR_BIT*sizeof(uid_t)/3+4];
301   snyprintf(buf,sizeof(buf),"%lu",(unsigned long)request_mbuf.callinguid);
302   return buf;
303 }
304
305 static const char *see_c_list(int n, const char *(*fn)(int i)) {
306   int l, i;
307   char *r;
308   
309   for (i=0, l=1; i<n; i++) l+= strlen(fn(i))+1;
310   r= xmalloc(l); r[l-1]= '*';
311   for (i=0, *r=0; i<n; i++) snytprintfcat(r,l,"%s ",fn(i));
312   assert(!r[l-1] && r[l-2]==' ');
313   r[l-2]= 0;
314   return r;
315 }
316
317 static const char *seei_group(int i) {
318   return grouparray[i];
319 }
320 static const char *see_c_group(void) {
321   return see_c_list(request_mbuf.ngids,seei_group);
322 }
323
324 static const char *seei_gid(int i) {
325   static char buf[CHAR_BIT*sizeof(gid_t)/3+4];
326   snyprintf(buf,sizeof(buf),"%d",gidarray[i]);
327   return buf;
328 }
329 static const char *see_c_gid(void) {
330   return see_c_list(request_mbuf.ngids,seei_gid);
331 }
332
333 static const struct servenvinfo {
334   const char *name;
335   const char *(*fn)(void);
336 } servenvinfos[]= {
337   { "USER",           see_logname    },
338   { "LOGNAME",        see_logname    },
339   { "HOME",           see_home       },
340   { "SHELL",          see_shell      },
341   { "PATH",           see_path       },
342   { "USERV_SERVICE",  see_service    },
343   { "USERV_CWD",      see_c_cwd      },
344   { "USERV_USER",     see_c_logname  },
345   { "USERV_UID",      see_c_uid      },
346   { "USERV_GROUP",    see_c_group    },
347   { "USERV_GID",      see_c_gid      },
348   {  0                               }
349 };
350
351 static void NONRETURNING execservice(const int synchsocket[]) {
352   static const char *const setenvpfargs[]= {
353     "/bin/sh",
354     "-c",
355     ". " SYSTEMCONFIGDIR "/environment; exec \"$@\"",
356     "-",
357     0
358   };
359   int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l;
360   char *envvarbuf=0;
361   const char **args, *const *cpp;
362   char *const *pp;
363   char synchmsg;
364   const struct servenvinfo *sei;
365
366   if (dup2(fdarray[2].realfd,2)<0) {
367     static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n";
368     write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1);
369     _exit(-1);
370   }
371   if (close(synchsocket[0])) syscallservfail("close parent synch socket");
372
373   if (setpgid(0,0)) syscallservfail("set process group");
374   synchmsg= 'y';
375   r= write(synchsocket[1],&synchmsg,1);
376   if (r!=1) syscallservfail("write synch byte to parent");
377   r= synchread(synchsocket[1],'g');
378   if (r) syscallservfail("reach synch byte from parent");
379
380   if (close(fileno(swfile))) syscallservfail("close client socket fd");
381   for (fd=0; fd<fdarrayused; fd++) {
382     if (fdarray[fd].holdfd == -1) continue;
383     if (close(fdarray[fd].holdfd)) syscallservfail("close pipe hold fd");
384     fdarray[fd].holdfd= -1;
385   }
386   for (fd=0; fd<fdarrayused; fd++) {
387     if (fdarray[fd].realfd < fdarrayused) fdarray[fdarray[fd].realfd].holdfd= fd;
388   }
389   for (fd=0; fd<fdarrayused; fd++) {
390     realfd= fdarray[fd].realfd;
391     if (realfd == -1) continue;
392     holdfd= fdarray[fd].holdfd;
393     if (holdfd == fd) {
394       assert(realfd == fd);
395       fdarray[fd].holdfd= -1;
396       continue;
397     } else if (holdfd != -1) {
398       assert(fdarray[holdfd].realfd == fd);
399       newfd= dup(fd); if (newfd<0) syscallservfail("dup out of the way");
400       fdarray[holdfd].realfd= newfd;
401       if (newfd<fdarrayused) fdarray[newfd].holdfd= holdfd;
402       fdarray[fd].holdfd= -1;
403     }
404     if (dup2(fdarray[fd].realfd,fd)<0) syscallservfail("dup2 set up fd");
405     if (close(fdarray[fd].realfd)) syscallservfail("close old fd");
406     if (fcntl(fd,F_SETFD,0)<0) syscallservfail("set no-close-on-exec on fd");
407     fdarray[fd].realfd= fd;
408   }
409   servresetsig(SIGPIPE);
410   servresetsig(SIGCHLD);
411
412   for (sei= servenvinfos; sei->name; sei++)
413     if (setenv(sei->name,sei->fn(),1)) syscallservfail("setenv standard");
414   for (i=0; i<request_mbuf.nvars; i++) {
415     l= strlen(defvararray[i][0])+9;
416     if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
417     snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
418     if (setenv(envvarbuf,defvararray[i][1],1)) syscallservfail("setenv defvar");
419   }
420
421   nargs= 0;
422   if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++;
423   nargs++;
424   if (execargs) for (pp= execargs; *pp; pp++) nargs++;
425   if (!suppressargs) nargs+= request_mbuf.nargs;
426   args= xmalloc(sizeof(char*)*(nargs+1));
427   targ= 0;
428   if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp;
429   args[targ++]= execpath;
430   if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp;
431   if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
432   args[targ++]= 0;
433
434   execv(args[0],(char* const*)args);
435
436   syscallservfail("exec service program");
437   _exit(-1);
438 }
439
440 static void NONRETURNING sighandler_pipe(int ignored) {
441   swfile= 0;
442   ensurelogopen(USERVD_LOGFACILITY);
443   syslog(LOG_DEBUG,"client went away (server got sigpipe)");
444   disconnect(8);
445 }
446
447 static void NONRETURNING sighandler_chld(int ignored) {
448   struct progress_msg progress_mbuf;
449   int status;
450   pid_t returned;
451
452   returned= wait3(&status,WNOHANG,0);
453   if (returned==-1) syscallerror("wait for child failed");
454   if (!returned) syscallfailure("spurious sigchld");
455   if (returned!=child) syscallfailure("spurious child process (pid %ld)",(long)returned);
456   child= childtokill= -1;
457
458   memset(&progress_mbuf,0,sizeof(progress_mbuf));
459   progress_mbuf.magic= PROGRESS_MAGIC;
460   progress_mbuf.type= pt_terminated;
461   progress_mbuf.data.terminated.status= status;
462   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
463   xfflush(swfile);
464
465   syslog(LOG_DEBUG,"service completed (status %d %d)",(status>>8)&0x0ff,status&0x0ff);
466   _exit(0);
467 }
468
469 static void getevent(struct event_msg *event_r) {
470   int fd;
471   
472   for (;;) {
473     xfread(event_r,sizeof(struct event_msg));
474     switch (event_r->type) {
475     case et_closereadfd:
476       fd= event_r->data.closereadfd.fd;
477       assert(fd<fdarrayused);
478       assert(fdarray[fd].holdfd!=-1);
479       if (close(fdarray[fd].holdfd)) syscallfailure("cannot close holding fd %d",fd);
480       break;
481     case et_disconnect:
482       syslog(LOG_DEBUG,"client disconnected");
483       disconnect(4);
484     default:
485       return;
486     }
487   }
488 }
489
490 static void NONRETURNING servicerequest(int sfd) {
491   struct opening_msg opening_mbuf;
492   struct progress_msg progress_mbuf;
493   struct event_msg event_mbuf;
494   pid_t mypid, newchild;
495   unsigned long ul;
496   int i,j, r, tempfd, fd, partsize, synchsocket[2];
497   char pipepathbuf[PIPEPATHMAXLEN];
498   const char *string, *delim, *nextstring;
499   char *part, *exectry;
500   char synchmsg;
501   struct stat stab;
502   struct sigaction sig;
503   struct group *cgrp;
504   struct passwd *pw;
505
506   ensurelogopen(USERVD_LOGFACILITY);
507   syslog(LOG_DEBUG,"call connected");
508
509   mypid= getpid(); if (mypid == -1) syscallerror("getpid");
510
511   srfile= fdopen(sfd,"r");
512   if (!srfile) syscallerror("turn socket fd into reading FILE*");
513   if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
514
515   swfile= fdopen(sfd,"w");
516   if (!swfile) syscallerror("turn socket fd into writing FILE*");
517   if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
518
519   opening_mbuf.magic= OPENING_MAGIC;
520   memcpy(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE);
521   opening_mbuf.serverpid= mypid;
522   xfwrite(&opening_mbuf,sizeof(opening_mbuf),swfile);
523   xfflush(swfile);
524
525   xfread(&request_mbuf,sizeof(request_mbuf));
526   serviceuser= xfreadsetstring(request_mbuf.serviceuserlen);
527   service= xfreadsetstring(request_mbuf.servicelen);
528   logname= xfreadsetstring(request_mbuf.lognamelen);
529   cwd= xfreadsetstring(request_mbuf.cwdlen);
530   if (request_mbuf.overridelen >= 0) {
531     assert(request_mbuf.overridelen <= MAX_OVERRIDE_LEN);
532     overridedata= xfreadsetstring(request_mbuf.overridelen);
533   } else {
534     overridedata= 0;
535   }
536   gidarray= xmalloc(sizeof(gid_t)*request_mbuf.ngids);
537   xfread(gidarray,sizeof(gid_t)*request_mbuf.ngids);
538
539   fdarraysize= 4; fdarray= xmalloc(sizeof(struct fdstate)*fdarraysize);
540   fdarrayused= 1; fdarray[0].iswrite= -1;
541   fdarray[0].wantstate= tokv_word_rejectfd;
542   for (i=0; i<request_mbuf.nreadfds+request_mbuf.nwritefds; i++) {
543     xfread(&fd,sizeof(int));
544     ensurefdarray(fd);
545     assert(fdarray[fd].iswrite == -1);
546     fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
547   }
548
549   argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs));
550   for (i=0; i<request_mbuf.nargs; i++) argarray[i]= xfreadstring();
551   defvararray= xmalloc(sizeof(char*)*request_mbuf.nvars*2);
552   for (i=0; i<request_mbuf.nvars; i++)
553     for (j=0; j<2; j++) defvararray[i][j]= xfreadstring();
554   xfread(&ul,sizeof(ul));
555   assert(ul == REQUEST_END_MAGIC);
556
557   for (fd=0; fd<fdarrayused; fd++) {
558     if (fdarray[fd].iswrite == -1) continue;
559     sprintf(pipepathbuf, PIPEPATHFORMAT, (unsigned long)request_mbuf.clientpid,
560             (unsigned long)mypid, fd);
561     tempfd= open(pipepathbuf,O_RDWR);
562     if (tempfd == -1) syscallerror("prelim open pipe");
563     if (!fdarray[fd].iswrite) {
564       fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
565       if (fdarray[fd].holdfd == -1) syscallerror("hold open pipe");
566       fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
567     } else {
568       fdarray[fd].holdfd= -1;
569       fdarray[fd].realfd= open(pipepathbuf, O_WRONLY);
570     }
571     if (fdarray[fd].realfd == -1) syscallerror("real open pipe");
572     if (unlink(pipepathbuf)) syscallerror("unlink pipe");
573     if (close(tempfd)) syscallerror("close prelim fd onto pipe");
574   }
575
576   pw= getpwnam(serviceuser);
577   if (!pw) miscerror("look up service user");
578   assert(!strcmp(pw->pw_name,serviceuser));
579   serviceuser_dir= xstrdup(nondebug_serviceuserdir(pw->pw_dir));
580   serviceuser_shell= xstrdup(pw->pw_shell);
581   serviceuser_uid= pw->pw_uid;
582   serviceuser_gid= pw->pw_gid;
583   if (initgroups(pw->pw_name,pw->pw_gid)) syscallerror("initgroups");
584   if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 1");
585   if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 2");
586   if (pw->pw_uid)
587     if (!setreuid(pw->pw_uid,0)) miscerror("setreuid 3 unexpectedly succeeded");
588   if (errno != EPERM) syscallerror("setreuid 3 failed in unexpected way");
589
590   debug_dumprequest(mypid);
591
592   grouparray= xmalloc(sizeof(char*)*request_mbuf.ngids);
593   for (i=0; i<request_mbuf.ngids; i++) {
594     cgrp= getgrgid(gidarray[i]);
595     if (!cgrp) miscerror("get group entry for calling group");
596     grouparray[i]= xmstrsave(cgrp->gr_name);
597   }
598
599   if (overridedata) {
600     r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION,
601                     "<builtin toplevel override configuration>");
602   } else {
603     r= parse_string(TOPLEVEL_CONFIGURATION,
604                     "<builtin toplevel configuration>");
605   }
606
607   ensurelogopen(USERVD_LOGFACILITY);
608
609   if (r == tokv_error) failure("error encountered while parsing configuration files");
610   assert(r == tokv_quit);
611
612   debug_dumpexecsettings();
613   
614   switch (execute) {
615   case tokv_word_reject:
616     failure("request rejected");
617   case tokv_word_execute:
618     r= stat(execpath,&stab);
619     if (r) syscallfailure("checking for executable `%s'",execpath);
620     break;
621   case tokv_word_executefromdirectory:
622     r= stat(execpath,&stab);
623     if (r) syscallfailure("checking for executable in directory, `%s'",execpath);
624     break;
625   case tokv_word_executefrompath:
626     if (strchr(service,'/')) {
627       r= stat(service,&stab);
628       if (r) syscallfailure("execute-from-path (contains slash)"
629                             " cannot check for executable `%s'",service);
630       execpath= service;
631     } else {
632       string= getenv("PATH");
633       if (!string) failure("execute-from-path, but daemon inherited no PATH !");
634       while (string) {
635         delim= strchr(string,':');
636         if (delim) {
637           if (delim-string > INT_MAX)
638             failure("execute-from-path, but PATH component too long");
639           partsize= delim-string;
640           nextstring= delim+1;
641         } else {
642           partsize= strlen(string);
643           nextstring= 0;
644         }
645         part= xmstrsubsave(string,partsize);
646         exectry= part[0] ? xmstrcat3save(part,"/",service) : xmstrsave(service);
647         free(part);
648         r= stat(exectry,&stab);
649         if (!r) { execpath= exectry; break; }
650         free(exectry);
651         string= nextstring;
652       }
653       if (!execpath) failure("execute-from-path, but program `%s' not found",service);
654     }
655     break;
656   default:
657     abort();
658   }
659
660   assert(fdarrayused>=2);
661   if (!(fdarray[2].wantstate == tokv_word_requirefd ||
662         fdarray[2].wantstate == tokv_word_allowfd) ||
663       fdarray[2].wantrw != tokv_word_write)
664     failure("must have stderr (fd 2), but file descriptor setup in "
665             "configuration does not have it or not for writing");
666
667   for (fd=0; fd<fdarrayused; fd++) {
668     switch (fdarray[fd].wantstate) {
669     case tokv_word_rejectfd:
670       if (fdarray[fd].realfd != -1)
671         failure("file descriptor %d provided but rejected",fd);
672       break;
673     case tokv_word_ignorefd:
674       if (fdarray[fd].realfd != -1)
675         if (close(fdarray[fd].realfd))
676           syscallfailure("close unwanted file descriptor %d",fd);
677       fdarray[fd].realfd= -1;
678       break;
679     case tokv_word_nullfd:
680       if (fdarray[fd].realfd != -1) close(fdarray[fd].realfd);
681       fdarray[fd].realfd= open("/dev/null",
682                                fdarray[fd].iswrite == -1 ? O_RDWR :
683                                fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
684       if (fdarray[fd].realfd == -1)
685         syscallfailure("cannot open /dev/null for null fd");
686       break;
687     case tokv_word_requirefd:
688       if (fdarray[fd].realfd == -1)
689         failure("file descriptor %d not provided but required",fd);
690       /* fall through */
691     case tokv_word_allowfd:
692       if (fdarray[fd].realfd == -1) {
693         fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write);
694         fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
695         if (fdarray[fd].realfd == -1)
696           syscallfailure("cannot open /dev/null for allowed but not provided fd");
697       } else {
698         if (fdarray[fd].iswrite) {
699           if (fdarray[fd].wantrw != tokv_word_write)
700             failure("file descriptor %d provided write, wanted read",fd);
701         } else {
702           if (fdarray[fd].wantrw != tokv_word_read)
703             failure("file descriptor %d provided read, wanted write",fd);
704         }
705       }
706     }
707   }
708
709   memset(&progress_mbuf,0,sizeof(progress_mbuf));
710   progress_mbuf.magic= PROGRESS_MAGIC;
711   progress_mbuf.type= pt_ok;
712   xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
713   xfflush(swfile);
714
715   r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket);
716   if (r) syscallfailure("cannot create socket for synch");
717
718   sig.sa_handler= sighandler_pipe;
719   sigemptyset(&sig.sa_mask);
720   sigaddset(&sig.sa_mask,SIGPIPE);
721   sigaddset(&sig.sa_mask,SIGCHLD);
722   sig.sa_flags= 0;
723   if (sigaction(SIGPIPE,&sig,0)) syscallfailure("cannot set sigpipe handler");
724
725   sig.sa_handler= sighandler_chld;
726   if (sigaction(SIGCHLD,&sig,0)) syscallfailure("cannot set sigchld handler");
727
728   getevent(&event_mbuf);
729   assert(event_mbuf.type == et_confirm);
730
731   newchild= fork();
732   if (newchild == -1) syscallfailure("cannot fork to invoke service");
733   if (!newchild) execservice(synchsocket);
734   childtokill= child= newchild;
735
736   if (close(synchsocket[1])) syscallfailure("cannot close other end of synch socket");
737
738   r= synchread(synchsocket[0],'y');
739   if (r) syscallfailure("read synch byte from child");
740
741   childtokill= -child;
742
743   synchmsg= 'g';
744   r= write(synchsocket[0],&synchmsg,1);
745   if (r!=1) syscallerror("write synch byte to child");
746
747   if (close(synchsocket[0])) syscallfailure("cannot close my end of synch socket");
748
749   getevent(&event_mbuf);
750   abort();
751 }
752
753 int main(int argc, char *const *argv) {
754   int mfd, sfd, csocklen;
755   struct sigaction childact;
756   struct sockaddr_un ssockname, csockname;
757
758 #ifdef NDEBUG
759   abort(); /* Do not disable assertions in this security-critical code ! */
760 #endif
761
762   if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
763
764   openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
765   mfd= socket(AF_UNIX,SOCK_STREAM,0);
766   if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
767
768   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
769   ssockname.sun_family= AF_UNIX;
770   strcpy(ssockname.sun_path,RENDEZVOUSPATH);
771   unlink(RENDEZVOUSPATH);
772   if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
773     { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
774   if (listen(mfd,5))
775     { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
776
777   childact.sa_handler= sigchildhandler;
778   sigemptyset(&childact.sa_mask);
779   childact.sa_flags= SA_NOCLDSTOP;
780   if (sigaction(SIGCHLD,&childact,0))
781     { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
782   syslog(LOG_NOTICE,"started");
783   for (;;) {
784     csocklen= sizeof(csockname);
785     sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
786     if (sfd == -1) {
787       if (errno == EINTR) continue;
788       if (errno == ENOMEM) {
789         syslog(LOG_ERR,"unable to accept connection: %m"); continue;
790       }
791       syslog(LOG_CRIT,"unable to accept new connections: %m"); exit(5);
792     }
793     child= nondebug_fork();
794     if (child == (pid_t)-1) {
795       syslog(LOG_ERR,"unable to fork server: %m"); close(sfd); continue;
796     }
797     if (!child) {
798       close(mfd); closelog(); servicerequest(sfd);
799     }
800     close(sfd);
801   }
802 }