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