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