netinet/in.h: add IPPROTO_MH
[uclibc-ng.git] / libc / stdio / popen.c
bloba5aa99885483dee9d9a9dd1d9baef6da97f96f54
1 /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
2 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
4 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
6 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
7 */
9 /* Jan 1, 2004
11 * Rewrite popen for SUSv3 compliance.
12 * Added a list of popen()'d to store pids and use waitpid() in pclose().
13 * Loop on waitpid() failure due to EINTR as required.
14 * Close parent's popen()'d FILEs in the {v}fork()'d child.
15 * Fix failure exit code for failed execve().
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <paths.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <sys/wait.h>
24 #include <bits/uClibc_mutex.h>
26 #ifndef VFORK_LOCK
27 __UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);
28 # define VFORK_LOCK __UCLIBC_MUTEX_LOCK(mylock)
29 # define VFORK_UNLOCK __UCLIBC_MUTEX_UNLOCK(mylock)
30 #endif
32 struct popen_list_item {
33 struct popen_list_item *next;
34 FILE *f;
35 pid_t pid;
38 static struct popen_list_item *popen_list /* = NULL (bss initialized) */;
40 FILE *popen(const char *command, const char *modes)
42 FILE *fp;
43 struct popen_list_item *pi;
44 struct popen_list_item *po;
45 int pipe_fd[2];
46 int parent_fd;
47 int child_fd;
48 int child_writing; /* Doubles as the desired child fildes. */
49 pid_t pid;
51 child_writing = 0; /* Assume child is writing. */
52 if (modes[0] != 'w') { /* Parent not writing... */
53 ++child_writing; /* so child must be writing. */
54 if (modes[0] != 'r') { /* Oops! Parent not reading either! */
55 __set_errno(EINVAL);
56 goto RET_NULL;
60 if (!(pi = malloc(sizeof(struct popen_list_item)))) {
61 goto RET_NULL;
64 if (pipe(pipe_fd)) {
65 goto FREE_PI;
68 child_fd = pipe_fd[child_writing];
69 parent_fd = pipe_fd[1-child_writing];
71 if (!(fp = fdopen(parent_fd, modes))) {
72 close(parent_fd);
73 close(child_fd);
74 goto FREE_PI;
77 VFORK_LOCK;
78 if ((pid = vfork()) == 0) { /* Child of vfork... */
79 close(parent_fd);
80 if (child_fd != child_writing) {
81 dup2(child_fd, child_writing);
82 close(child_fd);
85 /* SUSv3 requires that any previously popen()'d streams in the
86 * parent shall be closed in the child. */
87 for (po = popen_list ; po ; po = po->next) {
88 close(po->f->__filedes);
91 execl(_PATH_BSHELL, "sh", "-c", command, (char *)0);
93 /* SUSv3 mandates an exit code of 127 for the child if the
94 * command interpreter can not be invoked. */
95 _exit(127);
97 VFORK_UNLOCK;
99 /* We need to close the child filedes whether vfork failed or
100 * it succeeded and we're in the parent. */
101 close(child_fd);
103 if (pid > 0) { /* Parent of vfork... */
104 pi->pid = pid;
105 pi->f = fp;
106 VFORK_LOCK;
107 pi->next = popen_list;
108 popen_list = pi;
109 VFORK_UNLOCK;
111 return fp;
114 /* If we get here, vfork failed. */
115 fclose(fp); /* Will close parent_fd. */
117 FREE_PI:
118 free(pi);
120 RET_NULL:
121 return NULL;
124 int pclose(FILE *stream)
126 struct popen_list_item *p;
127 int status;
128 pid_t pid;
130 /* First, find the list entry corresponding to stream and remove it
131 * from the list. Set p to the list item (NULL if not found). */
132 VFORK_LOCK;
133 if ((p = popen_list) != NULL) {
134 if (p->f == stream) {
135 popen_list = p->next;
136 } else {
137 struct popen_list_item *t;
138 do {
139 t = p;
140 if (!(p = t->next)) {
141 __set_errno(EINVAL); /* Not required by SUSv3. */
142 break;
144 if (p->f == stream) {
145 t->next = p->next;
146 break;
148 } while (1);
151 VFORK_UNLOCK;
153 if (p) {
154 pid = p->pid; /* Save the pid we need */
155 free(p); /* and free the list item. */
157 fclose(stream); /* The SUSv3 example code ignores the return. */
159 /* SUSv3 specificly requires that pclose not return before the child
160 * terminates, in order to disallow pclose from returning on EINTR. */
161 do {
162 if (waitpid(pid, &status, 0) >= 0) {
163 return status;
165 if (errno != EINTR) {
166 break;
168 } while (1);
171 return -1;