#include #include #include #include #include #include #include #include #include extern const char *__progname; #include "fatal.h" #include "subproc.h" void proc_start(int *ifdp, int *ofdp, int *efdp, ...) { va_list ap; int ac; const char **av; int key; int n; int i; const char **vec; int ip[2]; int op[2]; int ep[2]; pid_t kid; va_start(ap,efdp); ac = 0; while <"args"> (1) { key = va_arg(ap,int); 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_END: break <"args">; } } va_end(ap); av = malloc((ac+1)*sizeof(const char *)); va_start(ap,efdp); i = 0; while <"args"> (1) { key = va_arg(ap,int); 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_END: break <"args">; } } va_end(ap); if (i != ac) abort(); av[i] = 0; if ( (pipe(&ip[0]) < 0) || (pipe(&op[0]) < 0) || (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) { close(ip[1]); close(op[0]); close(ep[0]); while (ip[0] < 3) ip[0] = dup(ip[0]); while (op[1] < 3) op[1] = dup(op[1]); while (op[1] < 3) ep[1] = dup(ep[1]); dup2(ip[0],0); close(ip[0]); dup2(op[1],1); close(op[1]); 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); } close(ip[0]); close(op[1]); close(ep[1]); if (ifdp) { *ifdp = ip[1]; fcntl(ip[1],F_SETFL,fcntl(ip[1],F_GETFL,0)|O_NONBLOCK); } else { close(ip[1]); } if (ofdp) { *ofdp = op[0]; fcntl(op[0],F_SETFL,fcntl(op[0],F_GETFL,0)|O_NONBLOCK); } else { close(op[0]); } if (efdp) { *efdp = ep[0]; fcntl(ep[0],F_SETFL,fcntl(ep[0],F_GETFL,0)|O_NONBLOCK); } else { close(ep[0]); } free(av); } void proc_gather_outputs(int ofd, MEMSTREAM **omp, int efd, MEMSTREAM **emp) { MEMSTREAM *om; MEMSTREAM *em; struct pollfd pfd[2]; int px; int opx; int epx; int rv; char rbuf[8192]; om = memstream_open_w(); em = memstream_open_w(); while (1) { px = 0; if (ofd >= 0) { opx = px; pfd[px++] = (struct pollfd) { .fd = ofd, .events = POLLIN | POLLRDNORM }; } else { opx = -1; } if (efd >= 0) { epx = px; pfd[px++] = (struct pollfd) { .fd = 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))) { rv = read(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(ofd); ofd = -1; } else { memstream_append(om,&rbuf[0],rv); } } if ((epx >= 0) && (pfd[epx].revents & (POLLIN|POLLRDNORM))) { rv = read(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(efd); efd = -1; } else { memstream_append(em,&rbuf[0],rv); } } } if (omp) *omp = om; else memstream_free(om); if (emp) *emp = em; else memstream_free(em); } int proc_external(int key0, ...) { va_list ap; int ac; const char **av; int key; int n; int i; const char **vec; pid_t kid; pid_t dead; 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_END: break <"args">; } key = va_arg(ap,int); } va_end(ap); av = malloc((ac+1)*sizeof(const char *)); 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_END: break <"args">; } key = va_arg(ap,int); } va_end(ap); if (i != ac) abort(); av[i] = 0; fflush(0); kid = fork(); if (kid < 0) { fatal("fork: %s",strerror(errno)); exit(1); } if (kid == 0) { execvp(av[0],(const void *)av); fprintf(stderr,"%s: exec %s: %s\n",__progname,av[0],strerror(errno)); _exit(1); } free(av); while (1) { dead = wait4(kid,&i,0,0); if (dead == kid) return(i); if (dead > 0) continue; if (dead == 0) fatal("wait4 returned 0 waiting for %d",(int)kid); switch (errno) { case EINTR: break; default: fatal("wait4 failed: %s",strerror(errno)); break; } } }