#include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "fatal.h" #include "subproc.h" struct proc { pid_t kid; int ac; const char **av; int ofd; int efd; MEMSTREAM **omp; MEMSTREAM **emp; } ; 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; MEMSTREAM **omp; MEMSTREAM **emp; int ip[2]; int op[2]; int ep[2]; pid_t kid; 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_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; 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_OUT: omp = va_arg(ap,MEMSTREAM **); 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 (nullin || 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 || infdp) { close(ip[1]); while (ip[0] < 3) ip[0] = dup(ip[0]); } if (omp) { close(op[0]); while (op[1] < 3) op[1] = dup(op[1]); } if (emp) { close(ep[0]); while (op[1] < 3) ep[1] = dup(ep[1]); } if (nullin || infdp) { dup2(ip[0],0); close(ip[0]); } if (omp) { dup2(op[1],1); close(op[1]); } if (emp) { dup2(ep[1],2); close(ep[1]); } 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]; } else if (nullin) { close(ip[0]); close(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,0,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) { break; } } free(p->av); free(p); return(status); }