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