1 /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
2 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
3 *
4 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
5 *
6 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
7 */
8
9 /* Jan 1, 2004
10 *
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().
16 */
17
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>
25
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
31
32 struct popen_list_item {
33 struct popen_list_item *next;
34 FILE *f;
35 pid_t pid;
36 };
37
38 static struct popen_list_item *popen_list /* = NULL (bss initialized) */;
39
popen(const char * command,const char * modes)40 FILE *popen(const char *command, const char *modes)
41 {
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;
50
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;
57 }
58 }
59
60 if (!(pi = malloc(sizeof(struct popen_list_item)))) {
61 goto RET_NULL;
62 }
63
64 if (pipe(pipe_fd)) {
65 goto FREE_PI;
66 }
67
68 child_fd = pipe_fd[child_writing];
69 parent_fd = pipe_fd[1-child_writing];
70
71 if (!(fp = fdopen(parent_fd, modes))) {
72 close(parent_fd);
73 close(child_fd);
74 goto FREE_PI;
75 }
76
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);
83 }
84
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);
89 }
90
91 execl(_PATH_BSHELL, "sh", "-c", command, (char *)0);
92
93 /* SUSv3 mandates an exit code of 127 for the child if the
94 * command interpreter can not be invoked. */
95 _exit(127);
96 }
97 VFORK_UNLOCK;
98
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);
102
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;
110
111 return fp;
112 }
113
114 /* If we get here, vfork failed. */
115 fclose(fp); /* Will close parent_fd. */
116
117 FREE_PI:
118 free(pi);
119
120 RET_NULL:
121 return NULL;
122 }
123
pclose(FILE * stream)124 int pclose(FILE *stream)
125 {
126 struct popen_list_item *p;
127 int status;
128 pid_t pid;
129
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;
143 }
144 if (p->f == stream) {
145 t->next = p->next;
146 break;
147 }
148 } while (1);
149 }
150 }
151 VFORK_UNLOCK;
152
153 if (p) {
154 pid = p->pid; /* Save the pid we need */
155 free(p); /* and free the list item. */
156
157 fclose(stream); /* The SUSv3 example code ignores the return. */
158
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;
164 }
165 if (errno != EINTR) {
166 break;
167 }
168 } while (1);
169 }
170
171 return -1;
172 }
173