/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; static const char *dev_null_path = "/dev/null"; #include "fatal.h" #include "subproc.h" struct proc { pid_t kid; int ac; const char **av; int ofd; int efd; MEMSTREAM **omp; MEMSTREAM **emp; } ; static int dev_null_fd = -1; static int nlfd(void) { if (dev_null_fd < 0) { dev_null_fd = open(dev_null_path,O_RDWR,0); if (dev_null_fd < 0) { fprintf(stderr,"%s: open %s: %s\n",__progname,dev_null_path,strerror(errno)); exit(1); } while (dev_null_fd < 3) dev_null_fd = dup(dev_null_fd); } return(dev_null_fd); } PROC *proc_start(int key0, ...) { PROC *p; va_list ap; int ac; const char **av; int key; int n; int i; const char **vec; int wd; int nullin; int *infdp; int nullout; MEMSTREAM **omp; int nullerr; MEMSTREAM **emp; int ip[2]; int op[2]; int ep[2]; pid_t kid; i = nlfd(); while (i < 3) i = dup(i); va_start(ap,key0); ac = 0; key = key0; while <"args"> (1) { switch (key) { case PROC_N: n = va_arg(ap,int); ac += n; for (;n>0;n--) (void) va_arg(ap,const char *); break; case PROC_VEC: vec = va_arg(ap,const char **); for (n=0;vec[n];n++) ; ac += n; break; case PROC_FCHDIR: (void) va_arg(ap,int); break; case PROC_NULLIN: break; case PROC_INFD: (void) va_arg(ap,int *); break; case PROC_NULLOUT: case PROC_NULLERR: break; case PROC_OUT: case PROC_ERR: (void) va_arg(ap,MEMSTREAM **); break; case PROC_END: break <"args">; } key = va_arg(ap,int); } va_end(ap); av = malloc((ac+1)*sizeof(const char *)); wd = -1; nullin = 0; nullout = 0; nullerr = 0; infdp = 0; omp = 0; emp = 0; va_start(ap,key0); i = 0; key = key0; while <"args"> (1) { switch (key) { case PROC_N: n = va_arg(ap,int); for (;n>0;n--) av[i++] = va_arg(ap,const char *); break; case PROC_VEC: vec = va_arg(ap,const char **); for (n=0;vec[n];n++) av[i++] = vec[n]; break; case PROC_FCHDIR: wd = va_arg(ap,int); break; case PROC_NULLIN: nullin = 1; break; case PROC_INFD: infdp = va_arg(ap,int *); break; case PROC_NULLOUT: nullout = 1; break; case PROC_OUT: omp = va_arg(ap,MEMSTREAM **); break; case PROC_NULLERR: nullerr = 1; break; case PROC_ERR: emp = va_arg(ap,MEMSTREAM **); break; case PROC_END: break <"args">; } key = va_arg(ap,int); } va_end(ap); if (i != ac) abort(); if (nullin && infdp) abort(); av[i] = 0; if (infdp) { if (socketpair(AF_LOCAL,SOCK_STREAM,0,&ip[0]) < 0) { fatal("socketpair: %s",strerror(errno)); exit(1); } shutdown(ip[0],SHUT_WR); shutdown(ip[1],SHUT_RD); } if ( (omp && (pipe(&op[0]) < 0)) || (emp && (pipe(&ep[0]) < 0)) ) { fatal("pipe: %s",strerror(errno)); exit(1); } fflush(0); kid = fork(); if (kid < 0) { fatal("fork: %s",strerror(errno)); exit(1); } if (kid == 0) { if ((wd >= 0) && (fchdir(wd) < 0)) { fprintf(stderr,"%s: can't fchdir: %s\n",__progname,strerror(errno)); _exit(1); } if (nullin) { dup2(nlfd(),0); } else if (infdp) { close(ip[1]); dup2(ip[0],0); close(ip[0]); } if (nullout) { dup2(nlfd(),1); } else if (omp) { close(op[0]); dup2(op[1],1); close(op[1]); } if (nullerr) { dup2(nlfd(),2); } else if (emp) { close(ep[0]); dup2(ep[1],2); close(ep[1]); } close(nlfd()); execvp(av[0],(const void *)av); fprintf(stderr,"%s: exec %s: %s\n",__progname,av[0],strerror(errno)); _exit(1); } p = malloc(sizeof(PROC)); p->kid = kid; p->ac = ac; p->av = av; if (infdp) { close(ip[0]); *infdp = ip[1]; } if (omp) { close(op[1]); p->ofd = op[0]; } else { p->ofd = -1; } p->omp = omp; if (emp) { close(ep[1]); p->efd = ep[0]; } else { p->efd = -1; } p->emp = emp; return(p); } int proc_await(PROC *p) { MEMSTREAM *om; MEMSTREAM *em; struct pollfd pfd[2]; int px; int opx; int epx; int rv; char rbuf[8192]; int status; pid_t dead; if (p->omp) om = memstream_open_w(); if (p->emp) em = memstream_open_w(); while (1) { px = 0; if (p->ofd >= 0) { opx = px; pfd[px++] = (struct pollfd) { .fd = p->ofd, .events = POLLIN | POLLRDNORM }; } else { opx = -1; } if (p->efd >= 0) { epx = px; pfd[px++] = (struct pollfd) { .fd = p->efd, .events = POLLIN | POLLRDNORM }; } else { epx = -1; } if (px == 0) break; rv = poll(&pfd[0],px,INFTIM); if (rv < 0) { if (errno == EINTR) continue; fatal("poll: %s",strerror(errno)); } if ((opx >= 0) && (pfd[opx].revents & (POLLIN|POLLRDNORM|POLLHUP|POLLERR|POLLNVAL))) { rv = read(p->ofd,&rbuf[0],sizeof(rbuf)); if (rv < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: break; default: fatal("pipe read: %s",strerror(errno)); break; } } else if (rv == 0) { close(p->ofd); p->ofd = -1; } else { memstream_append(om,&rbuf[0],rv); } } if ((epx >= 0) && (pfd[epx].revents & (POLLIN|POLLRDNORM|POLLHUP|POLLERR|POLLNVAL))) { rv = read(p->efd,&rbuf[0],sizeof(rbuf)); if (rv < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: break; default: fatal("pipe read: %s",strerror(errno)); break; } } else if (rv == 0) { close(p->efd); p->efd = -1; } else { memstream_append(em,&rbuf[0],rv); } } } if (p->omp) *p->omp = om; if (p->emp) *p->emp = em; while (1) { dead = wait4(p->kid,&status,WUNTRACED,0); if (dead < 0) { switch (errno) { case EINTR: break; default: fatal("wait4: %s",strerror(errno)); break; } } else if (dead == 0) { fatal("wait4 strange zero return"); } else if (dead == p->kid) { if (WIFSTOPPED(status)) { kill(0,WSTOPSIG(status)); kill(dead,SIGCONT); continue; } break; } } free(p->av); free(p); return(status); }