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