5 * Copyright (C)1996-1997 Ian Jackson
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with userv; if not, write to the Free Software
19 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <sys/types.h>
38 #include <sys/socket.h>
40 #include <sys/resource.h>
49 typedef void optionfunction(const struct optioninfo*, const char *value, char *key);
54 int values; /* 0: no value; 1: single value; 2: key and value */
58 enum fdmodifiervalues {
72 struct fdmodifierinfo {
79 const struct fdmodifierinfo fdmodifierinfos[]= {
86 { "overwrite", fdm_write|fdm_create|fdm_truncate,
87 fdm_read|fdm_fd|fdm_exclusive,
88 O_WRONLY|O_CREAT|O_TRUNC },
89 { "create", fdm_write|fdm_create,
92 { "creat", fdm_write|fdm_create,
95 { "exclusive", fdm_write|fdm_create|fdm_exclusive,
96 fdm_read|fdm_fd|fdm_truncate,
97 O_WRONLY|O_CREAT|O_EXCL },
98 { "excl", fdm_write|fdm_create|fdm_exclusive,
99 fdm_read|fdm_fd|fdm_truncate,
100 O_WRONLY|O_CREAT|O_EXCL },
101 { "truncate", fdm_write|fdm_truncate,
102 fdm_read|fdm_fd|fdm_exclusive,
103 O_WRONLY|O_CREAT|O_EXCL },
104 { "trunc", fdm_write|fdm_truncate,
105 fdm_read|fdm_fd|fdm_exclusive,
106 O_WRONLY|O_CREAT|O_EXCL },
107 { "append", fdm_write|fdm_append,
109 O_WRONLY|O_CREAT|O_APPEND },
110 { "sync", fdm_write|fdm_sync,
112 O_WRONLY|O_CREAT|O_SYNC },
114 fdm_nowait|fdm_close,
116 { "nowait", fdm_nowait,
119 { "close", fdm_close,
123 fdm_create|fdm_exclusive|fdm_truncate|fdm_append|fdm_sync,
128 struct fdsetupstate {
129 const char *filename;
131 int mods, oflags, pipefd, killed;
135 enum signalsexitspecials { se_number=-100, se_numbernocore, se_highbit, se_stdout };
136 enum overridetypes { ot_none, ot_string, ot_file, ot_builtin };
138 struct constkeyvaluepair { const char *key, *value; };
140 static const char *serviceuser;
141 static uid_t serviceuid, myuid;
142 static struct fdsetupstate *fdsetup;
143 static int fdsetupsize;
144 static struct constkeyvaluepair *defvararray;
145 static int defvaravail, defvarused;
146 static unsigned long timeout;
147 static int signalsexit=254;
148 static int sigpipeok, hidecwd;
149 static int overridetype= ot_none;
150 static const char *overridevalue, *spoofuser=0;
152 static FILE *srfile, *swfile;
154 static void blocksignals(int how) {
156 static const char blockerrmsg[]= "userv: failed to [un]block signals: ";
160 sigaddset(&set,SIGCHLD);
161 sigaddset(&set,SIGALRM);
162 if (sigprocmask(how,&set,0)) {
163 str= strerror(errno);
164 write(2,blockerrmsg,sizeof(blockerrmsg)-1);
165 write(2,str,strlen(str));
171 static void NONRETURNPRINTFFORMAT(1,2) miscerror(const char *fmt, ...) {
174 blocksignals(SIG_BLOCK);
176 fprintf(stderr,"userv: failure: ");
177 vfprintf(stderr,fmt,al);
178 fprintf(stderr,"\n");
182 static void NONRETURNPRINTFFORMAT(1,2) syscallerror(const char *fmt, ...) {
187 blocksignals(SIG_BLOCK);
189 fprintf(stderr,"userv: system call failure: ");
190 vfprintf(stderr,fmt,al);
191 fprintf(stderr,": %s\n",strerror(e));
195 static void NONRETURNING protoreaderror(FILE *file, const char *where) {
199 blocksignals(SIG_BLOCK);
201 fprintf(stderr,"userv: failure: read error %s: %s\n",where,strerror(e));
204 fprintf(stderr,"userv: internal failure: EOF from server %s\n",where);
209 static void NONRETURNPRINTFFORMAT(1,2) protoerror(const char *fmt, ...) {
212 blocksignals(SIG_BLOCK);
214 fprintf(stderr,"userv: internal failure: protocol error: ");
215 vfprintf(stderr,fmt,al);
216 fprintf(stderr,"\n");
221 static void priv_suspend(void) { }
222 static void priv_resume(void) { }
223 static void priv_permanentlyrevokesuspended(void) { }
225 static void priv_suspend(void) {
226 if (setreuid(0,myuid) != 0) syscallerror("suspend root setreuid(0,myuid)");
228 static void priv_resume(void) {
229 if (setreuid(myuid,0) != 0) syscallerror("resume root setreuid(myuid,0)");
231 static void priv_permanentlyrevokesuspended(void) {
232 if (setreuid(myuid,myuid) != 0) syscallerror("revoke root setreuid(myuid,myuid)");
233 if (setreuid(myuid,myuid) != 0) syscallerror("rerevoke root setreuid(myuid,myuid)");
235 if (!setreuid(myuid,0)) miscerror("revoked root but setreuid(0,0) succeeded !");
236 if (errno != EPERM) syscallerror("revoked and setreuid(myuid,0) unexpected error");
241 static void *xmalloc(size_t s) {
244 if (!p) syscallerror("malloc (%lu bytes)",(unsigned long)s);
248 static void *xrealloc(void *p, size_t s) {
250 if (!p) syscallerror("realloc (%lu bytes)",(unsigned long)s);
254 static void xfread(void *p, size_t sz, FILE *file) {
256 nr= fread(p,1,sz,file);
257 if (nr != sz) protoreaderror(file,"in data");
260 static void xfwrite(const void *p, size_t sz, FILE *file) {
262 nr= fwrite(p,1,sz,file); if (nr == sz) return;
263 syscallerror("writing to server");
266 static void xfwritestring(const char *s, FILE *file) {
269 assert(l<=MAX_GENERAL_STRING);
270 xfwrite(&l,sizeof(l),file);
271 xfwrite(s,sizeof(*s)*l,file);
274 static void xfflush(FILE *file) {
275 if (fflush(file)) syscallerror("flush server socket");
278 static void usage(void) {
280 "usage: userv <options> [--] <service-user> <service-name> [<argument> ...]\n"
281 "usage: userv <options> -B|--builtin [--] <builtin-service> [<info-argument> ...]\n"
282 "options: -f|--file <fd>[<fdmodifiers>]=<filename>\n"
283 " -D|--defvar <name>=<value>\n"
284 " -t|--timeout <seconds>\n"
285 " -S|--signals <status>|number|number-nocore|highbit|stdout\n"
286 " -w|--fdwait <fd>=wait|nowait|close\n"
287 " -P|--sigpipe -H|--hidecwd -h|--help --copyright\n"
288 " --override <configuration-data> } available only\n"
289 " --override-file <filename> } to root\n"
290 " --spoof-user <username> } or same user\n"
291 "fdmodifiers: read write overwrite trunc[ate]\n"
292 "(separate with commas) append sync excl[usive] creat[e] fd\n\n"
293 "userv and uservd version " VERSION "; copyright (C)1996-1997 Ian Jackson.\n"
294 "there is NO WARRANTY; type `userv --copyright' for details.\n")
295 == EOF) syscallerror("write usage to stderr");
298 static void NONRETURNPRINTFFORMAT(1,2) usageerror(const char *fmt, ...) {
301 fprintf(stderr,"userv: ");
302 vfprintf(stderr,fmt,al);
303 fprintf(stderr,"\n\n");
308 static void addfdmodifier(struct fdsetupstate *fdsus, int fd, const char *key) {
309 const struct fdmodifierinfo *fdmip;
312 for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,key); fdmip++);
313 if (!fdmip->string) usageerror("unknown fdmodifer `%s' for fd %d",key,fd);
314 if (fdmip->conflicts & fdsetup[fd].mods)
315 usageerror("fdmodifier `%s' conflicts with another for fd %d",key,fd);
316 fdsetup[fd].mods |= fdmip->implies;
317 fdsetup[fd].oflags |= fdmip->oflags;
320 static int fdstdnumber(const char *string) {
321 if (!strcmp(string,"stdin")) return 0;
322 else if (!strcmp(string,"stdout")) return 1;
323 else if (!strcmp(string,"stderr")) return 2;
327 static void of_file(const struct optioninfo *oip, const char *value, char *key) {
328 unsigned long fd, copyfd;
333 fd= strtoul(key,&delim,10);
335 delim= strchr(key,',');
336 if (delim) *delim++= 0;
337 fd= fdstdnumber(key);
338 if (fd<0) usageerror("first part of argument to -f or --file must be numeric "
339 "file descriptor or `stdin', `stdout' or `stderr' - `%s' "
340 "is not recognized",key);
342 if (fd > MAX_ALLOW_FD)
343 usageerror("file descriptor specified (%lu) is larger than maximum allowed (%d)",
345 if (fd >= fdsetupsize) {
346 oldarraysize= fdsetupsize;
347 fdsetupsize+=2; fdsetupsize<<=1;
348 fdsetup= xrealloc(fdsetup,sizeof(struct fdsetupstate)*fdsetupsize);
349 while (oldarraysize < fdsetupsize) {
350 fdsetup[oldarraysize].filename= 0;
351 fdsetup[oldarraysize].copyfd= -1;
352 fdsetup[oldarraysize].mods= 0;
353 fdsetup[oldarraysize].catpid= -1;
354 fdsetup[oldarraysize].killed= 0;
355 fdsetup[oldarraysize++].filename= 0;
359 fdsetup[fd].filename= value;
360 fdsetup[fd].oflags= 0;
362 fdsetup[fd].copyfd= -1;
363 while (delim && *delim) {
365 delim= strchr(key,',');
366 if (delim) *delim++= 0;
367 addfdmodifier(&fdsetup[fd],fd,key);
369 if (!(fdsetup[fd].mods & (fdm_read|fdm_write))) {
370 if (fd != 1 && fd != 2) {
371 addfdmodifier(&fdsetup[fd],fd,"read");
372 } else if (fdsetup[fd].mods & fdm_fd) {
373 addfdmodifier(&fdsetup[fd],fd,"write");
375 addfdmodifier(&fdsetup[fd],fd,"overwrite");
378 if (fdsetup[fd].mods & fdm_fd) {
379 copyfd= fdstdnumber(value);
381 copyfd= strtoul(value,&delim,0);
383 usageerror("value part of argument to --file with fd modifier must be "
384 "numeric or fd name- `%s' is not recognised",value);
385 else if (copyfd > MAX_ALLOW_FD)
386 usageerror("file descriptor %lu named as target of file descriptor redirection"
387 " (for file descriptor %lu) is larger than maximum allowed (%d)",
388 copyfd,fd,MAX_ALLOW_FD);
390 do { r= fstat(copyfd,&stab); } while (r && errno==EINTR);
392 if (oip) syscallerror("check filedescriptor %lu (named as target of file "
393 "descriptor redirection for %lu)",copyfd,fd);
394 else syscallerror("check basic filedescriptor %lu at program start",copyfd);
396 fdsetup[fd].copyfd= copyfd;
400 static void of_fdwait(const struct optioninfo *oip, const char *value, char *key) {
401 const struct fdmodifierinfo *fdmip;
406 fd= fdstdnumber(key);
408 ul= strtoul(key,&delim,0);
409 if (*delim) usageerror("first part of argument to --fdwait must be "
410 "numeric or fd name - `%s' is not recognised",key);
411 if (ul>INT_MAX) usageerror("first part of argument to --fdwait is far too large");
414 if (fd >= fdsetupsize || !fdsetup[fd].filename)
415 usageerror("file descriptor %d specified in --fdwait option is not open",fd);
416 for (fdmip= fdmodifierinfos; fdmip->string && strcmp(fdmip->string,value); fdmip++);
417 if (!fdmip->string || !(fdmip->implies & (fdm_wait|fdm_nowait|fdm_close)))
418 usageerror("value for --fdwait must be `wait', `nowait' or `close', not `%s'",value);
419 fdsetup[fd].mods &= ~(fdm_wait|fdm_nowait|fdm_close);
420 fdsetup[fd].mods |= fdmip->implies;
423 static void of_defvar(const struct optioninfo *oip, const char *value, char *key) {
427 usageerror("empty string not allowed as variable name");
428 if (strlen(key)>MAX_GENERAL_STRING)
429 usageerror("variable name `%s' is far too long",key);
430 if (strlen(value)>MAX_GENERAL_STRING)
431 usageerror("variable `%s' has value `%s' which is far too long",key,value);
432 for (i=0; i<defvarused && strcmp(defvararray[i].key,key); i++);
433 if (defvarused >= MAX_ARGSDEFVAR) usageerror("far too many --defvar or -D options");
434 if (i>=defvaravail) {
435 defvaravail+=10; defvaravail<<=1;
436 defvararray= xrealloc(defvararray,sizeof(struct constkeyvaluepair)*defvaravail);
438 if (i==defvarused) defvarused++;
439 defvararray[i].key= key;
440 defvararray[i].value= value;
443 static void of_timeout(const struct optioninfo *oip, const char *value, char *key) {
445 timeout= strtoul(value,&endp,0);
446 if (*endp) usageerror("timeout value `%s' must be a plain decimal string",value);
447 if (timeout>INT_MAX) usageerror("timeout value %lu too large",timeout);
450 static void of_signals(const struct optioninfo *oip, const char *value, char *key) {
451 unsigned long numvalue;
453 numvalue= strtoul(value,&endp,0);
455 if (!strcmp(value,"number")) signalsexit= se_number;
456 else if (!strcmp(value,"number-nocore")) signalsexit= se_numbernocore;
457 else if (!strcmp(value,"highbit")) signalsexit= se_highbit;
458 else if (!strcmp(value,"stdout")) signalsexit= se_stdout;
459 else usageerror("value `%s' for --signals not understood",value);
461 if (numvalue<0 || numvalue>255)
462 usageerror("value %lu for --signals not 0...255",numvalue);
463 signalsexit= numvalue;
467 static void of_sigpipe(const struct optioninfo *oip, const char *value, char *key) {
471 static void of_hidecwd(const struct optioninfo *oip, const char *value, char *key) {
475 static void of_help(const struct optioninfo *oip, const char *value, char *key) {
480 static void of_copyright(const struct optioninfo *oip, const char *value, char *key) {
482 " userv - user service daemon and client; copyright (C)1996-1997 Ian Jackson\n\n"
483 " This is free software; you can redistribute it and/or modify it under the\n"
484 " terms of the GNU General Public License as published by the Free Software\n"
485 " Foundation; either version 2 of the License, or (at your option) any\n"
486 " later version.\n\n"
487 " This program is distributed in the hope that it will be useful, but\n"
488 " WITHOUT ANY WARRANTY; without even the implied warranty of\n"
489 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General\n"
490 " Public License for more details.\n\n"
491 " You should have received a copy of the GNU General Public License along\n"
492 " with userv; if not, write to Ian Jackson <ian@chiark.greenend.org.uk> or\n"
493 " to the Free Software Foundation, 59 Temple Place - Suite 330, Boston,\n"
494 " MA 02111-1307, USA.\n"
495 ) == EOF) syscallerror("write usage to stderr");
499 static void of_builtin(const struct optioninfo *oip, const char *value, char *key) {
500 overridetype= ot_builtin;
503 static void of_override(const struct optioninfo *oip, const char *value, char *key) {
504 overridetype= ot_string;
505 overridevalue= value;
508 static void of_overridefile(const struct optioninfo *oip,
509 const char *value, char *key) {
510 overridetype= ot_file;
511 overridevalue= value;
514 static void of_spoofuser(const struct optioninfo *oip,
515 const char *value, char *key) {
519 const struct optioninfo optioninfos[]= {
520 { 'f', "file", 2, of_file },
521 { 'w', "fdwait", 2, of_fdwait },
522 { 'D', "defvar", 2, of_defvar },
523 { 't', "timeout", 1, of_timeout },
524 { 'S', "signals", 1, of_signals },
525 { 'P', "sigpipe", 0, of_sigpipe },
526 { 'H', "hidecwd", 0, of_hidecwd },
527 { 'B', "builtin", 0, of_builtin },
528 { 'h', "help", 0, of_help },
529 { 0, "copyright", 0, of_copyright },
530 { 0, "override", 1, of_override },
531 { 0, "override-file", 1, of_overridefile },
532 { 0, "spoof-user", 1, of_spoofuser },
536 static void callvalueoption(const struct optioninfo *oip, char *arg) {
538 if (oip->values == 2) {
539 equals= strchr(arg,'=');
542 usageerror("option --%s (-%c) passed argument `%s' with no `='",
543 oip->full,oip->abbrev,arg);
545 usageerror("option --%s passed argument `%s' with no `='",
548 (oip->fn)(oip,equals,arg);
550 (oip->fn)(oip,arg,0);
554 static void checkmagic(unsigned long was, unsigned long should, const char *when) {
556 protoerror("magic number %s was %08lx, expected %08lx",when,was,should);
559 static void getprogress(struct progress_msg *progress_r, FILE *file) {
564 xfread(progress_r,sizeof(struct progress_msg),file);
565 switch (progress_r->type) {
567 blocksignals(SIG_BLOCK);
568 fputs("userv: uservd reports that service failed\n",stderr);
571 blocksignals(SIG_BLOCK);
572 fputs("uservd: ",stderr);
573 if (progress_r->data.errmsg.messagelen>4096)
574 protoerror("stderr message length %d is far too long",
575 progress_r->data.errmsg.messagelen);
576 for (i=0; i<progress_r->data.errmsg.messagelen; i++) {
577 c= getc(file); if (c==EOF) protoreaderror(file,"in error message");
578 if (isprint(c)) putc(c,stderr);
579 else fprintf(stderr,"\\x%02x",(unsigned char)c);
582 if (ferror(stderr)) syscallerror("printing error message");
583 xfread(&ul,sizeof(ul),file);
584 checkmagic(ul,PROGRESS_ERRMSG_END_MAGIC,"after error message");
585 blocksignals(SIG_UNBLOCK);
593 static void xfwritefds(int modifier, int expected, FILE *file) {
596 for (i=0, fdcount=0; i<fdsetupsize; i++) {
597 if (!(fdsetup[i].filename && (fdsetup[i].mods & modifier)))
599 xfwrite(&i,sizeof(int),file); fdcount++;
601 assert(fdcount == expected);
604 static void disconnect(void) /* DOES return, unlike in daemon */ {
605 struct event_msg event_mbuf;
608 fputs("userv: failed, after service program terminated\n",stderr);
611 memset(&event_mbuf,0,sizeof(event_mbuf));
612 event_mbuf.magic= EVENT_MAGIC;
613 event_mbuf.type= et_disconnect;
614 xfwrite(&event_mbuf,sizeof(event_mbuf),swfile);
618 static void sighandler_alrm(int ignored) /* DOES return, unlike in daemon */ {
621 fputs("userv: timeout\n",stderr);
626 static void sighandler_chld(int ignored) /* DOES return, unlike in daemon */ {
627 struct event_msg event_mbuf;
629 int status, fd, r, es;
633 child= wait3(&status,WNOHANG,0);
634 if (child == 0 || (child == -1 && errno == ECHILD)) break;
635 if (child == -1) syscallerror("wait for child process (in sigchld handler)");
636 for (fd=0; fd<fdsetupsize && fdsetup[fd].catpid != child; fd++);
637 if (fd>=fdsetupsize) continue; /* perhaps the invoker gave us children */
638 if ((WIFEXITED(status) && WEXITSTATUS(status)==0) ||
639 (WIFSIGNALED(status) && WTERMSIG(status)==SIGPIPE) ||
640 (fdsetup[fd].killed && WIFSIGNALED(status) && WTERMSIG(status)==SIGKILL)) {
641 if (swfile && fdsetup[fd].mods & fdm_read) {
642 memset(&event_mbuf,0,sizeof(event_mbuf));
643 event_mbuf.magic= EVENT_MAGIC;
644 event_mbuf.type= et_closereadfd;
645 r= fwrite(&event_mbuf,1,sizeof(event_mbuf),swfile);
646 if (r != sizeof(event_mbuf) || fflush(swfile))
647 if (errno != EPIPE) syscallerror("inform service of closed read fd");
650 if (WIFEXITED(status))
651 fprintf(stderr,"userv: cat for fd %d exited with error exit status %d\n",
652 fd,WEXITSTATUS(status));
653 else if (WIFSIGNALED(status))
654 if (WCOREDUMP(status))
655 fprintf(stderr,"userv: cat for fd %d dumped core due to signal %s (%d)\n",
656 fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
658 fprintf(stderr,"userv: cat for fd %d terminated by signal %s (%d)\n",
659 fd,strsignal(WTERMSIG(status)),WTERMSIG(status));
661 fprintf(stderr,"userv: cat for fd %d gave unknown wait status %d\n",
665 fdsetup[fd].catpid= -1;
670 static void catdup(const char *which, int from, int to) {
671 if (dup2(from,to)<0) {
672 blocksignals(SIG_BLOCK);
673 fprintf(stderr,"userv: %s: cannot dup for %s: %s\n",which,
674 to?"stdout":"stdin", strerror(errno));
679 int main(int argc, char *const *argv) {
680 static char fd0key[]= "stdin,fd,read";
681 static char fd1key[]= "stdout,fd,write";
682 static char fd2key[]= "stderr,fd,write";
683 static char stderrbuf[BUFSIZ], stdoutbuf[1024];
687 const struct optioninfo *oip;
688 struct sockaddr_un ssockname;
689 int sfd, ngids, i, tempfd, l, c, reading, fd, r, status, ngidssize;
694 struct opening_msg opening_mbuf;
695 struct request_msg request_mbuf;
696 struct progress_msg progress_mbuf;
697 struct event_msg event_mbuf;
700 gid_t mygid, spoofgid, *gidarray;
707 char pipepathbuf[PIPEPATHMAXLEN], catnamebuf[sizeof(int)*3+30];
708 struct sigaction sig;
711 # error Do not disable assertions in this security-critical code !
714 mypid= getpid(); if (mypid == (pid_t)-1) syscallerror("getpid");
715 myuid= getuid(); if (myuid == (uid_t)-1) syscallerror("getuid");
716 mygid= getgid(); if (mygid == (gid_t)-1) syscallerror("getgid");
717 ngids= getgroups(0,0); if (ngids == (gid_t)-1) syscallerror("getgroups(0,0)");
718 gidarray= xmalloc(sizeof(gid_t)*ngids);
719 if (getgroups(ngids,gidarray) != ngids) syscallerror("getgroups(ngids,)");
723 of_file(0,"stdin",fd0key);
724 of_file(0,"stdout",fd1key);
725 of_file(0,"stderr",fd2key);
728 (argp= *argpp) && *argp == '-' && argp[1];
730 if (!*++argp) usageerror("unknown option/argument `%s'",*argpp);
731 if (*argp == '-') { /* Two hyphens */
732 if (!*++argp) { argpp++; break; /* End of options. */ }
733 for (oip= optioninfos; oip->full && strcmp(oip->full,argp); oip++);
734 if (!oip->full) usageerror("unknown long option `%s'",*argpp);
736 if (!argpp[1]) usageerror("long option `%s' needs a value",*argpp);
737 callvalueoption(oip,*++argpp);
742 for (; *argp; argp++) {
743 for (oip= optioninfos; oip->full && oip->abbrev != *argp; oip++);
744 if (!oip->full) usageerror("unknown short option `-%c' in argument `%s'",
750 if (!argpp[1]) usageerror("short option `-%c' in argument `%s' needs"
751 " a value",*argp,*argpp);
754 callvalueoption(oip,argp);
755 break; /* No more options in this argument, go on to the next one. */
762 if (overridetype == ot_builtin) {
765 if (!*argpp) usageerror("no service user given after options");
766 serviceuser= *argpp++;
768 if (!*argpp) usageerror(overridetype == ot_builtin ?
769 "no service name given after options and service user" :
770 "no builtin service given after options");
772 for (fd=0; fd<fdsetupsize; fd++) {
773 if (!fdsetup[fd].filename) continue;
774 if (fdsetup[fd].mods & (fdm_wait|fdm_nowait|fdm_close)) continue;
775 assert(fdsetup[fd].mods & (fdm_read|fdm_write));
776 fdsetup[fd].mods |= (fdsetup[fd].mods & fdm_read) ? fdm_close : fdm_wait;
779 if (setvbuf(stderr,stderrbuf,_IOLBF,sizeof(stderrbuf)))
780 syscallerror("set buffering on stderr");
781 if (setvbuf(stdout,stdoutbuf,_IOFBF,sizeof(stdoutbuf)))
782 syscallerror("set buffering on stdout");
786 if (argc > MAX_ARGSDEFVAR) usageerror("far too many arguments");
787 if (ngids > MAX_GIDS) miscerror("caller is in far too many gids");
791 logname= getenv("LOGNAME");
792 if (!logname) logname= getenv("USER");
794 pw= getpwnam(logname);
795 if (!pw || pw->pw_uid != myuid) logname= 0;
798 pw= getpwuid(myuid); if (!pw) miscerror("cannot determine your login name");
799 logname= pw->pw_name;
802 if (!strcmp(serviceuser,"-")) serviceuser= logname;
803 pw= getpwnam(serviceuser);
804 if (!pw) miscerror("requested service user `%s' is not a user",serviceuser);
805 serviceuid= pw->pw_uid;
807 if ((overridetype != ot_none || spoofuser) && myuid != 0 && myuid != serviceuid)
808 miscerror("--override and --spoof options only available to root or to"
809 " the user who will be providing the service");
813 pw= getpwnam(logname);
814 if (!pw) miscerror("spoofed login name `%s' is not valid",logname);
815 spoofuid= pw->pw_uid;
816 spoofgid= pw->pw_gid;
817 ngidssize= ngids; ngids= 0;
820 gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
822 gidarray[ngids++]= spoofgid;
823 while ((gr= getgrent())) { /* ouch! getgrent has no error behaviour! */
824 for (mem= gr->gr_mem; *mem && strcmp(*mem,logname); mem++);
826 if (ngids>=ngidssize) {
827 ngidssize= (ngids+5)<<1;
828 gidarray= xrealloc(gidarray,sizeof(gid_t)*ngidssize);
830 gidarray[ngids++]= gr->gr_gid;
834 cwdbufsize= 0; cwdbuf= 0;
837 assert(cwdbufsize < INT_MAX/3);
838 cwdbufsize <<= 1; cwdbufsize+= 100;
839 cwdbuf= xrealloc(cwdbuf,cwdbufsize);
840 cwdbuf[cwdbufsize-1]= 0;
841 if (getcwd(cwdbuf,cwdbufsize-1)) { cwdbufsize= strlen(cwdbuf); break; }
842 if (errno != ERANGE) { cwdbufsize= 0; break; }
846 switch (overridetype) {
853 if (l >= MAX_OVERRIDE_LEN-20)
854 miscerror("builtin service string is too long (%d, max is %d)",
855 l,MAX_OVERRIDE_LEN-21);
858 snprintf(ovbuf,l,"execute-builtin %s\n",argv[0]);
859 ovused= strlen(ovbuf);
862 l= strlen(overridevalue);
863 if (l >= MAX_OVERRIDE_LEN)
864 miscerror("override string is too long (%d, max is %d)",l,MAX_OVERRIDE_LEN-1);
866 snprintf(ovbuf,l+2,"%s\n",overridevalue);
870 ovfile= fopen(overridevalue,"r");
871 if (!ovfile) syscallerror("open overriding configuration file `%s'",overridevalue);
872 ovbuf= 0; ovavail= ovused= 0;
873 while ((c= getc(ovfile)) != EOF) {
874 if (!c) miscerror("overriding config file `%s' contains null(s)",overridevalue);
875 if (ovused >= MAX_OVERRIDE_LEN)
876 miscerror("override file is too long (max is %d)",MAX_OVERRIDE_LEN);
877 if (ovused >= ovavail) {
878 ovavail+=80; ovavail<<=2; ovbuf= xrealloc(ovbuf,ovavail);
882 if (ferror(ovfile) || fclose(ovfile))
883 syscallerror("read overriding configuration file `%s'",overridevalue);
884 ovbuf= xrealloc(ovbuf,ovused+1);
891 sig.sa_handler= SIG_IGN;
892 sigemptyset(&sig.sa_mask);
894 if (sigaction(SIGPIPE,&sig,0)) syscallerror("ignore sigpipe");
896 sfd= socket(AF_UNIX,SOCK_STREAM,0);
897 if (!sfd) syscallerror("create client socket");
899 assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
900 ssockname.sun_family= AF_UNIX;
901 strcpy(ssockname.sun_path,RENDEZVOUSPATH);
903 while (connect(sfd,(struct sockaddr*)&ssockname,sizeof(ssockname))) {
904 if (errno == ECONNREFUSED || errno == ENOENT)
905 syscallerror("uservd daemon is not running - service not available");
906 syscallerror("unable to connect to uservd daemon");
910 srfile= fdopen(sfd,"r");
911 if (!srfile) syscallerror("turn socket fd into FILE* for read");
912 if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
914 swfile= fdopen(sfd,"w");
915 if (!swfile) syscallerror("turn socket fd into FILE* for write");
916 if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
918 xfread(&opening_mbuf,sizeof(opening_mbuf),srfile);
919 checkmagic(opening_mbuf.magic,OPENING_MAGIC,"in opening message");
920 if (memcmp(protocolchecksumversion,opening_mbuf.protocolchecksumversion,PCSUMSIZE))
921 protoerror("protocol version checksum mismatch - server not same as client");
923 for (fd=0; fd<fdsetupsize; fd++) {
924 if (!fdsetup[fd].filename) continue;
925 sprintf(pipepathbuf, PIPEPATHFORMAT,
926 (unsigned long)mypid, (unsigned long)opening_mbuf.serverpid, fd);
928 if (unlink(pipepathbuf) && errno != ENOENT)
929 syscallerror("remove any old pipe `%s'",pipepathbuf);
930 if (mkfifo(pipepathbuf,0600)) /* permissions are irrelevant */
931 syscallerror("create pipe `%s'",pipepathbuf);
932 tempfd= open(pipepathbuf,O_RDWR);
933 if (tempfd == -1) syscallerror("prelim open pipe `%s' for read+write",pipepathbuf);
934 assert(fdsetup[fd].mods & (fdm_read|fdm_write));
936 open(pipepathbuf, (fdsetup[fd].mods & fdm_read) ? O_WRONLY : O_RDONLY);
937 if (fdsetup[fd].pipefd == -1) syscallerror("real open pipe `%s'",pipepathbuf);
938 if (close(tempfd)) syscallerror("close prelim fd onto pipe `%s'",pipepathbuf);
942 memset(&request_mbuf,0,sizeof(request_mbuf));
943 request_mbuf.magic= REQUEST_MAGIC;
944 request_mbuf.clientpid= getpid();
945 request_mbuf.serviceuserlen= strlen(serviceuser);
946 request_mbuf.servicelen= strlen(argv[0]);
947 request_mbuf.lognamelen= strlen(logname);
948 request_mbuf.cwdlen= cwdbufsize;
949 request_mbuf.callinguid= spoofuid;
950 request_mbuf.ngids= ngids+1;
951 request_mbuf.nreadfds= 0;
952 request_mbuf.nwritefds= 0;
953 for (fd=0; fd<fdsetupsize; fd++) {
954 if (!fdsetup[fd].filename) continue;
955 assert(fdsetup[fd].mods & (fdm_write|fdm_read));
956 if (fdsetup[fd].mods & fdm_write) request_mbuf.nwritefds++;
957 else request_mbuf.nreadfds++;
959 request_mbuf.nargs= argc-1;
960 request_mbuf.nvars= defvarused;
961 request_mbuf.overridelen= ovused;
962 xfwrite(&request_mbuf,sizeof(request_mbuf),swfile);
963 xfwrite(serviceuser,sizeof(*serviceuser)*request_mbuf.serviceuserlen,swfile);
964 xfwrite(argv[0],sizeof(*argv[0])*request_mbuf.servicelen,swfile);
965 xfwrite(logname,sizeof(*logname)*request_mbuf.lognamelen,swfile);
966 xfwrite(cwdbuf,sizeof(*cwdbuf)*request_mbuf.cwdlen,swfile);
967 if (ovused>=0) xfwrite(ovbuf,sizeof(*ovbuf)*ovused,swfile);
968 xfwrite(&spoofgid,sizeof(gid_t),swfile);
969 xfwrite(gidarray,sizeof(gid_t)*ngids,swfile);
970 xfwritefds(fdm_read,request_mbuf.nreadfds,swfile);
971 xfwritefds(fdm_write,request_mbuf.nwritefds,swfile);
972 for (i=1; i<argc; i++)
973 xfwritestring(argv[i],swfile);
974 for (i=0; i<defvarused; i++) {
975 xfwritestring(defvararray[i].key,swfile);
976 xfwritestring(defvararray[i].value,swfile);
978 ul= REQUEST_END_MAGIC; xfwrite(&ul,sizeof(ul),swfile);
981 priv_permanentlyrevokesuspended(); /* Must not do this before we give our real id */
983 getprogress(&progress_mbuf,srfile);
984 if (progress_mbuf.type != pt_ok)
985 protoerror("progress message during configuration phase"
986 " unexpected type %d",progress_mbuf.type);
988 sig.sa_handler= sighandler_chld;
989 sigemptyset(&sig.sa_mask);
990 sigaddset(&sig.sa_mask,SIGCHLD);
991 sigaddset(&sig.sa_mask,SIGALRM);
993 if (sigaction(SIGCHLD,&sig,0)) syscallerror("set up sigchld handler");
995 sig.sa_handler= sighandler_alrm;
996 if (sigaction(SIGALRM,&sig,0)) syscallerror("set up sigalrm handler");
998 for (fd=0; fd<fdsetupsize; fd++) {
999 if (!fdsetup[fd].filename) continue;
1000 if (!(fdsetup[fd].mods & fdm_fd)) {
1002 open(fdsetup[fd].filename,fdsetup[fd].oflags,0777);
1003 if (fdsetup[fd].copyfd<0)
1004 syscallerror("open file `%s' for fd %d",fdsetup[fd].filename,fd);
1006 fdsetup[fd].catpid= fork();
1007 if (fdsetup[fd].catpid==-1) syscallerror("fork for cat for fd %d",fd);
1008 if (!fdsetup[fd].catpid) {
1009 snprintf(catnamebuf,sizeof(catnamebuf),"cat fd%d",fd);
1010 catnamebuf[sizeof(catnamebuf)-1]= 0;
1011 sig.sa_handler= SIG_DFL;
1012 sigemptyset(&sig.sa_mask);
1014 if (sigaction(SIGPIPE,&sig,0)) {
1015 fprintf(stderr,"userv: %s: reset sigpipe handler for cat: %s",
1016 catnamebuf,strerror(errno));
1019 reading= fdsetup[fd].mods & fdm_read;
1020 catdup(catnamebuf, fdsetup[fd].copyfd, reading ? 0 : 1);
1021 catdup(catnamebuf, fdsetup[fd].pipefd, reading ? 1 : 0);
1022 execl("/bin/cat",catnamebuf,(char*)0);
1023 fprintf(stderr,"userv: %s: cannot exec `cat': %s\n",catnamebuf,strerror(errno));
1026 if (fdsetup[fd].copyfd>2)
1027 if (close(fdsetup[fd].copyfd)) syscallerror("close real fd for %d",fd);
1028 if (close(fdsetup[fd].pipefd)) syscallerror("close pipe fd for %d",fd);
1032 if (alarm(timeout)<0) syscallerror("set up timeout alarm");
1034 blocksignals(SIG_BLOCK);
1035 memset(&event_mbuf,0,sizeof(event_mbuf));
1036 event_mbuf.magic= EVENT_MAGIC;
1037 event_mbuf.type= et_confirm;
1038 xfwrite(&event_mbuf,sizeof(event_mbuf),swfile);
1040 blocksignals(SIG_UNBLOCK);
1042 getprogress(&progress_mbuf,srfile);
1043 if (progress_mbuf.type != pt_terminated)
1044 protoerror("progress message during execution phase"
1045 " unexpected type %d",progress_mbuf.type);
1049 blocksignals(SIG_BLOCK);
1050 for (fd=0; fd<fdsetupsize; fd++) {
1051 if (!(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_close))) continue;
1052 if (kill(fdsetup[fd].catpid,SIGKILL)) syscallerror("kill cat for %d",fd);
1053 fdsetup[fd].killed= 1;
1055 blocksignals(SIG_UNBLOCK);
1058 blocksignals(SIG_BLOCK);
1060 fd<fdsetupsize && !(fdsetup[fd].catpid!=-1 && (fdsetup[fd].mods & fdm_wait));
1062 if (fd>=fdsetupsize) break;
1064 r= sigsuspend(&sset);
1065 if (r && errno != EINTR) syscallerror("sigsuspend failed in unexpected way");
1066 blocksignals(SIG_UNBLOCK);
1069 blocksignals(SIG_BLOCK);
1071 status= progress_mbuf.data.terminated.status;
1072 if (sigpipeok && signalsexit != se_stdout && WIFSIGNALED(status) &&
1073 WTERMSIG(status)==SIGPIPE && !WCOREDUMP(status)) status= 0;
1075 switch (signalsexit) {
1077 case se_numbernocore:
1078 if (WIFEXITED(status))
1079 _exit(WEXITSTATUS(status));
1080 else if (WIFSIGNALED(status))
1081 _exit(WTERMSIG(status) + (signalsexit==se_number && WCOREDUMP(status) ? 128 : 0));
1084 if (WIFEXITED(status))
1085 _exit(WEXITSTATUS(status)<=127 ? WEXITSTATUS(status) : 127);
1086 else if (WIFSIGNALED(status) && WTERMSIG(status)<=126)
1087 _exit(WTERMSIG(status)+128);
1090 printf("\n%d %d ",(status>>8)&0x0ff,status&0x0ff);
1091 if (WIFEXITED(status))
1092 printf("exited with code %d",WEXITSTATUS(status));
1093 else if (WIFSIGNALED(status))
1094 printf("killed by %s (signal %d)%s",
1095 strsignal(WTERMSIG(status)),WTERMSIG(status),
1096 WCOREDUMP(status) ? ", core dumped " : "");
1098 printf("unknown wait status");
1100 if (ferror(stdout) || fflush(stdout)) syscallerror("write exit status to stdout");
1103 if (WIFEXITED(status))
1104 _exit(WEXITSTATUS(status));
1105 else if (WIFSIGNALED(status))
1110 fprintf(stderr,"unknown wait status %d\n",status);