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.
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().
24 #include <bits/uClibc_mutex.h>
27 __UCLIBC_MUTEX_STATIC(mylock
, PTHREAD_MUTEX_INITIALIZER
);
28 # define VFORK_LOCK __UCLIBC_MUTEX_LOCK(mylock)
29 # define VFORK_UNLOCK __UCLIBC_MUTEX_UNLOCK(mylock)
32 struct popen_list_item
{
33 struct popen_list_item
*next
;
38 static struct popen_list_item
*popen_list
/* = NULL (bss initialized) */;
40 FILE *popen(const char *command
, const char *modes
)
43 struct popen_list_item
*pi
;
44 struct popen_list_item
*po
;
48 int child_writing
; /* Doubles as the desired child fildes. */
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! */
60 if (!(pi
= malloc(sizeof(struct popen_list_item
)))) {
68 child_fd
= pipe_fd
[child_writing
];
69 parent_fd
= pipe_fd
[1-child_writing
];
71 if (!(fp
= fdopen(parent_fd
, modes
))) {
78 if ((pid
= vfork()) == 0) { /* Child of vfork... */
80 if (child_fd
!= child_writing
) {
81 dup2(child_fd
, child_writing
);
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. */
99 /* We need to close the child filedes whether vfork failed or
100 * it succeeded and we're in the parent. */
103 if (pid
> 0) { /* Parent of vfork... */
107 pi
->next
= popen_list
;
114 /* If we get here, vfork failed. */
115 fclose(fp
); /* Will close parent_fd. */
124 int pclose(FILE *stream
)
126 struct popen_list_item
*p
;
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). */
133 if ((p
= popen_list
) != NULL
) {
134 if (p
->f
== stream
) {
135 popen_list
= p
->next
;
137 struct popen_list_item
*t
;
140 if (!(p
= t
->next
)) {
141 __set_errno(EINVAL
); /* Not required by SUSv3. */
144 if (p
->f
== stream
) {
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. */
162 if (waitpid(pid
, &status
, 0) >= 0) {
165 if (errno
!= EINTR
) {