chiark / gitweb /
740aac54e44ecd357b2e21a61b58f82ab22b71b4
[userv.git] / client.c
1 /*
2  * userv - client.c
3  * client code
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 /*
23  * Here too, we do some horrible asynchronous stuff with signals.
24  *
25  * The following objects &c. are used in signal handlers and so
26  * must be protected by calls to blocksignals if they are used in
27  * the main program:
28  *  stderr
29  *  swfile
30  *
31  * The following objects are used in the main program unprotected
32  * and so must not be used in signal handlers:
33  *  srfile
34  *  fdsetup[].copyfd
35  *  fdsetup[].pipefd
36  *
37  * The following objects/functions are not modified/called after the
38  * asynchronicity starts:
39  *  malloc
40  *  fdsetupsize
41  *  fdsetup[].mods
42  *  fdsetup[].filename
43  *  fdsetup[].oflags
44  *  results of argument parsing
45  *
46  * systemerror, swfile, fdsetup[].catpid and fdsetup[].killed are used
47  * for communication between the main thread and the signal handlers.
48  *
49  * All the signal handlers save errno so that is OK too.
50  */
51
52 #include <fcntl.h>
53 #include <stdarg.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <assert.h>
59 #include <unistd.h>
60 #include <ctype.h>
61 #include <pwd.h>
62 #include <grp.h>
63 #include <signal.h>
64 #include <limits.h>
65 #include <sys/time.h>
66 #include <sys/types.h>
67 #include <sys/stat.h>
68 #include <sys/socket.h>
69 #include <sys/un.h>
70 #include <sys/resource.h>
71 #include <sys/wait.h>
72
73 #include "config.h"
74 #include "common.h"
75 #include "both.h"
76 #include "version.h"
77
78 enum fdmodifiervalues {
79   fdm_read=       00001,
80   fdm_write=      00002,
81   fdm_create=     00004,
82   fdm_exclusive=  00010,
83   fdm_truncate=   00020,
84   fdm_append=     00040,
85   fdm_sync=       00100,
86   fdm_fd=         00200,
87   fdm_wait=       01000,
88   fdm_nowait=     02000,
89   fdm_close=      04000,
90 };
91
92 struct fdmodifierinfo {
93   const char *string;
94   int implies;
95   int conflicts;
96   int oflags;
97 };
98
99 struct fdsetupstate {
100   const char *filename; /* non-null iff this fd has been specified */
101   int copyfd; /* fd to copy, -1 unless mods & fdm_fd */
102   int mods, oflags, pipefd, killed; /* 0,0,-1,0 unless otherwise set */
103   pid_t catpid; /* -1 indicates no cat process */
104 };
105
106 enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
107 enum overridetypes { ot_none, ot_string, ot_file, ot_builtin };
108
109 struct constkeyvaluepair { const char *key, *value; };
110
111 /* Variables from command-line arguments */
112 static const char *serviceuser;
113 static uid_t serviceuid;
114 static struct fdsetupstate *fdsetup;
115 static int fdsetupsize;
116 static struct constkeyvaluepair *defvararray;
117 static int defvaravail, defvarused;
118 static unsigned long timeout;
119 static int signalsexit=254;
120 static int sigpipeok, hidecwd;
121 static int overridetype= ot_none;
122 static const char *overridevalue, *spoofuser=0;
123
124 /* Other state variables */
125 static FILE *srfile, *swfile;
126 static pid_t mypid;
127 static uid_t myuid, spoofuid;
128 static gid_t mygid, spoofgid, *gidarray;
129 static int ngids;
130 static struct opening_msg opening_mbuf;
131 static const char *loginname;
132 static char *cwdbuf;
133 static size_t cwdbufsize;
134 static char *ovbuf;
135 static int ovused, systemerror;
136
137 static void blocksignals(int how) {
138   sigset_t set;
139   static const char blockerrmsg[]= "userv: failed to [un]block signals: ";
140   const char *str;
141
142   sigemptyset(&set);
143   sigaddset(&set,SIGCHLD);
144   sigaddset(&set,SIGALRM);
145   if (sigprocmask(how,&set,0)) {
146     str= strerror(errno);
147     write(2,blockerrmsg,sizeof(blockerrmsg)-1);
148     write(2,str,strlen(str));
149     write(2,"\n",1);
150     exit(-1);
151   }
152 }
153
154 /* Functions which may be called either from signal handlers or from
155  * the main thread.  They block signals in case they are on the main
156  * thread, and may only use signal handler objects.  None of them
157  * return.  If they did they'd have to restore the signal mask.
158  */
159
160 static void NONRETURNPRINTFFORMAT(1,2) miscerror(const char *fmt, ...) {
161   va_list al;
162
163   blocksignals(SIG_BLOCK);
164   va_start(al,fmt);
165   fprintf(stderr,"userv: failure: ");
166   vfprintf(stderr,fmt,al);
167   fprintf(stderr,"\n");
168   exit(-1);
169 }
170
171 static void NONRETURNPRINTFFORMAT(1,2) syscallerror(const char *fmt, ...) {
172   va_list al;
173   int e;
174
175   e= errno;
176   blocksignals(SIG_BLOCK);
177   va_start(al,fmt);
178   fprintf(stderr,"userv: system call failure: ");
179   vfprintf(stderr,fmt,al);
180   fprintf(stderr,": %s\n",strerror(e));
181   exit(-1);
182 }
183
184 static void NONRETURNING protoreaderror(FILE *file, const char *where) {
185   int e;
186
187   e= errno;
188   blocksignals(SIG_BLOCK);
189   if (ferror(file)) {
190     fprintf(stderr,"userv: failure: read error %s: %s\n",where,strerror(e));
191   } else {
192     assert(feof(file));
193     fprintf(stderr,"userv: internal failure: EOF from server %s\n",where);
194   }
195   exit(-1);
196 }
197
198 static void NONRETURNPRINTFFORMAT(1,2) protoerror(const char *fmt, ...) {
199   va_list al;
200
201   blocksignals(SIG_BLOCK);
202   va_start(al,fmt);
203   fprintf(stderr,"userv: internal failure: protocol error: ");
204   vfprintf(stderr,fmt,al);
205   fprintf(stderr,"\n");
206   exit(-1);
207 }
208
209 /*
210  * General-purpose functions; these do nothing special about signals,
211  * except that they can call error-handlers which may block them
212  * to print error messages.
213  */
214
215 static void xfread(void *p, size_t sz, FILE *file) {
216   size_t nr;
217   nr= working_fread(p,sz,file);
218   if (nr != sz) protoreaderror(file,"in data");
219 }
220
221 static void xfwrite(const void *p, size_t sz, FILE *file) {
222   size_t nr;
223   nr= fwrite(p,1,sz,file); if (nr == sz) return;
224   syscallerror("writing to server");
225 }
226
227 static void xfflush(FILE *file) {
228   if (fflush(file)) syscallerror("flush server socket");
229 }
230
231 /* Functions which may be called only from the main thread.  These may
232  * use main-thread objects and must block signals before using signal
233  * handler objects.
234  */
235
236 #ifdef DEBUG
237 static void priv_suspend(void) { }
238 static void priv_resume(void) { }
239 static void priv_permanentlyrevokesuspended(void) { }
240 #else
241 static void priv_suspend(void) {
242   if (setreuid(0,myuid) != 0) syscallerror("suspend root setreuid(0,myuid)");
243 }
244 static void priv_resume(void) {
245   if (setreuid(myuid,0) != 0) syscallerror("resume root setreuid(myuid,0)");
246 }
247 static void priv_permanentlyrevokesuspended(void) {
248   if (setreuid(myuid,myuid) != 0) syscallerror("revoke root setreuid(myuid,myuid)");
249   if (setreuid(myuid,myuid) != 0) syscallerror("rerevoke root setreuid(myuid,myuid)");
250   if (myuid) {
251     if (!setreuid(myuid,0)) miscerror("revoked root but setreuid(0,0) succeeded !");
252     if (errno != EPERM) syscallerror("revoked and setreuid(myuid,0) unexpected error");
253   }
254 }
255 #endif
256
257 static void checkmagic(unsigned long was, unsigned long should, const char *when) {
258   if (was != should)
259     protoerror("magic number %s was %08lx, expected %08lx",when,was,should);
260 }
261
262 static void getprogress(struct progress_msg *progress_r, FILE *file) {
263   int i, c;
264   unsigned long ul;
265
266   for (;;) {
267     xfread(progress_r,sizeof(struct progress_msg),file);
268     checkmagic(progress_r->magic,PROGRESS_MAGIC,"in progress message");
269     switch (progress_r->type) {
270     case pt_failed:
271       blocksignals(SIG_BLOCK);
272       fputs("userv: uservd reports that service failed\n",stderr);
273       exit(-1);
274     case pt_errmsg:
275       blocksignals(SIG_BLOCK);
276       fputs("uservd: ",stderr);
277       if (progress_r->data.errmsg.messagelen>MAX_ERRMSG_STRING)
278         protoerror("stderr message length %d is far too long",
279                    progress_r->data.errmsg.messagelen);
280       for (i=0; i<progress_r->data.errmsg.messagelen; i++) {
281         c= working_getc(file);
282         if (c==EOF) protoreaderror(file,"in error message");
283         if (isprint(c)) putc(c,stderr);
284         else fprintf(stderr,"\\x%02x",(unsigned char)c);
285       }
286       putc('\n',stderr);
287       if (ferror(stderr)) syscallerror("printing error message");
288       xfread(&ul,sizeof(ul),file);
289       checkmagic(ul,PROGRESS_ERRMSG_END_MAGIC,"after error message");
290       blocksignals(SIG_UNBLOCK);
291       break;
292     default:
293       return;
294     }
295   }
296 }
297
298 /*
299  * Functions which are called only during setup, before
300  * the signal asynchronicity starts.  They can do anything they like.
301  */
302
303 static void *xmalloc(size_t s) {
304   void *p;
305   p= malloc(s?s:1);
306   if (!p) syscallerror("malloc (%lu bytes)",(unsigned long)s);
307   return p;
308 }
309
310 static void *xrealloc(void *p, size_t s) {
311   p= realloc(p,s);
312   if (!p) syscallerror("realloc (%lu bytes)",(unsigned long)s);
313   return p;
314 }
315
316 static void xfwritestring(const char *s, FILE *file) {
317   int l;
318   l= strlen(s);
319   assert(l<=MAX_GENERAL_STRING);
320   xfwrite(&l,sizeof(l),file);
321   xfwrite(s,sizeof(*s)*l,file);
322 }
323
324 static void xfwritefds(int modifier, int expected, FILE *file) {
325   int i, fdcount;
326
327   for (i=0, fdcount=0; i<fdsetupsize; i++) {
328     if (!(fdsetup[i].filename && (fdsetup[i].mods & modifier)))
329       continue;
330     xfwrite(&i,sizeof(int),file); fdcount++;
331   }
332   assert(fdcount == expected);
333 }
334
335 /* Functions which may be called from signal handlers.  These
336  * may use signal-handler objects.  The main program may only
337  * call them with signals blocked, and they may not use any
338  * main-thread objects.
339  */
340
341 static void disconnect(void) /* DOES return, unlike in daemon */ {
342   struct event_msg event_mbuf;
343   int r;
344
345   if (swfile) {
346     memset(&event_mbuf,0,sizeof(event_mbuf));
347     event_mbuf.magic= EVENT_MAGIC;
348     event_mbuf.type= et_disconnect;
349     r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
350     if ((r != sizeof(event_mbuf) || fflush(swfile)) && errno != EPIPE)
351       syscallerror("write to server when disconnecting\n");
352   }
353   systemerror= 1;
354 }
355
356 static void sighandler_alrm(int ignored) /* DOES return, unlike in daemon */ {
357   int es;
358   es= errno;
359   fputs("userv: timeout\n",stderr);
360   disconnect();
361   errno= es;
362 }
363
364 static void sighandler_chld(int ignored) /* DOES return, unlike in daemon */ {
365   struct event_msg event_mbuf;
366   pid_t child;
367   int status, fd, r, es;
368
369   es= errno;
370   for (;;) {
371     child= wait3(&status,WNOHANG,0);
372     if (child == 0 || (child == -1 && errno == ECHILD)) break;
373     if (child == -1) syscallerror("wait for child process (in sigchld handler)");
374     for (fd=0; fd<fdsetupsize && fdsetup[fd].catpid != child; fd++);
375     if (fd>=fdsetupsize) continue; /* perhaps the caller gave us children */
376     if ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
377         (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) ||
378         (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) {
379       if (swfile && fdsetup[fd].mods & fdm_read) {
380         memset(&event_mbuf,0,sizeof(event_mbuf));
381         event_mbuf.magic= EVENT_MAGIC;
382         event_mbuf.type= et_closereadfd;
383         event_mbuf.data.closereadfd.fd= fd;
384         r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
385         if (r != sizeof(event_mbuf) || fflush(swfile))
386           if (errno != EPIPE) syscallerror("inform service of closed read fd");
387       }
388     } else {
389       if (WIFEXITED(status))
390         fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n",
391                 fd,WEXITSTATUS(status));
392       else if (WIFSIGNALED(status))
393         if (WCOREDUMP(status))
394           fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n",
395                   fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
396         else
397           fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n",
398                   fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
399       else
400         fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n",
401                 fd,status);
402       disconnect();
403     }
404     fdsetup[fd].catpid= -1;
405   }
406   errno= es;
407 }
408
409 /*
410  * Argument parsing.  These functions which are called only during
411  * setup, before the signal asynchronicity starts.
412  */
413
414 struct optioninfo;
415
416 typedef void optionfunction(const struct optioninfo*, const char *value, char *key);
417
418 struct optioninfo {
419   int abbrev;
420   const char *full;
421   int values; /* 0: no value; 1: single value; 2: key and value */
422   optionfunction *fn;
423 };
424
425 static void usage(void) {
426   if (fputs(
427     "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
428     "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
429     "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
430     "         -D|--defvar <name>=<value>\n"
431     "         -t|--timeout <seconds>\n"
432     "         -S|--signals <status>|number|number-nocore|highbit|stdout\n"
433     "         -w|--fdwait <fd>=wait|nowait|close\n"
434     "         -P|--sigpipe  -H|--hidecwd  -h|--help  --copyright\n"
435     "         --override <configuration-data> } available only\n"
436     "         --override-file <filename>      }  to root\n"
437     "         --spoof-user <username>         }  or same user\n"
438     "fdmodifiers:            read    write  overwrite    trunc[ate]\n"
439     "(separate with commas)  append  sync   excl[usive]  creat[e]  fd\n"
440     "userv -B 'X' ... is same as userv --override 'execute-builtin X' - 'X' ...\n"
441     "         for help, type `userv -B help'; remember to quote multi-word X\n"
442     "userv and uservd version " VERSION VEREXT "; copyright (C)1996-1999 Ian Jackson.\n"
443     "there is NO WARRANTY; type `userv --copyright' for details.\n",
444             stderr) < 0)
445     syscallerror("write usage to stderr");
446 }
447
448 static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) {
449   va_list al;
450   va_start(al,fmt);
451   fputs("userv: ",stderr);
452   vfprintf(stderr,fmt,al);
453   fputs("\n\n",stderr);
454   usage();
455   exit(-1);
456 }
457
458 static const struct fdmodifierinfo fdmodifierinfos[]= {
459   { "read",      fdm_read,
460                  fdm_write,
461                  O_RDONLY                                                         },
462   { "write",     fdm_write,
463                  fdm_read,
464                  O_WRONLY                                                         },
465   { "overwrite", fdm_write|fdm_create|fdm_truncate,
466                  fdm_read|fdm_fd|fdm_exclusive,
467                  O_WRONLY|O_CREAT|O_TRUNC                                         },
468   { "create",    fdm_write|fdm_create,
469                  fdm_read|fdm_fd,
470                  O_WRONLY|O_CREAT                                                 },
471   { "creat",     fdm_write|fdm_create,
472                  fdm_read|fdm_fd,
473                  O_WRONLY|O_CREAT                                                 },
474   { "exclusive", fdm_write|fdm_create|fdm_exclusive,
475                  fdm_read|fdm_fd|fdm_truncate,
476                  O_WRONLY|O_CREAT|O_EXCL                                          },
477   { "excl",      fdm_write|fdm_create|fdm_exclusive,
478                  fdm_read|fdm_fd|fdm_truncate,
479                  O_WRONLY|O_CREAT|O_EXCL                                          },
480   { "truncate",  fdm_write|fdm_truncate,
481                  fdm_read|fdm_fd|fdm_exclusive,
482                  O_WRONLY|O_CREAT|O_EXCL                                          },
483   { "trunc",     fdm_write|fdm_truncate,
484                  fdm_read|fdm_fd|fdm_exclusive,
485                  O_WRONLY|O_CREAT|O_EXCL                                          },
486   { "append",    fdm_write|fdm_append,
487                  fdm_read|fdm_fd,
488                  O_WRONLY|O_CREAT|O_APPEND                                        },
489   { "sync",      fdm_write|fdm_sync,
490                  fdm_read|fdm_fd,
491                  O_WRONLY|O_CREAT|O_SYNC                                          },
492   { "wait",      fdm_wait,
493                  fdm_nowait|fdm_close,
494                  0                                                                },
495   { "nowait",    fdm_nowait,
496                  fdm_wait|fdm_close,
497                  0                                                                },
498   { "close",     fdm_close,
499                  fdm_wait|fdm_nowait,
500                  0                                                                },
501   { "fd",        fdm_fd,
502                  fdm_create|fdm_exclusive|fdm_truncate|fdm_append|fdm_sync,
503                  0                                                                },
504   {  0                                                                            }
505 };
506
507 static void addfdmodifier(int fd, const char *key) {
508   const struct fdmodifierinfo *fdmip;
509   
510   if (!*key) return;
511   for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,key); fdmip++);
512   if (!fdmip->string) usageerror("unknown fdmodifer `%s' for fd %d",key,fd);
513   if (fdmip->conflicts & fdsetup[fd].mods)
514     usageerror("fdmodifier `%s' conflicts with another for fd %d",key,fd);
515   fdsetup[fd].mods |= fdmip->implies;
516   fdsetup[fd].oflags |= fdmip->oflags;
517 }
518
519 static int fdstdnumber(const char *string) {
520   if (!strcmp(string,"stdin")) return 0;
521   else if (!strcmp(string,"stdout")) return 1;
522   else if (!strcmp(string,"stderr")) return 2;
523   else return -1;
524 }
525
526 static void of_file(const struct optioninfo *oip, const char *value, char *key) {
527   unsigned long fd, copyfd;
528   struct stat stab;
529   int oldarraysize, r;
530   char *delim;
531
532   fd= strtoul(key,&delim,10);
533   if (delim == key) {
534     delim= strchr(key,',');
535     if (delim) *delim++= 0;
536     fd= fdstdnumber(key);
537     if (fd<0) usageerror("first part of argument to -f or --file must be numeric "
538                          "file descriptor or `stdin', `stdout' or `stderr' - `%s' "
539                          "is not recognized",key);
540   }
541   if (fd > MAX_ALLOW_FD)
542     usageerror("file descriptor specified (%lu) is larger than maximum allowed (%d)",
543                fd,MAX_ALLOW_FD);
544   if (fd >= fdsetupsize) {
545     oldarraysize= fdsetupsize;
546     fdsetupsize+=2; fdsetupsize<<=1;
547     fdsetup= xrealloc(fdsetup,sizeof(struct fdsetupstate)*fdsetupsize);
548     while (oldarraysize < fdsetupsize) {
549       fdsetup[oldarraysize].filename= 0;
550       fdsetup[oldarraysize].pipefd= -1;
551       fdsetup[oldarraysize].copyfd= -1;
552       fdsetup[oldarraysize].mods= 0;
553       fdsetup[oldarraysize].oflags= 0;
554       fdsetup[oldarraysize].catpid= -1;
555       fdsetup[oldarraysize].killed= 0;
556       fdsetup[oldarraysize].filename= 0;
557       oldarraysize++;
558     }
559   }
560   fdsetup[fd].filename= value;
561   fdsetup[fd].oflags= 0;
562   fdsetup[fd].mods= 0;
563   fdsetup[fd].copyfd= -1;
564   while (delim && *delim) {
565     key= delim;
566     delim= strchr(key,',');
567     if (delim) *delim++= 0;
568     addfdmodifier(fd,key);
569   }
570   if (!(fdsetup[fd].mods & (fdm_read|fdm_write))) {
571     if (fd == 0) {
572       addfdmodifier(fd,"read");
573     } else if (fdsetup[fd].mods & fdm_fd) {
574       addfdmodifier(fd,"write");
575     } else {
576       addfdmodifier(fd,"overwrite");
577     }
578   }
579   if (fdsetup[fd].mods & fdm_fd) {
580     copyfd= fdstdnumber(value);
581     if (copyfd<0) {
582       copyfd= strtoul(value,&delim,0);
583       if (*delim)
584         usageerror("value part of argument to --file with fd modifier must be "
585                    "numeric or fd name - `%s' is not recognised",value);
586       else if (copyfd > MAX_ALLOW_FD)
587         usageerror("file descriptor %lu named as target of file descriptor redirection"
588                    " (for file descriptor %lu) is larger than maximum allowed (%d)",
589                    copyfd,fd,MAX_ALLOW_FD);
590     }
591     r= fstat(copyfd,&stab);
592     if (r) {
593       if (oip) syscallerror("check filedescriptor %lu (named as target of file "
594                             "descriptor redirection for %lu)",copyfd,fd);
595       else syscallerror("check basic filedescriptor %lu at program start",copyfd);
596     }
597     fdsetup[fd].copyfd= copyfd;
598   }
599 }
600
601 static void of_fdwait(const struct optioninfo *oip, const char *value, char *key) {
602   const struct fdmodifierinfo *fdmip;
603   unsigned long ul;
604   int fd;
605   char *delim;
606   
607   fd= fdstdnumber(key);
608   if (fd<0) {
609     ul= strtoul(key,&delim,0);
610     if (*delim) usageerror("first part of argument to --fdwait must be "
611                            "numeric or fd name - `%s' is not recognised",key);
612     if (ul>MAX_ALLOW_FD) usageerror("first part of argument to --fdwait is too large");
613     fd= ul;
614   }
615   if (fd >= fdsetupsize || !fdsetup[fd].filename)
616     usageerror("file descriptor %d specified in --fdwait option is not open",fd);
617   for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,value); fdmip++);
618   if (!fdmip->string || !(fdmip->implies & (fdm_wait|fdm_nowait|fdm_close)))
619     usageerror("value for --fdwait must be `wait', `nowait' or `close', not `%s'",value);
620   fdsetup[fd].mods &= ~(fdm_wait|fdm_nowait|fdm_close);
621   fdsetup[fd].mods |= fdmip->implies;
622 }
623
624 static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
625   int i;
626
627   if (!key[0])
628     usageerror("empty string not allowed as variable name");
629   if (strlen(key)>MAX_GENERAL_STRING)
630     usageerror("variable name `%s' is far too long",key);
631   if (strlen(value)>MAX_GENERAL_STRING)
632     usageerror("variable `%s' has value `%s' which is far too long",key,value);
633   for (i=0; i<defvarused && strcmp(defvararray[i].key,key); i++);
634   if (defvarused >= MAX_ARGSDEFVAR) usageerror("far too many --defvar or -D options");
635   if (i>=defvaravail) {
636     defvaravail+=10; defvaravail<<=1;
637     defvararray= xrealloc(defvararray,sizeof(struct constkeyvaluepair)*defvaravail);
638   }
639   if (i==defvarused) defvarused++;
640   defvararray[i].key= key;
641   defvararray[i].value= value;
642 }
643
644 static void of_timeout(const struct optioninfo *oip, const char *value, char *key) {
645   char *endp;
646   unsigned long ul;
647   
648   ul= strtoul(value,&endp,0);
649   if (*endp) usageerror("timeout value `%s' must be a plain decimal string",value);
650   if (ul>INT_MAX) usageerror("timeout value %lu too large",ul);
651   timeout= ul;
652 }
653
654 static void of_signals(const struct optioninfo *oip, const char *value, char *key) {
655   unsigned long numvalue;
656   char *endp;
657   
658   numvalue= strtoul(value,&endp,0);
659   if (*endp) {
660     if (!strcmp(value,"number")) signalsexit= se_number;
661     else if (!strcmp(value,"number-nocore")) signalsexit= se_numbernocore;
662     else if (!strcmp(value,"highbit")) signalsexit= se_highbit;
663     else if (!strcmp(value,"stdout")) signalsexit= se_stdout;
664     else usageerror("value `%s' for --signals not understood",value);
665   } else {
666     if (numvalue<0 || numvalue>255)
667       usageerror("value %lu for --signals not 0...255",numvalue);
668     signalsexit= numvalue;
669   }
670 }
671
672 static void of_sigpipe(const struct optioninfo *oip, const char *value, char *key) {
673   sigpipeok=1;
674 }
675
676 static void of_hidecwd(const struct optioninfo *oip, const char *value, char *key) {
677   hidecwd=1;
678 }
679
680 static void of_help(const struct optioninfo *oip, const char *value, char *key) {
681   usage();
682   exit(0);
683 }
684
685 static void of_copyright(const struct optioninfo *oip, const char *value, char *key) {
686   if (fputs(
687 " userv - user service daemon and client; copyright (C)1996-1999 Ian Jackson\n\n"
688 " This is free software; you can redistribute it and/or modify it under the\n"
689 " terms of the GNU General Public License as published by the Free Software\n"
690 " Foundation; either version 2 of the License, or (at your option) any\n"
691 " later version.\n\n"
692 " This program is distributed in the hope that it will be useful, but\n"
693 " WITHOUT ANY WARRANTY; without even the implied warranty of\n"
694 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General\n"
695 " Public License for more details.\n\n"
696 " You should have received a copy of the GNU General Public License along\n"
697 " with userv; if not, write to Ian Jackson <ian@chiark.greenend.org.uk> or\n"
698 " to the Free Software Foundation, 59 Temple Place - Suite 330, Boston,\n"
699 " MA 02111-1307, USA.\n",
700             stdout) < 0) syscallerror("write usage to stderr");
701   exit(0);
702 }
703
704 static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
705   overridetype= ot_builtin;
706 }
707
708 static void of_override(const struct optioninfo *oip, const char *value, char *key) {
709   overridetype= ot_string;
710   overridevalue= value;
711 }
712
713 static void of_overridefile(const struct optioninfo *oip,
714                             const char *value, char *key) {
715   overridetype= ot_file;
716   overridevalue= value;
717 }
718
719 static void of_spoofuser(const struct optioninfo *oip,
720                          const char *value, char *key) {
721   spoofuser= value;
722 }
723
724 const struct optioninfo optioninfos[]= {
725   { 'f', "file",          2, of_file         },
726   { 'w', "fdwait",        2, of_fdwait       },
727   { 'D', "defvar",        2, of_defvar       },
728   { 't', "timeout",       1, of_timeout      },
729   { 'S', "signals",       1, of_signals      },
730   { 'P', "sigpipe",       0, of_sigpipe      },
731   { 'H', "hidecwd",       0, of_hidecwd      },
732   { 'B', "builtin",       0, of_builtin      },
733   { 'h', "help",          0, of_help         },
734   {  0,  "copyright",     0, of_copyright    },
735   {  0,  "override",      1, of_override     },
736   {  0,  "override-file", 1, of_overridefile },
737   {  0,  "spoof-user",    1, of_spoofuser    },
738   {  0,   0                                  }
739 };
740
741 static void callvalueoption(const struct optioninfo *oip, char *arg) {
742   char *equals;
743   if (oip->values == 2) {
744     equals= strchr(arg,'=');
745     if (!equals) {
746       if (oip->abbrev)
747         usageerror("option --%s (-%c) passed argument `%s' with no `='",
748                    oip->full,oip->abbrev,arg);
749       else
750         usageerror("option --%s passed argument `%s' with no `='",
751                    oip->full,arg);
752     }
753     *equals++= 0;
754     (oip->fn)(oip,equals,arg);
755   } else {
756     (oip->fn)(oip,arg,0);
757   }
758 }
759
760 /*
761  * Main thread main processing functions - in order of execution.
762  */
763
764 static void security_init(void) {
765   /* May not open any file descriptors. */
766   
767   mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid");
768   myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid");
769   mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid");
770   ngids= getgroups(0,0); if (ngids == (gid_t)-1) syscallerror("getgroups(0,0)");
771   gidarray= xmalloc(sizeof(gid_t)*ngids);
772   if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)");
773   
774   priv_suspend();
775
776   if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
777 }
778
779 static void parse_arguments(int *argcp, char *const **argvp) {
780   static char fd0key[]= "stdin,fd,read";
781   static char fd1key[]= "stdout,fd,write";
782   static char fd2key[]= "stderr,fd,write";
783
784   char *const *argpp;
785   char *argp;
786   const struct optioninfo *oip;
787   int fd;
788
789   assert((*argvp)[0]);
790   of_file(0,"stdin",fd0key);
791   of_file(0,"stdout",fd1key);
792   of_file(0,"stderr",fd2key);
793
794   for (argpp= *argvp+1;
795        (argp= *argpp) && *argp == '-' && argp[1];
796        argpp++) {
797     if (!*++argp) usageerror("unknown option/argument `%s'",*argpp);
798     if (*argp == '-') { /* Two hyphens */
799       if (!*++argp) { argpp++; break; /* End of options. */ }
800       for (oip= optioninfos; oip->full && strcmp(oip->full,argp); oip++);
801       if (!oip->full) usageerror("unknown long option `%s'",*argpp);
802       if (oip->values) {
803         if (!argpp[1]) usageerror("long option `%s' needs a value",*argpp);
804         callvalueoption(oip,*++argpp);
805       } else {
806         (oip->fn)(oip,0,0);
807       }
808     } else {
809       for (; *argp; argp++) {
810         for (oip= optioninfos; oip->full && oip->abbrev != *argp; oip++);
811         if (!oip->full) usageerror("unknown short option `-%c' in argument `%s'",
812                                     *argp, *argpp);
813         if (oip->values) {
814           if (argp[1]) {
815             argp++;
816           } else {
817             if (!argpp[1]) usageerror("short option `-%c' in argument `%s' needs"
818                                       " a value",*argp,*argpp);
819             argp= *++argpp;
820           }
821           callvalueoption(oip,argp);
822           break; /* No more options in this argument, go on to the next one. */
823         } else {
824           (oip->fn)(oip,0,0);
825         }
826       }
827     }
828   }
829   if (overridetype == ot_builtin) {
830     serviceuser= "-";
831   } else {
832     if (!*argpp) usageerror("no service user given after options");
833     serviceuser= *argpp++;
834   }
835   if (!*argpp) usageerror(overridetype == ot_builtin ?
836                           "no service name given after options and service user" :
837                           "no builtin service given after options");
838
839   *argcp-= (argpp-*argvp);
840   *argvp= argpp;
841
842   if (*argcp > MAX_ARGSDEFVAR) usageerror("far too many arguments");
843   
844   for (fd=0; fd<fdsetupsize; fd++) {
845     if (!fdsetup[fd].filename) continue;
846     if (fdsetup[fd].mods & (fdm_wait|fdm_nowait|fdm_close)) continue;
847     assert(fdsetup[fd].mods & (fdm_read|fdm_write));
848     fdsetup[fd].mods |= (fdsetup[fd].mods & fdm_read) ? fdm_close : fdm_wait;
849   }
850 }
851
852 static void determine_users(void) {
853   int ngidssize;
854   char **mem;
855   struct passwd *pw;
856   struct group *gr;
857   
858   spoofuid= myuid;
859   spoofgid= mygid;
860   loginname= getenv("LOGNAME");
861   if (!loginname) loginname= getenv("USER");
862   if (loginname) {
863     pw= getpwnam(loginname);
864     if (!pw || pw->pw_uid != myuid) loginname= 0;
865   }
866   if (!loginname) {
867     pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
868     loginname= pw->pw_name;
869   }
870
871   if (!strcmp(serviceuser,"-")) serviceuser= loginname;
872   pw= getpwnam(serviceuser);
873   if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
874   serviceuid= pw->pw_uid;
875
876   if ((overridetype != ot_none || spoofuser) && myuid != 0 && myuid != serviceuid)
877     miscerror("--override and --spoof options only available to root or to"
878               " the user who will be providing the service");
879
880   if (spoofuser) {
881     loginname= spoofuser;
882     pw= getpwnam(loginname);
883     if (!pw) miscerror("spoofed login name `%s' is not valid",loginname);
884     spoofuid= pw->pw_uid;
885     spoofgid= pw->pw_gid;
886     ngidssize= ngids; ngids= 0;
887     if (ngidssize<5) {
888       ngidssize= 5;
889       gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
890     }
891     gidarray[ngids++]= spoofgid;
892     while ((gr= getgrent())) { /* ouch! getgrent has no error behaviour! */
893       for (mem= gr->gr_mem; *mem && strcmp(*mem,loginname); mem++);
894       if (!*mem) continue;
895       if (ngids>=ngidssize) {
896       if (ngids>=MAX_GIDS) miscerror("spoofed user is member of too many groups");
897         ngidssize= (ngids+5)<<1;
898         gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
899       }
900       gidarray[ngids++]= gr->gr_gid;
901     }
902   }
903 }
904
905 static void determine_cwd(void) {
906   cwdbufsize= 0; cwdbuf= 0;
907
908   if (hidecwd) return;
909   
910   for (;;) {
911     if (cwdbufsize > MAX_GENERAL_STRING) { cwdbufsize= 0; free(cwdbuf); break; }
912     cwdbufsize <<= 1; cwdbufsize+= 100;
913     cwdbuf= xrealloc(cwdbuf,cwdbufsize);
914     cwdbuf[cwdbufsize-1]= 0;
915     if (getcwd(cwdbuf,cwdbufsize-1)) { cwdbufsize= strlen(cwdbuf); break; }
916     if (errno != ERANGE) { cwdbufsize= 0; free(cwdbuf); break; }
917   }
918 }
919
920 static void process_override(const char *servicename) {
921   FILE *ovfile;
922   int ovavail, l, c;
923
924   switch (overridetype) {
925   case ot_none:
926     ovused= -1;
927     ovbuf= 0;
928     break;
929   case ot_builtin:
930     l= strlen(servicename);
931     if (l >= MAX_OVERRIDE_LEN-20)
932       miscerror("builtin service string is too long (%d, max is %d)",
933                 l,MAX_OVERRIDE_LEN-21);
934     l+= 20;
935     ovbuf= xmalloc(l);
936     snprintf(ovbuf,l,"execute-builtin %s\n",servicename);
937     ovused= strlen(ovbuf);
938     break;
939   case ot_string:
940     l= strlen(overridevalue);
941     if (l >= MAX_OVERRIDE_LEN)
942       miscerror("override string is too long (%d, max is %d)",l,MAX_OVERRIDE_LEN-1);
943     ovbuf= xmalloc(l+2);
944     snprintf(ovbuf,l+2,"%s\n",overridevalue);
945     ovused= l+1;
946     break;
947   case ot_file:
948     ovfile= fopen(overridevalue,"r");
949     if (!ovfile) syscallerror("open overriding configuration file `%s'",overridevalue);
950     ovbuf= 0; ovavail= ovused= 0;
951     while ((c= getc(ovfile)) != EOF) {
952       if (!c) miscerror("overriding config file `%s' contains null(s)",overridevalue);
953       if (ovused >= MAX_OVERRIDE_LEN)
954         miscerror("override file is too long (max is %d)",MAX_OVERRIDE_LEN);
955       if (ovused >= ovavail) {
956         ovavail+=80; ovavail<<=2; ovbuf= xrealloc(ovbuf,ovavail);
957       }
958       ovbuf[ovused++]= c;
959     }
960     if (ferror(ovfile) || fclose(ovfile))
961       syscallerror("read overriding configuration file `%s'",overridevalue);
962     ovbuf= xrealloc(ovbuf,ovused+1);
963     ovbuf[ovused]= 0;
964     break;
965   default:
966     abort();
967   }
968 }
969
970 static int server_connect(void) {
971   struct sockaddr_un ssockname;
972   int sfd;
973   struct sigaction sig;
974   sigset_t sset;
975
976   sigemptyset(&sset);
977   sigaddset(&sset,SIGCHLD);
978   sigaddset(&sset,SIGALRM);
979   sigaddset(&sset,SIGPIPE);
980   if (sigprocmask(SIG_UNBLOCK,&sset,0)) syscallerror("preliminarily unblock signals");
981
982   sig.sa_handler= SIG_IGN;
983   sigemptyset(&sig.sa_mask);
984   sig.sa_flags= 0;
985   if (sigaction(SIGPIPE,&sig,0)) syscallerror("ignore sigpipe");
986
987   sfd= socket(AF_UNIX,SOCK_STREAM,0);
988   if (!sfd) syscallerror("create client socket");
989
990   assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
991   ssockname.sun_family= AF_UNIX;
992   strcpy(ssockname.sun_path,RENDEZVOUSPATH);
993   priv_resume();
994   while (connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) {
995     if (errno == ECONNREFUSED || errno == ENOENT)
996       syscallerror("uservd daemon is not running - service not available");
997     if (errno != EINTR)
998       syscallerror("unable to connect to uservd daemon: %m");
999   }
1000
1001   return sfd;
1002 }
1003
1004 static void server_handshake(int sfd) {
1005   srfile= fdopen(sfd,"r");
1006   if (!srfile) syscallerror("turn socket fd into FILE* for read");
1007   if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
1008
1009   swfile= fdopen(sfd,"w");
1010   if (!swfile) syscallerror("turn socket fd into FILE* for write");
1011   if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
1012
1013   xfread(&opening_mbuf,sizeof(opening_mbuf),srfile);
1014   checkmagic(opening_mbuf.magic,OPENING_MAGIC,"in opening message");
1015   if (memcmp(protocolchecksumversion,opening_mbuf.protocolchecksumversion,PCSUMSIZE))
1016     protoerror("protocol version checksum mismatch - server not same as client");
1017 }
1018
1019 static void server_preparepipes(void) {
1020   char pipepathbuf[PIPEPATHMAXLEN+2];
1021   int fd, tempfd;
1022   
1023   for (fd=0; fd<fdsetupsize; fd++) {
1024     if (!fdsetup[fd].filename) continue;
1025     pipepathbuf[PIPEPATHMAXLEN]= 0;
1026     sprintf(pipepathbuf, PIPEPATHFORMAT,
1027             (unsigned long)mypid, (unsigned long)opening_mbuf.serverpid, fd);
1028     assert(!pipepathbuf[PIPEPATHMAXLEN]);
1029     priv_resume();
1030     if (unlink(pipepathbuf) && errno != ENOENT)
1031       syscallerror("remove any old pipe `%s'",pipepathbuf);
1032     if (mkfifo(pipepathbuf,0600)) /* permissions are irrelevant */
1033       syscallerror("create pipe `%s'",pipepathbuf);
1034     tempfd= open(pipepathbuf,O_RDWR);
1035     if (tempfd<0) syscallerror("prelim open pipe `%s' for read+write",pipepathbuf);
1036     assert(fdsetup[fd].mods & (fdm_read|fdm_write));
1037     fdsetup[fd].pipefd=
1038       open(pipepathbuf, (fdsetup[fd].mods & fdm_read) ? O_WRONLY : O_RDONLY);
1039     if (fdsetup[fd].pipefd<0) syscallerror("real open pipe `%s'",pipepathbuf);
1040     if (close(tempfd)) syscallerror("close prelim fd onto pipe `%s'",pipepathbuf);
1041     priv_suspend();
1042   }
1043 }
1044
1045 static void server_sendrequest(int argc, char *const *argv) {
1046   struct request_msg request_mbuf;
1047   unsigned long ul;
1048   int fd, i;
1049   
1050   memset(&request_mbuf,0,sizeof(request_mbuf));
1051   request_mbuf.magic= REQUEST_MAGIC;
1052   request_mbuf.clientpid= getpid();
1053   request_mbuf.serviceuserlen= strlen(serviceuser);
1054   request_mbuf.servicelen= strlen(argv[0]);
1055   request_mbuf.loginnamelen= strlen(loginname);
1056   request_mbuf.spoofed= spoofuser ? 1 : 0;
1057   request_mbuf.cwdlen= cwdbufsize;
1058   request_mbuf.callinguid= spoofuid;
1059   request_mbuf.ngids= ngids+1;
1060   request_mbuf.nreadfds= 0;
1061   request_mbuf.nwritefds= 0;
1062   for (fd=0; fd<fdsetupsize; fd++) {
1063     if (!fdsetup[fd].filename) continue;
1064     assert(fdsetup[fd].mods & (fdm_write|fdm_read));
1065     if (fdsetup[fd].mods & fdm_write) request_mbuf.nwritefds++;
1066     else request_mbuf.nreadfds++;
1067   }
1068   request_mbuf.nargs= argc-1;
1069   request_mbuf.nvars= defvarused;
1070   request_mbuf.overridelen= ovused;
1071   xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
1072   xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
1073   xfwrite(argv[0],sizeof(*argv[0])*request_mbuf.servicelen,swfile);
1074   xfwrite(loginname,sizeof(*loginname)*request_mbuf.loginnamelen,swfile);
1075   xfwrite(cwdbuf,sizeof(*cwdbuf)*request_mbuf.cwdlen,swfile);
1076   if (ovused>=0) xfwrite(ovbuf,sizeof(*ovbuf)*ovused,swfile);
1077   xfwrite(&spoofgid,sizeof(gid_t),swfile);
1078   xfwrite(gidarray,sizeof(gid_t)*ngids,swfile);
1079   xfwritefds(fdm_read,request_mbuf.nreadfds,swfile);
1080   xfwritefds(fdm_write,request_mbuf.nwritefds,swfile);
1081   for (i=1; i<argc; i++)
1082     xfwritestring(argv[i],swfile);
1083   for (i=0; i<defvarused; i++) {
1084     xfwritestring(defvararray[i].key,swfile);
1085     xfwritestring(defvararray[i].value,swfile);
1086   }
1087   ul= REQUEST_END_MAGIC; xfwrite(&ul,sizeof(ul),swfile);
1088   xfflush(swfile);
1089 }
1090
1091 static void server_awaitconfirm(void) {
1092   struct progress_msg progress_mbuf;
1093   
1094   getprogress(&progress_mbuf,srfile);
1095   if (progress_mbuf.type != pt_ok)
1096     protoerror("progress message during configuration phase"
1097                " unexpected type %d",progress_mbuf.type);
1098 }
1099
1100 static void prepare_asynchsignals(void) {
1101   static char stderrbuf[BUFSIZ], stdoutbuf[BUFSIZ];
1102   
1103   struct sigaction sig;
1104
1105   if (setvbuf(stderr,stderrbuf,_IOLBF,sizeof(stderrbuf)))
1106     syscallerror("set buffering on stderr");
1107   if (setvbuf(stdout,stdoutbuf,_IOFBF,sizeof(stdoutbuf)))
1108     syscallerror("set buffering on stdout");
1109   
1110   sig.sa_handler= sighandler_chld;
1111   sigemptyset(&sig.sa_mask);
1112   sigaddset(&sig.sa_mask,SIGCHLD);
1113   sigaddset(&sig.sa_mask,SIGALRM);
1114   sig.sa_flags= 0;
1115   if (sigaction(SIGCHLD,&sig,0)) syscallerror("set up sigchld handler");
1116
1117   sig.sa_handler= sighandler_alrm;
1118   if (sigaction(SIGALRM,&sig,0)) syscallerror("set up sigalrm handler");
1119
1120   if (!timeout) return;
1121   if (alarm(timeout)<0) syscallerror("set up timeout alarm");
1122 }
1123
1124 static void catdup(const char *which, int from, int to) {
1125   if (dup2(from,to)<0) {
1126     blocksignals(SIG_BLOCK);
1127     fprintf(stderr,"userv: %s: cannot dup for %s: %s\n",which,
1128             to?"stdout":"stdin", strerror(errno));
1129     exit(-1);
1130   }
1131 }
1132
1133 static void connect_pipes(void) {
1134   struct sigaction sig;
1135   char catnamebuf[sizeof(int)*3+30];
1136   int fd, reading;
1137   pid_t child;
1138   
1139   for (fd=0; fd<fdsetupsize; fd++) {
1140     if (!fdsetup[fd].filename) continue;
1141     if (!(fdsetup[fd].mods & fdm_fd)) {
1142       fdsetup[fd].copyfd=
1143         open(fdsetup[fd].filename,fdsetup[fd].oflags|O_NOCTTY,0777);
1144       if (fdsetup[fd].copyfd<0)
1145         syscallerror("open file `%s' for fd %d",fdsetup[fd].filename,fd);
1146     }
1147     blocksignals(SIG_BLOCK);
1148     child= fork();
1149     fdsetup[fd].catpid= child;
1150     blocksignals(SIG_UNBLOCK);
1151     if (child==-1) syscallerror("fork for cat for fd %d",fd);
1152     if (!child) {
1153       snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd);
1154       catnamebuf[sizeof(catnamebuf)-1]= 0;
1155       sig.sa_handler= SIG_DFL;
1156       sigemptyset(&sig.sa_mask);
1157       sig.sa_flags= 0;
1158       if (sigaction(SIGPIPE,&sig,0)) {
1159         fprintf(stderr,"userv: %s: reset sigpipe handler for cat: %s",
1160                 catnamebuf,strerror(errno));
1161         exit(-1);
1162       }
1163       reading= fdsetup[fd].mods & fdm_read;
1164       catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1);
1165       catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0);
1166       execl("/bin/cat",catnamebuf,(char*)0);
1167       fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno));
1168       exit(-1);
1169     }
1170     if (fdsetup[fd].copyfd>2)
1171       if (close(fdsetup[fd].copyfd)) syscallerror("close real fd for %d",fd);
1172     if (close(fdsetup[fd].pipefd)) syscallerror("close pipe fd for %d",fd);
1173   }
1174 }
1175
1176 static void server_sendconfirm(void) {
1177   struct event_msg event_mbuf;
1178
1179   blocksignals(SIG_BLOCK);
1180   memset(&event_mbuf,0,sizeof(event_mbuf));
1181   event_mbuf.magic= EVENT_MAGIC;
1182   event_mbuf.type= et_confirm;
1183   xfwrite(&event_mbuf,sizeof(event_mbuf),swfile);
1184   xfflush(swfile);
1185   blocksignals(SIG_UNBLOCK);
1186 }
1187
1188 static int server_awaitcompletion(void) {
1189   struct progress_msg progress_mbuf;
1190   
1191   getprogress(&progress_mbuf,srfile);
1192   if (progress_mbuf.type != pt_terminated)
1193     protoerror("progress message during execution phase"
1194                " unexpected type %d",progress_mbuf.type);
1195
1196   swfile= 0;
1197   return progress_mbuf.data.terminated.status;
1198 }
1199
1200 static void dispose_remaining_pipes(void) {
1201   sigset_t sset;
1202   int fd, r;
1203
1204   blocksignals(SIG_BLOCK);
1205   for (fd=0; fd<fdsetupsize; fd++) {
1206     if (!(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_close))) continue;
1207     if (kill(fdsetup[fd].catpid,SIGKILL)) syscallerror("kill cat for %d",fd);
1208     fdsetup[fd].killed= 1;
1209   }
1210   blocksignals(SIG_UNBLOCK);
1211
1212   for (;;) {
1213     blocksignals(SIG_BLOCK);
1214     for (fd=0;
1215          fd<fdsetupsize && !(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_wait));
1216          fd++);
1217     if (fd>=fdsetupsize) break;
1218     sigemptyset(&sset);
1219     r= sigsuspend(&sset);
1220     if (r && errno != EINTR) syscallerror("sigsuspend failed in unexpected way");
1221     blocksignals(SIG_UNBLOCK);
1222   }
1223 }
1224
1225 static void NONRETURNING process_exitstatus(int status) {
1226   blocksignals(SIG_BLOCK);
1227
1228   if (systemerror) _exit(255);
1229
1230   if (sigpipeok && signalsexit != se_stdout && WIFSIGNALED(status) &&
1231       WTERMSIG(status)==SIGPIPE && !WCOREDUMP(status)) status= 0;
1232   
1233   switch (signalsexit) {
1234   case se_number:
1235   case se_numbernocore:
1236     if (WIFEXITED(status))
1237       _exit(WEXITSTATUS(status));
1238     else if (WIFSIGNALED(status))
1239       _exit(WTERMSIG(status) + (signalsexit==se_number && WCOREDUMP(status) ? 128 : 0));
1240     break;
1241   case se_highbit:
1242     if (WIFEXITED(status))
1243       _exit(WEXITSTATUS(status)<=127 ? WEXITSTATUS(status) : 127);
1244     else if (WIFSIGNALED(status) && WTERMSIG(status)<=126)
1245       _exit(WTERMSIG(status)+128);
1246     break;
1247   case se_stdout:
1248     printf("\n%d %d ",(status>>8)&0x0ff,status&0x0ff);
1249     if (WIFEXITED(status))
1250       printf("exited with code %d",WEXITSTATUS(status));
1251     else if (WIFSIGNALED(status))
1252       printf("killed by %s (signal %d)%s",
1253              strsignal(WTERMSIG(status)),WTERMSIG(status),
1254              WCOREDUMP(status) ? ", core dumped " : "");
1255     else
1256       printf("unknown wait status");
1257     putchar('\n');
1258     if (ferror(stdout) || fflush(stdout)) syscallerror("write exit status to stdout");
1259     _exit(0);
1260   default:
1261     if (WIFEXITED(status))
1262       _exit(WEXITSTATUS(status));
1263     else if (WIFSIGNALED(status))
1264       _exit(signalsexit);
1265     break;
1266   }
1267       
1268   fprintf(stderr,"userv: unknown wait status %d\n",status);
1269   _exit(-1);
1270 }
1271
1272 int main(int argc, char *const *argv) {
1273   int status, socketfd;
1274
1275 #ifdef NDEBUG
1276 # error Do not disable assertions in this security-critical code !
1277 #endif
1278
1279   security_init();
1280   parse_arguments(&argc,&argv);
1281   determine_users();
1282   determine_cwd();
1283   process_override(argv[0]);
1284
1285   socketfd= server_connect();
1286   priv_suspend();
1287   server_handshake(socketfd);
1288   server_preparepipes();
1289   server_sendrequest(argc,argv);
1290
1291   priv_permanentlyrevokesuspended();
1292   
1293   server_awaitconfirm();
1294   prepare_asynchsignals();
1295   connect_pipes();
1296   server_sendconfirm();
1297   status= server_awaitcompletion();
1298   
1299   dispose_remaining_pipes();
1300   process_exitstatus(status);
1301 }