|
|
| version 1.1, 2013/12/05 14:56:42 | version 1.1.2.1, 2013/12/05 14:56:42 |
|---|---|
| Line 0 | Line 1 |
| #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; | |
| FILE *iop; | |
| int pdes[2], pid, twoway, cloexec; | |
| char *argv[4]; | |
| struct pid *p; | |
| 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 pid))) == NULL) { | |
| 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 pid *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 == NULL) { | |
| THREAD_UNLOCK(); | |
| return (-1); | |
| } | |
| if (last == NULL) | |
| SLIST_REMOVE_HEAD(&pio_pidlist, next); | |
| else | |
| SLIST_REMOVE_AFTER(last, next); | |
| THREAD_UNLOCK(); | |
| fclose(iop); | |
| do { | |
| pid = wait4(cur->pid, &pstat, 0, (struct rusage *)0); | |
| } while (pid == -1 && errno == EINTR); | |
| e_free(cur); | |
| return (pid == -1 ? -1 : pstat); | |
| } |