#include "global.h" extern char **environ; pio_pid_t pio_pidlist = SLIST_HEAD_INITIALIZER(pio_pidlist); static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; #define THREAD_LOCK() if (__isthreaded) pthread_mutex_lock(&pidlist_mutex) #define THREAD_UNLOCK() if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex) /* * e_popen() - ELWIX replacement of standard popen * * @command = command * @type = type * @ppid = return pid of child program * return: NULL error or !=NULL open program */ FILE * e_popen(const char *command, const char *type, pid_t *ppid) { struct tagPIOPID *cur, *p; FILE *iop; int pdes[2], pid, twoway, cloexec; char *argv[4]; cloexec = strchr(type, 'e') != NULL; /* * Lite2 introduced two-way popen() pipes using _socketpair(). * FreeBSD's pipe() is bidirectional, so we use that. */ if (strchr(type, '+')) { twoway = 1; type = "r+"; } else { twoway = 0; if ((*type != 'r' && *type != 'w') || (type[1] && (type[1] != 'e' || type[2]))) return (NULL); } if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0) return (NULL); if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) { close(pdes[0]); close(pdes[1]); return (NULL); } argv[0] = "sh"; argv[1] = "-c"; argv[2] = (char *)command; argv[3] = NULL; THREAD_LOCK(); switch (pid = vfork()) { case -1: /* Error. */ THREAD_UNLOCK(); close(pdes[0]); close(pdes[1]); e_free(cur); return (NULL); /* NOTREACHED */ case 0: /* Child. */ if (*type == 'r') { /* * The _dup2() to STDIN_FILENO is repeated to avoid * writing to pdes[1], which might corrupt the * parent's copy. This isn't good enough in * general, since the _exit() is no return, so * the compiler is free to corrupt all the local * variables. */ if (!cloexec) close(pdes[0]); if (pdes[1] != STDOUT_FILENO) { dup2(pdes[1], STDOUT_FILENO); if (!cloexec) close(pdes[1]); if (twoway) dup2(STDOUT_FILENO, STDIN_FILENO); } else if (twoway && (pdes[1] != STDIN_FILENO)) { dup2(pdes[1], STDIN_FILENO); if (cloexec) fcntl(pdes[1], F_SETFD, 0); } else if (cloexec) fcntl(pdes[1], F_SETFD, 0); } else { if (pdes[0] != STDIN_FILENO) { dup2(pdes[0], STDIN_FILENO); if (!cloexec) close(pdes[0]); } else if (cloexec) fcntl(pdes[0], F_SETFD, 0); if (!cloexec) close(pdes[1]); } SLIST_FOREACH(p, &pio_pidlist, next) close(fileno(p->fp)); execve(_PATH_BSHELL, argv, environ); _exit(127); /* NOTREACHED */ default: if (ppid) *ppid = pid; } THREAD_UNLOCK(); /* Parent; assume fdopen can't fail. */ if (*type == 'r') { iop = fdopen(pdes[0], type); close(pdes[1]); } else { iop = fdopen(pdes[1], type); close(pdes[0]); } /* Link into list of file descriptors. */ cur->fp = iop; cur->pid = pid; THREAD_LOCK(); SLIST_INSERT_HEAD(&pio_pidlist, cur, next); THREAD_UNLOCK(); return (iop); } /* * e_pclose() - ELWIX replacement of standard pclose * * @iop = popen handle * return: -1 error or !=-1 pid status */ int e_pclose(FILE *iop) { struct tagPIOPID *cur, *last = NULL; int pstat; pid_t pid; /* * Find the appropriate file pointer and remove it from the list. */ THREAD_LOCK(); SLIST_FOREACH(cur, &pio_pidlist, next) { if (cur->fp == iop) break; last = cur; } if (!cur) { THREAD_UNLOCK(); return (-1); } if (!last) SLIST_REMOVE_HEAD(&pio_pidlist, next); else SLIST_REMOVE_AFTER(last, next); THREAD_UNLOCK(); fclose(iop); do { pid = wait4(cur->pid, &pstat, 0, NULL); } while (pid == -1 && errno == EINTR); e_free(cur); return (pid == -1 ? -1 : pstat); }