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.
34 #include <sys/types.h>
35 #include <sys/fcntl.h>
36 #include <sys/socket.h>
48 /* NB: defaults for the execution state are not set here, but in
49 * the RESET_CONFIGURATION #define in daemon.h. */
52 char *((*defvararray)[2])=0;
53 struct fdstate *fdarray=0;
54 int fdarraysize=0, fdarrayused=0;
55 int restfdwantstate= tokv_word_rejectfd, restfdwantrw= 0;
56 struct request_msg request_mbuf;
57 char *serviceuser=0, *service=0, *logname=0, *cwd=0;
58 char *overridedata=0, *userrcfile=0;
59 char *serviceuser_dir=0, *serviceuser_shell=0, *callinguser_shell;
60 uid_t serviceuser_uid=-1;
61 gid_t serviceuser_gid=-1;
62 char *execpath=0, **execargs=0;
63 int execute, setenvironment, suppressargs, disconnecthup, ehandling;
64 int ehlogfacility=0, ehloglevel=0, ehfilekeep=0, syslogopenfacility=-1;
68 static FILE *swfile= 0, *srfile= 0;
69 static pid_t child= -1, childtokill= -1;
70 static const char **grouparray;
72 static void sigchildhandler(int x) {
78 r= waitpid((pid_t)-1,&status,WNOHANG);
79 if (!r || (r==-1 && errno==ECHILD)) break;
80 if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
81 if (WIFSIGNALED(status))
82 if (WCOREDUMP(status))
83 syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
84 strsignal(WTERMSIG(status)));
86 syslog(LOG_ERR,"call pid %ld died due to signal %s",
87 (long)r,strsignal(WTERMSIG(status)));
88 else if (!WIFEXITED(status))
89 syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
91 else if (WEXITSTATUS(status)>24)
92 syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
93 (long)r,WEXITSTATUS(status));
99 static void xfread(void *p, size_t sz) {
101 nr= fread(p,1,sz,srfile); if (nr == sz) return;
102 if (ferror(srfile)) syscallerror("reading from client");
103 assert(feof(srfile));
104 syslog(LOG_DEBUG,"client went away (unexpected EOF)");
109 static void xfwrite(const void *p, size_t sz, FILE *file) {
111 nr= fwrite(p,1,sz,file); if (nr == sz) return;
112 syscallerror("writing to client");
115 static char *xfreadsetstring(int l) {
118 xfread(s,sizeof(*s)*l);
123 static char *xfreadstring(void) {
125 xfread(&l,sizeof(l));
126 return xfreadsetstring(l);
129 static void xfflush(FILE *file) {
130 if (fflush(file)) syscallerror("flush client socket");
133 void ensurefdarray(int fd) {
134 if (fd < fdarrayused) return;
135 if (fd >= fdarraysize) {
136 fdarraysize= ((fd+2)<<1);
137 fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize);
139 while (fd >= fdarrayused) {
140 fdarray[fdarrayused].iswrite= -1;
141 fdarray[fdarrayused].realfd= -1;
142 fdarray[fdarrayused].wantstate= restfdwantstate;
143 fdarray[fdarrayused].wantrw= restfdwantrw;
148 void ensurelogopen(int wantfacility) {
149 if (syslogopenfacility==wantfacility) return;
150 if (syslogopenfacility!=-1) closelog();
151 openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,wantfacility);
152 syslogopenfacility= wantfacility;
155 void senderrmsgstderr(const char *errmsg) {
156 struct progress_msg progress_mbuf;
161 memset(&progress_mbuf,0,sizeof(progress_mbuf));
162 progress_mbuf.magic= PROGRESS_MAGIC;
163 progress_mbuf.type= pt_errmsg;
164 progress_mbuf.data.errmsg.messagelen= l;
165 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
166 xfwrite(errmsg,l,swfile);
167 ul= PROGRESS_ERRMSG_END_MAGIC;
168 xfwrite(&ul,sizeof(ul),swfile);
172 void miscerror(const char *what) {
173 syslog(LOG_ERR,"failure: %s",what);
177 void syscallerror(const char *what) {
181 syslog(LOG_ERR,"system call failure: %s: %s",what,strerror(e));
185 static void NONRETURNING generalfailure(const char *prefix, int reserveerrno,
186 int errnoval, const char *fmt, va_list al) {
187 char errmsg[MAX_ERRMSG_LEN];
190 strnycpy(errmsg,prefix,sizeof(errmsg));
191 strnytcat(errmsg,": ",sizeof(errmsg));
195 vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al);
197 strnytcat(errmsg,": ",sizeof(errmsg));
198 strnytcat(errmsg,strerror(errnoval),sizeof(errmsg));
200 senderrmsgstderr(errmsg);
201 syslog(LOG_DEBUG,"service failed (%s)",errmsg);
205 static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) {
209 generalfailure(0,0,0,fmt,al);
212 static void NONRETURNPRINTFFORMAT(1,2) syscallfailure(const char *fmt, ...) {
218 generalfailure("system call failed",ERRMSG_RESERVE_ERRNO,e,fmt,al);
221 void NONRETURNING disconnect(int exitstatus) {
222 /* This function can sometimes indirectly call itself (eg,
223 * xfwrite, syscallerror can cause it to be called). So, all
224 * the global variables indicating need for action are reset
225 * before the action is taken so that if it fails it isn't
228 struct progress_msg progress_mbuf;
233 if (childtokill!=-1 && disconnecthup) {
234 orgtokill= childtokill;
237 r= kill(-orgtokill,SIGHUP);
238 if (r && errno!=EPERM && errno!=ESRCH)
239 syscallerror("sending SIGHUP to service process group");
246 memset(&progress_mbuf,0,sizeof(progress_mbuf));
247 progress_mbuf.magic= PROGRESS_MAGIC;
248 progress_mbuf.type= pt_failed;
249 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfilereal);
256 static void NONRETURNING syscallservfail(const char *msg) {
257 fputs("uservd(service): ",stderr);
262 static void servresetsig(int signo) {
263 struct sigaction sig;
265 sig.sa_handler= SIG_DFL;
266 sigemptyset(&sig.sa_mask);
268 if (sigaction(signo,&sig,0)) syscallservfail("reset signal handler");
271 static int synchread(int fd, int ch) {
276 r= read(fd,&synchmsg,1);
278 if (r==0) { errno= ECONNRESET; return -1; }
280 if (errno!=EINTR) return -1;
282 if (synchmsg != ch) { errno= EPROTO; return -1; }
286 static const char *see_logname(void) { return serviceuser; }
287 static const char *see_home(void) { return serviceuser_dir; }
288 static const char *see_shell(void) { return serviceuser_shell; }
290 static const char *see_path(void) {
291 return serviceuser_uid ?
292 "/usr/local/bin:/bin:/usr/bin" :
293 "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin";
296 static const char *see_service(void) { return service; }
297 static const char *see_c_cwd(void) { return cwd; }
298 static const char *see_c_logname(void) { return logname; }
299 static const char *see_c_uid(void) {
300 static char buf[CHAR_BIT*sizeof(uid_t)/3+4];
301 snyprintf(buf,sizeof(buf),"%lu",(unsigned long)request_mbuf.callinguid);
305 static const char *see_c_list(int n, const char *(*fn)(int i)) {
309 for (i=0, l=1; i<n; i++) l+= strlen(fn(i))+1;
310 r= xmalloc(l); r[l-1]= '*';
311 for (i=0, *r=0; i<n; i++) snytprintfcat(r,l,"%s ",fn(i));
312 assert(!r[l-1] && r[l-2]==' ');
317 static const char *seei_group(int i) {
318 return grouparray[i];
320 static const char *see_c_group(void) {
321 return see_c_list(request_mbuf.ngids,seei_group);
324 static const char *seei_gid(int i) {
325 static char buf[CHAR_BIT*sizeof(gid_t)/3+4];
326 snyprintf(buf,sizeof(buf),"%d",gidarray[i]);
329 static const char *see_c_gid(void) {
330 return see_c_list(request_mbuf.ngids,seei_gid);
333 static const struct servenvinfo {
335 const char *(*fn)(void);
337 { "USER", see_logname },
338 { "LOGNAME", see_logname },
339 { "HOME", see_home },
340 { "SHELL", see_shell },
341 { "PATH", see_path },
342 { "USERV_SERVICE", see_service },
343 { "USERV_CWD", see_c_cwd },
344 { "USERV_USER", see_c_logname },
345 { "USERV_UID", see_c_uid },
346 { "USERV_GROUP", see_c_group },
347 { "USERV_GID", see_c_gid },
351 static void NONRETURNING execservice(const int synchsocket[]) {
352 static const char *const setenvpfargs[]= {
355 ". " SYSTEMCONFIGDIR "/environment; exec \"$@\"",
359 int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l;
361 const char **args, *const *cpp;
364 const struct servenvinfo *sei;
366 if (dup2(fdarray[2].realfd,2)<0) {
367 static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n";
368 write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1);
371 if (close(synchsocket[0])) syscallservfail("close parent synch socket");
373 if (setpgid(0,0)) syscallservfail("set process group");
375 r= write(synchsocket[1],&synchmsg,1);
376 if (r!=1) syscallservfail("write synch byte to parent");
377 r= synchread(synchsocket[1],'g');
378 if (r) syscallservfail("reach synch byte from parent");
380 if (close(fileno(swfile))) syscallservfail("close client socket fd");
381 for (fd=0; fd<fdarrayused; fd++) {
382 if (fdarray[fd].holdfd == -1) continue;
383 if (close(fdarray[fd].holdfd)) syscallservfail("close pipe hold fd");
384 fdarray[fd].holdfd= -1;
386 for (fd=0; fd<fdarrayused; fd++) {
387 if (fdarray[fd].realfd < fdarrayused) fdarray[fdarray[fd].realfd].holdfd= fd;
389 for (fd=0; fd<fdarrayused; fd++) {
390 realfd= fdarray[fd].realfd;
391 if (realfd == -1) continue;
392 holdfd= fdarray[fd].holdfd;
394 assert(realfd == fd);
395 fdarray[fd].holdfd= -1;
397 } else if (holdfd != -1) {
398 assert(fdarray[holdfd].realfd == fd);
399 newfd= dup(fd); if (newfd<0) syscallservfail("dup out of the way");
400 fdarray[holdfd].realfd= newfd;
401 if (newfd<fdarrayused) fdarray[newfd].holdfd= holdfd;
402 fdarray[fd].holdfd= -1;
404 if (dup2(fdarray[fd].realfd,fd)<0) syscallservfail("dup2 set up fd");
405 if (close(fdarray[fd].realfd)) syscallservfail("close old fd");
406 if (fcntl(fd,F_SETFD,0)<0) syscallservfail("set no-close-on-exec on fd");
407 fdarray[fd].realfd= fd;
409 servresetsig(SIGPIPE);
410 servresetsig(SIGCHLD);
412 for (sei= servenvinfos; sei->name; sei++)
413 if (setenv(sei->name,sei->fn(),1)) syscallservfail("setenv standard");
414 for (i=0; i<request_mbuf.nvars; i++) {
415 l= strlen(defvararray[i][0])+9;
416 if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
417 snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
418 if (setenv(envvarbuf,defvararray[i][1],1)) syscallservfail("setenv defvar");
422 if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++;
424 if (execargs) for (pp= execargs; *pp; pp++) nargs++;
425 if (!suppressargs) nargs+= request_mbuf.nargs;
426 args= xmalloc(sizeof(char*)*(nargs+1));
428 if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp;
429 args[targ++]= execpath;
430 if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp;
431 if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
434 execv(args[0],(char* const*)args);
436 syscallservfail("exec service program");
440 static void NONRETURNING sighandler_pipe(int ignored) {
442 ensurelogopen(USERVD_LOGFACILITY);
443 syslog(LOG_DEBUG,"client went away (server got sigpipe)");
447 static void NONRETURNING sighandler_chld(int ignored) {
448 struct progress_msg progress_mbuf;
452 returned= wait3(&status,WNOHANG,0);
453 if (returned==-1) syscallerror("wait for child failed");
454 if (!returned) syscallfailure("spurious sigchld");
455 if (returned!=child) syscallfailure("spurious child process (pid %ld)",(long)returned);
456 child= childtokill= -1;
458 memset(&progress_mbuf,0,sizeof(progress_mbuf));
459 progress_mbuf.magic= PROGRESS_MAGIC;
460 progress_mbuf.type= pt_terminated;
461 progress_mbuf.data.terminated.status= status;
462 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
465 syslog(LOG_DEBUG,"service completed (status %d %d)",(status>>8)&0x0ff,status&0x0ff);
469 static void getevent(struct event_msg *event_r) {
473 xfread(event_r,sizeof(struct event_msg));
474 switch (event_r->type) {
476 fd= event_r->data.closereadfd.fd;
477 assert(fd<fdarrayused);
478 assert(fdarray[fd].holdfd!=-1);
479 if (close(fdarray[fd].holdfd)) syscallfailure("cannot close holding fd %d",fd);
482 syslog(LOG_DEBUG,"client disconnected");
490 static void NONRETURNING servicerequest(int sfd) {
491 struct opening_msg opening_mbuf;
492 struct progress_msg progress_mbuf;
493 struct event_msg event_mbuf;
494 pid_t mypid, newchild;
496 int i,j, r, tempfd, fd, partsize, synchsocket[2];
497 char pipepathbuf[PIPEPATHMAXLEN];
498 const char *string, *delim, *nextstring;
499 char *part, *exectry;
502 struct sigaction sig;
506 ensurelogopen(USERVD_LOGFACILITY);
507 syslog(LOG_DEBUG,"call connected");
509 mypid= getpid(); if (mypid == -1) syscallerror("getpid");
511 srfile= fdopen(sfd,"r");
512 if (!srfile) syscallerror("turn socket fd into reading FILE*");
513 if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
515 swfile= fdopen(sfd,"w");
516 if (!swfile) syscallerror("turn socket fd into writing FILE*");
517 if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
519 opening_mbuf.magic= OPENING_MAGIC;
520 memcpy(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE);
521 opening_mbuf.serverpid= mypid;
522 xfwrite(&opening_mbuf,sizeof(opening_mbuf),swfile);
525 xfread(&request_mbuf,sizeof(request_mbuf));
526 serviceuser= xfreadsetstring(request_mbuf.serviceuserlen);
527 service= xfreadsetstring(request_mbuf.servicelen);
528 logname= xfreadsetstring(request_mbuf.lognamelen);
529 cwd= xfreadsetstring(request_mbuf.cwdlen);
530 if (request_mbuf.overridelen >= 0) {
531 assert(request_mbuf.overridelen <= MAX_OVERRIDE_LEN);
532 overridedata= xfreadsetstring(request_mbuf.overridelen);
536 gidarray= xmalloc(sizeof(gid_t)*request_mbuf.ngids);
537 xfread(gidarray,sizeof(gid_t)*request_mbuf.ngids);
539 fdarraysize= 4; fdarray= xmalloc(sizeof(struct fdstate)*fdarraysize);
540 fdarrayused= 1; fdarray[0].iswrite= -1;
541 fdarray[0].wantstate= tokv_word_rejectfd;
542 for (i=0; i<request_mbuf.nreadfds+request_mbuf.nwritefds; i++) {
543 xfread(&fd,sizeof(int));
545 assert(fdarray[fd].iswrite == -1);
546 fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
549 argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs));
550 for (i=0; i<request_mbuf.nargs; i++) argarray[i]= xfreadstring();
551 defvararray= xmalloc(sizeof(char*)*request_mbuf.nvars*2);
552 for (i=0; i<request_mbuf.nvars; i++)
553 for (j=0; j<2; j++) defvararray[i][j]= xfreadstring();
554 xfread(&ul,sizeof(ul));
555 assert(ul == REQUEST_END_MAGIC);
557 for (fd=0; fd<fdarrayused; fd++) {
558 if (fdarray[fd].iswrite == -1) continue;
559 sprintf(pipepathbuf, PIPEPATHFORMAT, (unsigned long)request_mbuf.clientpid,
560 (unsigned long)mypid, fd);
561 tempfd= open(pipepathbuf,O_RDWR);
562 if (tempfd == -1) syscallerror("prelim open pipe");
563 if (!fdarray[fd].iswrite) {
564 fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
565 if (fdarray[fd].holdfd == -1) syscallerror("hold open pipe");
566 fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
568 fdarray[fd].holdfd= -1;
569 fdarray[fd].realfd= open(pipepathbuf, O_WRONLY);
571 if (fdarray[fd].realfd == -1) syscallerror("real open pipe");
572 if (unlink(pipepathbuf)) syscallerror("unlink pipe");
573 if (close(tempfd)) syscallerror("close prelim fd onto pipe");
576 pw= getpwnam(serviceuser);
577 if (!pw) miscerror("look up service user");
578 assert(!strcmp(pw->pw_name,serviceuser));
579 serviceuser_dir= xstrdup(nondebug_serviceuserdir(pw->pw_dir));
580 serviceuser_shell= xstrdup(pw->pw_shell);
581 serviceuser_uid= pw->pw_uid;
582 serviceuser_gid= pw->pw_gid;
583 if (initgroups(pw->pw_name,pw->pw_gid)) syscallerror("initgroups");
584 if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 1");
585 if (setreuid(pw->pw_uid,pw->pw_uid)) syscallerror("setreuid 2");
587 if (!setreuid(pw->pw_uid,0)) miscerror("setreuid 3 unexpectedly succeeded");
588 if (errno != EPERM) syscallerror("setreuid 3 failed in unexpected way");
590 debug_dumprequest(mypid);
592 grouparray= xmalloc(sizeof(char*)*request_mbuf.ngids);
593 for (i=0; i<request_mbuf.ngids; i++) {
594 cgrp= getgrgid(gidarray[i]);
595 if (!cgrp) miscerror("get group entry for calling group");
596 grouparray[i]= xmstrsave(cgrp->gr_name);
600 r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION,
601 "<builtin toplevel override configuration>");
603 r= parse_string(TOPLEVEL_CONFIGURATION,
604 "<builtin toplevel configuration>");
607 ensurelogopen(USERVD_LOGFACILITY);
609 if (r == tokv_error) failure("error encountered while parsing configuration files");
610 assert(r == tokv_quit);
612 debug_dumpexecsettings();
615 case tokv_word_reject:
616 failure("request rejected");
617 case tokv_word_execute:
618 r= stat(execpath,&stab);
619 if (r) syscallfailure("checking for executable `%s'",execpath);
621 case tokv_word_executefromdirectory:
622 r= stat(execpath,&stab);
623 if (r) syscallfailure("checking for executable in directory, `%s'",execpath);
625 case tokv_word_executefrompath:
626 if (strchr(service,'/')) {
627 r= stat(service,&stab);
628 if (r) syscallfailure("execute-from-path (contains slash)"
629 " cannot check for executable `%s'",service);
632 string= getenv("PATH");
633 if (!string) failure("execute-from-path, but daemon inherited no PATH !");
635 delim= strchr(string,':');
637 if (delim-string > INT_MAX)
638 failure("execute-from-path, but PATH component too long");
639 partsize= delim-string;
642 partsize= strlen(string);
645 part= xmstrsubsave(string,partsize);
646 exectry= part[0] ? xmstrcat3save(part,"/",service) : xmstrsave(service);
648 r= stat(exectry,&stab);
649 if (!r) { execpath= exectry; break; }
653 if (!execpath) failure("execute-from-path, but program `%s' not found",service);
660 assert(fdarrayused>=2);
661 if (!(fdarray[2].wantstate == tokv_word_requirefd ||
662 fdarray[2].wantstate == tokv_word_allowfd) ||
663 fdarray[2].wantrw != tokv_word_write)
664 failure("must have stderr (fd 2), but file descriptor setup in "
665 "configuration does not have it or not for writing");
667 for (fd=0; fd<fdarrayused; fd++) {
668 switch (fdarray[fd].wantstate) {
669 case tokv_word_rejectfd:
670 if (fdarray[fd].realfd != -1)
671 failure("file descriptor %d provided but rejected",fd);
673 case tokv_word_ignorefd:
674 if (fdarray[fd].realfd != -1)
675 if (close(fdarray[fd].realfd))
676 syscallfailure("close unwanted file descriptor %d",fd);
677 fdarray[fd].realfd= -1;
679 case tokv_word_nullfd:
680 if (fdarray[fd].realfd != -1) close(fdarray[fd].realfd);
681 fdarray[fd].realfd= open("/dev/null",
682 fdarray[fd].iswrite == -1 ? O_RDWR :
683 fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
684 if (fdarray[fd].realfd == -1)
685 syscallfailure("cannot open /dev/null for null fd");
687 case tokv_word_requirefd:
688 if (fdarray[fd].realfd == -1)
689 failure("file descriptor %d not provided but required",fd);
691 case tokv_word_allowfd:
692 if (fdarray[fd].realfd == -1) {
693 fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write);
694 fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
695 if (fdarray[fd].realfd == -1)
696 syscallfailure("cannot open /dev/null for allowed but not provided fd");
698 if (fdarray[fd].iswrite) {
699 if (fdarray[fd].wantrw != tokv_word_write)
700 failure("file descriptor %d provided write, wanted read",fd);
702 if (fdarray[fd].wantrw != tokv_word_read)
703 failure("file descriptor %d provided read, wanted write",fd);
709 memset(&progress_mbuf,0,sizeof(progress_mbuf));
710 progress_mbuf.magic= PROGRESS_MAGIC;
711 progress_mbuf.type= pt_ok;
712 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
715 r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket);
716 if (r) syscallfailure("cannot create socket for synch");
718 sig.sa_handler= sighandler_pipe;
719 sigemptyset(&sig.sa_mask);
720 sigaddset(&sig.sa_mask,SIGPIPE);
721 sigaddset(&sig.sa_mask,SIGCHLD);
723 if (sigaction(SIGPIPE,&sig,0)) syscallfailure("cannot set sigpipe handler");
725 sig.sa_handler= sighandler_chld;
726 if (sigaction(SIGCHLD,&sig,0)) syscallfailure("cannot set sigchld handler");
728 getevent(&event_mbuf);
729 assert(event_mbuf.type == et_confirm);
732 if (newchild == -1) syscallfailure("cannot fork to invoke service");
733 if (!newchild) execservice(synchsocket);
734 childtokill= child= newchild;
736 if (close(synchsocket[1])) syscallfailure("cannot close other end of synch socket");
738 r= synchread(synchsocket[0],'y');
739 if (r) syscallfailure("read synch byte from child");
744 r= write(synchsocket[0],&synchmsg,1);
745 if (r!=1) syscallerror("write synch byte to child");
747 if (close(synchsocket[0])) syscallfailure("cannot close my end of synch socket");
749 getevent(&event_mbuf);
753 int main(int argc, char *const *argv) {
754 int mfd, sfd, csocklen;
755 struct sigaction childact;
756 struct sockaddr_un ssockname, csockname;
759 abort(); /* Do not disable assertions in this security-critical code ! */
762 if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
764 openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
765 mfd= socket(AF_UNIX,SOCK_STREAM,0);
766 if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
768 assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
769 ssockname.sun_family= AF_UNIX;
770 strcpy(ssockname.sun_path,RENDEZVOUSPATH);
771 unlink(RENDEZVOUSPATH);
772 if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
773 { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
775 { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
777 childact.sa_handler= sigchildhandler;
778 sigemptyset(&childact.sa_mask);
779 childact.sa_flags= SA_NOCLDSTOP;
780 if (sigaction(SIGCHLD,&childact,0))
781 { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
782 syslog(LOG_NOTICE,"started");
784 csocklen= sizeof(csockname);
785 sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
787 if (errno == EINTR) continue;
788 if (errno == ENOMEM) {
789 syslog(LOG_ERR,"unable to accept connection: %m"); continue;
791 syslog(LOG_CRIT,"unable to accept new connections: %m"); exit(5);
793 child= nondebug_fork();
794 if (child == (pid_t)-1) {
795 syslog(LOG_ERR,"unable to fork server: %m"); close(sfd); continue;
798 close(mfd); closelog(); servicerequest(sfd);