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;
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;
71 static struct passwd *servicepw, *callingpw;
72 static const char **grouparray;
74 static void sigchildhandler(int x) {
80 r= waitpid((pid_t)-1,&status,WNOHANG);
81 if (!r || (r==-1 && errno==ECHILD)) break;
82 if (r==-1) { syslog(LOG_ERR,"wait in sigchild handler gave error: %m"); break; }
83 if (WIFSIGNALED(status))
84 if (WCOREDUMP(status))
85 syslog(LOG_ERR,"call pid %ld dumped core due to signal %s",(long)r,
86 strsignal(WTERMSIG(status)));
88 syslog(LOG_ERR,"call pid %ld died due to signal %s",
89 (long)r,strsignal(WTERMSIG(status)));
90 else if (!WIFEXITED(status))
91 syslog(LOG_ERR,"call pid %ld died due to unknown reason, code %ld",
93 else if (WEXITSTATUS(status)>24)
94 syslog(LOG_ERR,"call pid %ld exited with status %ld >24",
95 (long)r,WEXITSTATUS(status));
101 static void xfread(void *p, size_t sz) {
103 nr= fread(p,1,sz,srfile); if (nr == sz) return;
104 if (ferror(srfile)) syscallerror("reading from client");
105 assert(feof(srfile));
106 syslog(LOG_DEBUG,"client went away (unexpected EOF)");
111 static void xfwrite(const void *p, size_t sz, FILE *file) {
113 nr= fwrite(p,1,sz,file); if (nr == sz) return;
114 syscallerror("writing to client");
117 static char *xfreadsetstring(int l) {
120 xfread(s,sizeof(*s)*l);
125 static char *xfreadstring(void) {
127 xfread(&l,sizeof(l));
128 return xfreadsetstring(l);
131 static void xfflush(FILE *file) {
132 if (fflush(file)) syscallerror("flush client socket");
135 void ensurefdarray(int fd) {
136 if (fd < fdarrayused) return;
137 if (fd >= fdarraysize) {
138 fdarraysize= ((fd+2)<<1);
139 fdarray= xrealloc(fdarray,sizeof(struct fdstate)*fdarraysize);
141 while (fd >= fdarrayused) {
142 fdarray[fdarrayused].iswrite= -1;
143 fdarray[fdarrayused].realfd= -1;
144 fdarray[fdarrayused].wantstate= restfdwantstate;
145 fdarray[fdarrayused].wantrw= restfdwantrw;
150 void ensurelogopen(int wantfacility) {
151 if (syslogopenfacility==wantfacility) return;
152 if (syslogopenfacility!=-1) closelog();
153 openlog(USERVD_LOGIDENT,LOG_NDELAY|LOG_PID,wantfacility);
154 syslogopenfacility= wantfacility;
157 void senderrmsgstderr(const char *errmsg) {
158 struct progress_msg progress_mbuf;
163 memset(&progress_mbuf,0,sizeof(progress_mbuf));
164 progress_mbuf.magic= PROGRESS_MAGIC;
165 progress_mbuf.type= pt_errmsg;
166 progress_mbuf.data.errmsg.messagelen= l;
167 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
168 xfwrite(errmsg,l,swfile);
169 ul= PROGRESS_ERRMSG_END_MAGIC;
170 xfwrite(&ul,sizeof(ul),swfile);
174 void miscerror(const char *what) {
175 syslog(LOG_ERR,"failure: %s",what);
179 void syscallerror(const char *what) {
183 syslog(LOG_ERR,"system call failure: %s: %s",what,strerror(e));
187 static void NONRETURNING generalfailure(const char *prefix, int reserveerrno,
188 int errnoval, const char *fmt, va_list al) {
189 char errmsg[MAX_ERRMSG_LEN];
192 strnycpy(errmsg,prefix,sizeof(errmsg));
193 strnytcat(errmsg,": ",sizeof(errmsg));
197 vsnytprintfcat(errmsg,sizeof(errmsg)-reserveerrno,fmt,al);
199 strnytcat(errmsg,": ",sizeof(errmsg));
200 strnytcat(errmsg,strerror(errnoval),sizeof(errmsg));
202 senderrmsgstderr(errmsg);
203 syslog(LOG_DEBUG,"service failed (%s)",errmsg);
207 static void NONRETURNPRINTFFORMAT(1,2) failure(const char *fmt, ...) {
211 generalfailure(0,0,0,fmt,al);
214 static void NONRETURNPRINTFFORMAT(1,2) syscallfailure(const char *fmt, ...) {
220 generalfailure("system call failed",ERRMSG_RESERVE_ERRNO,e,fmt,al);
223 void NONRETURNING disconnect(int exitstatus) {
224 /* This function can sometimes indirectly call itself (eg,
225 * xfwrite, syscallerror can cause it to be called). So, all
226 * the global variables indicating need for action are reset
227 * before the action is taken so that if it fails it isn't
230 struct progress_msg progress_mbuf;
235 if (childtokill!=-1 && disconnecthup) {
236 orgtokill= childtokill;
239 r= kill(-orgtokill,SIGHUP);
240 if (r && errno!=EPERM && errno!=ESRCH)
241 syscallerror("sending SIGHUP to service process group");
248 memset(&progress_mbuf,0,sizeof(progress_mbuf));
249 progress_mbuf.magic= PROGRESS_MAGIC;
250 progress_mbuf.type= pt_failed;
251 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfilereal);
258 static void NONRETURNING syscallservfail(const char *msg) {
259 fputs("uservd(service): ",stderr);
264 static void servresetsig(int signo) {
265 struct sigaction sig;
267 sig.sa_handler= SIG_DFL;
268 sigemptyset(&sig.sa_mask);
270 if (sigaction(signo,&sig,0)) syscallservfail("reset signal handler");
273 static int synchread(int fd, int ch) {
278 r= read(fd,&synchmsg,1);
280 if (r==0) { errno= ECONNRESET; return -1; }
282 if (errno!=EINTR) return -1;
284 if (synchmsg != ch) { errno= EPROTO; return -1; }
288 static const char *see_logname(void) { return servicepw->pw_name; }
289 static const char *see_home(void) { return servicepw->pw_dir; }
290 static const char *see_shell(void) { return servicepw->pw_shell; }
292 static const char *see_path(void) {
293 return servicepw->pw_uid ?
294 "/usr/local/bin:/bin:/usr/bin" :
295 "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin";
298 static const char *see_service(void) { return service; }
299 static const char *see_c_cwd(void) { return cwd; }
300 static const char *see_c_logname(void) { return logname; }
301 static const char *see_c_uid(void) {
302 static char buf[CHAR_BIT*sizeof(uid_t)/3+4];
303 snyprintf(buf,sizeof(buf),"%lu",(unsigned long)callingpw->pw_uid);
307 static const char *see_c_list(int n, const char *(*fn)(int i)) {
311 for (i=0, l=1; i<n; i++) l+= strlen(fn(i))+1;
312 r= xmalloc(l); r[l-1]= '*';
313 for (i=0, *r=0; i<n; i++) snytprintfcat(r,l,"%s ",fn(i));
314 assert(!r[l-1] && r[l-2]==' ');
319 static const char *seei_group(int i) {
320 return grouparray[i];
322 static const char *see_c_group(void) {
323 return see_c_list(request_mbuf.ngids,seei_group);
326 static const char *seei_gid(int i) {
327 static char buf[CHAR_BIT*sizeof(gid_t)/3+4];
328 snyprintf(buf,sizeof(buf),"%d",gidarray[i]);
331 static const char *see_c_gid(void) {
332 return see_c_list(request_mbuf.ngids,seei_gid);
335 static const struct servenvinfo {
337 const char *(*fn)(void);
339 { "USER", see_logname },
340 { "LOGNAME", see_logname },
341 { "HOME", see_home },
342 { "SHELL", see_shell },
343 { "PATH", see_path },
344 { "USERV_SERVICE", see_service },
345 { "USERV_CWD", see_c_cwd },
346 { "USERV_USER", see_c_logname },
347 { "USERV_UID", see_c_uid },
348 { "USERV_GROUP", see_c_group },
349 { "USERV_GID", see_c_gid },
353 static void NONRETURNING execservice(const int synchsocket[]) {
354 static const char *const setenvpfargs[]= {
357 ". " SYSTEMCONFIGDIR "/environment; exec \"$@\"",
361 int fd, realfd, holdfd, newfd, r, envvarbufsize=0, targ, nargs, i, l;
363 const char **args, *const *cpp;
366 const struct servenvinfo *sei;
368 if (dup2(fdarray[2].realfd,2)<0) {
369 static const char duperrmsg[]= "uservd(service): cannot dup2 for stderr\n";
370 write(fdarray[2].realfd,duperrmsg,sizeof(duperrmsg)-1);
373 if (close(synchsocket[0])) syscallservfail("close parent synch socket");
375 if (setpgid(0,0)) syscallservfail("set process group");
377 r= write(synchsocket[1],&synchmsg,1);
378 if (r!=1) syscallservfail("write synch byte to parent");
379 r= synchread(synchsocket[1],'g');
380 if (r) syscallservfail("reach synch byte from parent");
382 if (close(fileno(swfile))) syscallservfail("close client socket fd");
383 for (fd=0; fd<fdarrayused; fd++) {
384 if (fdarray[fd].holdfd == -1) continue;
385 if (close(fdarray[fd].holdfd)) syscallservfail("close pipe hold fd");
386 fdarray[fd].holdfd= -1;
388 for (fd=0; fd<fdarrayused; fd++) {
389 if (fdarray[fd].realfd < fdarrayused) fdarray[fdarray[fd].realfd].holdfd= fd;
391 for (fd=0; fd<fdarrayused; fd++) {
392 realfd= fdarray[fd].realfd;
393 if (realfd == -1) continue;
394 holdfd= fdarray[fd].holdfd;
396 assert(realfd == fd);
397 fdarray[fd].holdfd= -1;
399 } else if (holdfd != -1) {
400 assert(fdarray[holdfd].realfd == fd);
401 newfd= dup(fd); if (newfd<0) syscallservfail("dup out of the way");
402 fdarray[holdfd].realfd= newfd;
403 if (newfd<fdarrayused) fdarray[newfd].holdfd= holdfd;
404 fdarray[fd].holdfd= -1;
406 if (dup2(fdarray[fd].realfd,fd)<0) syscallservfail("dup2 set up fd");
407 if (close(fdarray[fd].realfd)) syscallservfail("close old fd");
408 if (fcntl(fd,F_SETFD,0)<0) syscallservfail("set no-close-on-exec on fd");
409 fdarray[fd].realfd= fd;
411 servresetsig(SIGPIPE);
412 servresetsig(SIGCHLD);
414 for (sei= servenvinfos; sei->name; sei++)
415 if (setenv(sei->name,sei->fn(),1)) syscallservfail("setenv standard");
416 for (i=0; i<request_mbuf.nvars; i++) {
417 l= strlen(defvararray[i][0])+9;
418 if (l>envvarbufsize) { envvarbufsize= l; envvarbuf= xrealloc(envvarbuf,l); }
419 snyprintf(envvarbuf,l,"USERV_U_%s",defvararray[i][0]);
420 if (setenv(envvarbuf,defvararray[i][1],1)) syscallservfail("setenv defvar");
424 if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) nargs++;
426 if (execargs) for (pp= execargs; *pp; pp++) nargs++;
427 if (!suppressargs) nargs+= request_mbuf.nargs;
428 args= xmalloc(sizeof(char*)*(nargs+1));
430 if (setenvironment) for (cpp= setenvpfargs; *cpp; cpp++) args[targ++]= *cpp;
431 args[targ++]= execpath;
432 if (execargs) for (pp= execargs; *pp; pp++) args[targ++]= *pp;
433 if (!suppressargs) for (i=0; i<request_mbuf.nargs; i++) args[targ++]= argarray[i];
436 execv(args[0],(char* const*)args);
438 syscallservfail("exec service program");
442 static void NONRETURNING sighandler_pipe(int ignored) {
444 ensurelogopen(USERVD_LOGFACILITY);
445 syslog(LOG_DEBUG,"client went away (server got sigpipe)");
449 static void NONRETURNING sighandler_chld(int ignored) {
450 struct progress_msg progress_mbuf;
454 returned= wait3(&status,WNOHANG,0);
455 if (returned==-1) syscallerror("wait for child failed");
456 if (!returned) syscallfailure("spurious sigchld");
457 if (returned!=child) syscallfailure("spurious child process (pid %ld)",(long)returned);
458 child= childtokill= -1;
460 memset(&progress_mbuf,0,sizeof(progress_mbuf));
461 progress_mbuf.magic= PROGRESS_MAGIC;
462 progress_mbuf.type= pt_terminated;
463 progress_mbuf.data.terminated.status= status;
464 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
467 syslog(LOG_DEBUG,"service completed (status %d %d)",(status>>8)&0x0ff,status&0x0ff);
471 static void getevent(struct event_msg *event_r) {
475 xfread(event_r,sizeof(struct event_msg));
476 switch (event_r->type) {
478 fd= event_r->data.closereadfd.fd;
479 assert(fd<fdarrayused);
480 assert(fdarray[fd].holdfd!=-1);
481 if (close(fdarray[fd].holdfd)) syscallfailure("cannot close holding fd %d",fd);
484 syslog(LOG_DEBUG,"client disconnected");
492 static void NONRETURNING servicerequest(int sfd) {
493 struct opening_msg opening_mbuf;
494 struct progress_msg progress_mbuf;
495 struct event_msg event_mbuf;
496 pid_t mypid, newchild;
498 int i,j, r, tempfd, fd, partsize, synchsocket[2];
499 char pipepathbuf[PIPEPATHMAXLEN];
500 const char *string, *delim, *nextstring;
501 char *part, *exectry;
504 struct sigaction sig;
507 ensurelogopen(USERVD_LOGFACILITY);
508 syslog(LOG_DEBUG,"call connected");
510 mypid= getpid(); if (mypid == -1) syscallerror("getpid");
512 srfile= fdopen(sfd,"r");
513 if (!srfile) syscallerror("turn socket fd into reading FILE*");
514 if (setvbuf(srfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket reads");
516 swfile= fdopen(sfd,"w");
517 if (!swfile) syscallerror("turn socket fd into writing FILE*");
518 if (setvbuf(swfile,0,_IOFBF,BUFSIZ)) syscallerror("set buffering on socket writes");
520 opening_mbuf.magic= OPENING_MAGIC;
521 memcpy(opening_mbuf.protocolchecksumversion,protocolchecksumversion,PCSUMSIZE);
522 opening_mbuf.serverpid= mypid;
523 xfwrite(&opening_mbuf,sizeof(opening_mbuf),swfile);
526 xfread(&request_mbuf,sizeof(request_mbuf));
527 serviceuser= xfreadsetstring(request_mbuf.serviceuserlen);
528 service= xfreadsetstring(request_mbuf.servicelen);
529 logname= xfreadsetstring(request_mbuf.lognamelen);
530 cwd= xfreadsetstring(request_mbuf.cwdlen);
531 if (request_mbuf.overridelen >= 0) {
532 assert(request_mbuf.overridelen <= MAX_OVERRIDE_LEN);
533 overridedata= xfreadsetstring(request_mbuf.overridelen);
537 gidarray= xmalloc(sizeof(gid_t)*request_mbuf.ngids);
538 xfread(gidarray,sizeof(gid_t)*request_mbuf.ngids);
540 fdarraysize= 4; fdarray= xmalloc(sizeof(struct fdstate)*fdarraysize);
541 fdarrayused= 1; fdarray[0].iswrite= -1;
542 fdarray[0].wantstate= tokv_word_rejectfd;
543 for (i=0; i<request_mbuf.nreadfds+request_mbuf.nwritefds; i++) {
544 xfread(&fd,sizeof(int));
546 assert(fdarray[fd].iswrite == -1);
547 fdarray[fd].iswrite= (i>=request_mbuf.nreadfds);
550 argarray= xmalloc(sizeof(char*)*(request_mbuf.nargs));
551 for (i=0; i<request_mbuf.nargs; i++) argarray[i]= xfreadstring();
552 defvararray= xmalloc(sizeof(char*)*request_mbuf.nvars*2);
553 for (i=0; i<request_mbuf.nvars; i++)
554 for (j=0; j<2; j++) defvararray[i][j]= xfreadstring();
555 xfread(&ul,sizeof(ul));
556 assert(ul == REQUEST_END_MAGIC);
558 for (fd=0; fd<fdarrayused; fd++) {
559 if (fdarray[fd].iswrite == -1) continue;
560 sprintf(pipepathbuf, PIPEPATHFORMAT, (unsigned long)request_mbuf.clientpid,
561 (unsigned long)mypid, fd);
562 tempfd= open(pipepathbuf,O_RDWR);
563 if (tempfd == -1) syscallerror("prelim open pipe");
564 if (!fdarray[fd].iswrite) {
565 fdarray[fd].holdfd= open(pipepathbuf, O_WRONLY);
566 if (fdarray[fd].holdfd == -1) syscallerror("hold open pipe");
567 fdarray[fd].realfd= open(pipepathbuf, O_RDONLY);
569 fdarray[fd].holdfd= -1;
570 fdarray[fd].realfd= open(pipepathbuf, O_WRONLY);
572 if (fdarray[fd].realfd == -1) syscallerror("real open pipe");
573 if (unlink(pipepathbuf)) syscallerror("unlink pipe");
574 if (close(tempfd)) syscallerror("close prelim fd onto pipe");
577 servicepw= getpwnam(serviceuser);
578 if (!servicepw) syscallerror("look up service user");
579 assert(!strcmp(servicepw->pw_name,serviceuser));
580 serviceuser_dir= xstrdup(nondebug_serviceuserdir(servicepw->pw_dir));
581 serviceuser_shell= xstrdup(servicepw->pw_shell);
582 serviceuser_uid= servicepw->pw_uid;
583 serviceuser_gid= servicepw->pw_gid;
584 if (initgroups(servicepw->pw_name,servicepw->pw_gid)) syscallerror("initgroups");
585 if (setreuid(servicepw->pw_uid,servicepw->pw_uid)) syscallerror("setreuid 1");
586 if (setreuid(servicepw->pw_uid,servicepw->pw_uid)) syscallerror("setreuid 2");
587 if (servicepw->pw_uid)
588 if (!setreuid(servicepw->pw_uid,0)) miscerror("setreuid 3 unexpectedly succeeded");
589 if (errno != EPERM) syscallerror("setreuid 3 failed in unexpected way");
591 debug_dumprequest(mypid);
593 callingpw= getpwnam(logname);
594 if (!callingpw) syscallerror("get passwd entry for calling user");
595 grouparray= xmalloc(sizeof(char*)*request_mbuf.ngids);
596 for (i=0; i<request_mbuf.ngids; i++) {
597 cgrp= getgrgid(gidarray[i]);
598 if (!cgrp) syscallerror("get group entry for calling group");
599 grouparray[i]= xmstrsave(cgrp->gr_name);
603 r= parse_string(TOPLEVEL_OVERRIDDEN_CONFIGURATION,
604 "<builtin toplevel override configuration>");
606 r= parse_string(TOPLEVEL_CONFIGURATION,
607 "<builtin toplevel configuration>");
610 ensurelogopen(USERVD_LOGFACILITY);
612 if (r == tokv_error) failure("error encountered while parsing configuration files");
613 assert(r == tokv_quit);
615 debug_dumpexecsettings();
618 case tokv_word_reject:
619 failure("request rejected");
620 case tokv_word_execute:
621 r= stat(execpath,&stab);
622 if (r) syscallfailure("checking for executable `%s'",execpath);
624 case tokv_word_executefromdirectory:
625 r= stat(execpath,&stab);
626 if (r) syscallfailure("checking for executable in directory, `%s'",execpath);
628 case tokv_word_executefrompath:
629 if (strchr(service,'/')) {
630 r= stat(service,&stab);
631 if (r) syscallfailure("execute-from-path (contains slash)"
632 " cannot check for executable `%s'",service);
635 string= getenv("PATH");
636 if (!string) failure("execute-from-path, but daemon inherited no PATH !");
638 delim= strchr(string,':');
640 if (delim-string > INT_MAX)
641 failure("execute-from-path, but PATH component too long");
642 partsize= delim-string;
645 partsize= strlen(string);
648 part= xmstrsubsave(string,partsize);
649 exectry= part[0] ? xmstrcat3save(part,"/",service) : xmstrsave(service);
651 r= stat(exectry,&stab);
652 if (!r) { execpath= exectry; break; }
656 if (!execpath) failure("execute-from-path, but program `%s' not found",service);
663 assert(fdarrayused>=2);
664 if (!(fdarray[2].wantstate == tokv_word_requirefd ||
665 fdarray[2].wantstate == tokv_word_allowfd) ||
666 fdarray[2].wantrw != tokv_word_write)
667 failure("must have stderr (fd 2), but file descriptor setup in "
668 "configuration does not have it or not for writing");
670 for (fd=0; fd<fdarrayused; fd++) {
671 switch (fdarray[fd].wantstate) {
672 case tokv_word_rejectfd:
673 if (fdarray[fd].realfd != -1)
674 failure("file descriptor %d provided but rejected",fd);
676 case tokv_word_ignorefd:
677 if (fdarray[fd].realfd != -1)
678 if (close(fdarray[fd].realfd))
679 syscallfailure("close unwanted file descriptor %d",fd);
680 fdarray[fd].realfd= -1;
682 case tokv_word_nullfd:
683 if (fdarray[fd].realfd != -1) close(fdarray[fd].realfd);
684 fdarray[fd].realfd= open("/dev/null",
685 fdarray[fd].iswrite == -1 ? O_RDWR :
686 fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
687 if (fdarray[fd].realfd == -1)
688 syscallfailure("cannot open /dev/null for null fd");
690 case tokv_word_requirefd:
691 if (fdarray[fd].realfd == -1)
692 failure("file descriptor %d not provided but required",fd);
694 case tokv_word_allowfd:
695 if (fdarray[fd].realfd == -1) {
696 fdarray[fd].iswrite= (fdarray[fd].wantrw == tokv_word_write);
697 fdarray[fd].realfd= open("/dev/null",fdarray[fd].iswrite ? O_WRONLY : O_RDONLY);
698 if (fdarray[fd].realfd == -1)
699 syscallfailure("cannot open /dev/null for allowed but not provided fd");
701 if (fdarray[fd].iswrite) {
702 if (fdarray[fd].wantrw != tokv_word_write)
703 failure("file descriptor %d provided write, wanted read",fd);
705 if (fdarray[fd].wantrw != tokv_word_read)
706 failure("file descriptor %d provided read, wanted write",fd);
712 memset(&progress_mbuf,0,sizeof(progress_mbuf));
713 progress_mbuf.magic= PROGRESS_MAGIC;
714 progress_mbuf.type= pt_ok;
715 xfwrite(&progress_mbuf,sizeof(progress_mbuf),swfile);
718 r= socketpair(AF_UNIX,SOCK_STREAM,0,synchsocket);
719 if (r) syscallfailure("cannot create socket for synch");
721 sig.sa_handler= sighandler_pipe;
722 sigemptyset(&sig.sa_mask);
723 sigaddset(&sig.sa_mask,SIGPIPE);
724 sigaddset(&sig.sa_mask,SIGCHLD);
726 if (sigaction(SIGPIPE,&sig,0)) syscallfailure("cannot set sigpipe handler");
728 sig.sa_handler= sighandler_chld;
729 if (sigaction(SIGCHLD,&sig,0)) syscallfailure("cannot set sigchld handler");
731 getevent(&event_mbuf);
732 assert(event_mbuf.type == et_confirm);
735 if (newchild == -1) syscallfailure("cannot fork to invoke service");
736 if (!newchild) execservice(synchsocket);
737 childtokill= child= newchild;
739 if (close(synchsocket[1])) syscallfailure("cannot close other end of synch socket");
741 r= synchread(synchsocket[0],'y');
742 if (r) syscallfailure("read synch byte from child");
747 r= write(synchsocket[0],&synchmsg,1);
748 if (r!=1) syscallerror("write synch byte to child");
750 if (close(synchsocket[0])) syscallfailure("cannot close my end of synch socket");
752 getevent(&event_mbuf);
756 int main(int argc, char *const *argv) {
757 int mfd, sfd, csocklen;
758 struct sigaction childact;
759 struct sockaddr_un ssockname, csockname;
762 abort(); /* Do not disable assertions in this security-critical code ! */
765 if (argc>1) { fputs("usage: uservd\n",stderr); exit(3); }
767 openlog(USERVD_LOGIDENT,LOG_NDELAY,USERVD_LOGFACILITY);
768 mfd= socket(AF_UNIX,SOCK_STREAM,0);
769 if (!mfd) { syslog(LOG_CRIT,"cannot create master socket: %m"); exit(4); }
771 assert(sizeof(ssockname.sun_path) > sizeof(RENDEZVOUSPATH));
772 ssockname.sun_family= AF_UNIX;
773 strcpy(ssockname.sun_path,RENDEZVOUSPATH);
774 unlink(RENDEZVOUSPATH);
775 if (bind(mfd,(struct sockaddr*)&ssockname,sizeof(ssockname)))
776 { syslog(LOG_CRIT,"cannot bind master socket: %m"); exit(4); }
778 { syslog(LOG_CRIT,"cannot listen on master socket: %m"); exit(4); }
780 childact.sa_handler= sigchildhandler;
781 sigemptyset(&childact.sa_mask);
782 childact.sa_flags= SA_NOCLDSTOP;
783 if (sigaction(SIGCHLD,&childact,0))
784 { syslog(LOG_CRIT,"cannot setup sigchld handler: %m"); exit(4); }
785 syslog(LOG_NOTICE,"started");
787 csocklen= sizeof(csockname);
788 sfd= accept(mfd,(struct sockaddr*)&csockname,&csocklen);
790 if (errno == EINTR) continue;
791 if (errno == ENOMEM) {
792 syslog(LOG_ERR,"unable to accept connection: %m"); continue;
794 syslog(LOG_CRIT,"unable to accept new connections: %m"); exit(5);
796 child= nondebug_fork();
797 if (child == (pid_t)-1) {
798 syslog(LOG_ERR,"unable to fork server: %m"); close(sfd); continue;
801 close(mfd); closelog(); servicerequest(sfd);