larn(6): Fix two "use of index before limits check" issues.
[dragonfly.git] / usr.sbin / installer / libaura / popen.c
blobd4a2fdf324917a0600a7d88201dbc9e49bd2c229
1 /*
2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2004 The DragonFly Project. All rights reserved.
6 * This code is derived from software written by Ken Arnold and
7 * published in UNIX Review, Vol. 6, No. 8.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * $FreeBSD: src/lib/libc/gen/popen.c,v 1.14 2000/01/27 23:06:19 jasone Exp $
34 * $Id: popen.c,v 1.6 2005/02/06 06:57:30 cpressey Exp $
36 * @(#)popen.c 8.3 (Berkeley) 5/3/95
38 * A modified version of the standard popen()/pclose() functions
39 * which adds a third function, pgetpid(), which allows the program
40 * which used popen() to obtain the pid of the process on the other
41 * end of the pipe.
44 #include <sys/param.h>
45 #include <sys/wait.h>
46 #include <sys/types.h>
47 #include <sys/time.h>
49 #include <errno.h>
50 #include <signal.h>
51 #include <stdarg.h>
52 #include <paths.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
58 #include "popen.h"
60 extern char **environ;
62 static struct pid {
63 struct pid *next;
64 FILE *fp;
65 pid_t pid;
66 } *pidlist;
68 FILE *
69 aura_popen(const char *fmt, const char *type, ...)
71 FILE *iop;
72 struct pid *cur;
73 struct pid *p;
74 va_list args;
75 char *command;
76 const char *argv[4];
77 int pdes[2], pid;
78 volatile enum { READ, WRITE, TWO_WAY } pipedir;
81 * Lite2 introduced two-way popen() pipes using socketpair().
82 * FreeBSD's pipe() is bidirectional, so we use that.
84 if (strchr(type, '+')) {
85 pipedir = TWO_WAY;
86 } else {
87 if (type[1] != '\0')
88 return(NULL);
89 if (*type == 'r')
90 pipedir = READ;
91 else if (*type == 'w')
92 pipedir = WRITE;
93 else
94 return(NULL);
97 if (pipe(pdes) < 0)
98 return(NULL);
100 if ((cur = malloc(sizeof(struct pid))) == NULL) {
101 close(pdes[0]);
102 close(pdes[1]);
103 return(NULL);
106 va_start(args, type);
107 vasprintf(&command, fmt, args);
108 va_end(args);
110 argv[0] = "sh";
111 argv[1] = "-c";
112 argv[2] = command;
113 argv[3] = NULL;
115 switch (pid = vfork()) {
116 case -1: /* Error. */
117 close(pdes[0]);
118 close(pdes[1]);
119 free(cur);
120 free(command);
121 return(NULL);
122 /* NOTREACHED */
123 case 0: /* Child. */
124 if (pipedir == READ || pipedir == TWO_WAY) {
126 * The dup2() to STDIN_FILENO is repeated to avoid
127 * writing to pdes[1], which might corrupt the
128 * parent's copy. This isn't good enough in
129 * general, since the _exit() is no return, so
130 * the compiler is free to corrupt all the local
131 * variables.
133 close(pdes[0]);
134 if (pdes[1] != STDOUT_FILENO) {
135 dup2(pdes[1], STDOUT_FILENO);
136 close(pdes[1]);
137 if (pipedir == TWO_WAY)
138 dup2(STDOUT_FILENO, STDIN_FILENO);
139 } else if (pipedir == TWO_WAY &&
140 (pdes[1] != STDIN_FILENO)) {
141 dup2(pdes[1], STDIN_FILENO);
143 } else {
144 if (pdes[0] != STDIN_FILENO) {
145 dup2(pdes[0], STDIN_FILENO);
146 close(pdes[0]);
148 close(pdes[1]);
150 for (p = pidlist; p; p = p->next) {
151 close(fileno(p->fp));
153 execve(_PATH_BSHELL, __DECONST(char **, argv), environ);
154 _exit(127);
155 /* NOTREACHED */
158 /* Parent; assume fdopen can't fail. */
159 if (pipedir == READ || pipedir == TWO_WAY) {
160 iop = fdopen(pdes[0], type);
161 close(pdes[1]);
162 } else {
163 iop = fdopen(pdes[1], type);
164 close(pdes[0]);
167 /* Link into list of file descriptors. */
168 cur->fp = iop;
169 cur->pid = pid;
170 cur->next = pidlist;
171 pidlist = cur;
173 free(command);
174 return(iop);
178 * pclose --
179 * Pclose returns -1 if stream is not associated with a `popened' command,
180 * if already `pclosed', or waitpid returns an error.
183 aura_pclose(FILE *iop)
185 struct pid *cur, *last;
186 int pstat;
187 pid_t pid;
189 /* Find the appropriate file pointer. */
190 for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) {
191 if (cur->fp == iop)
192 break;
195 if (cur == NULL)
196 return (-1);
198 fclose(iop);
200 do {
201 pid = wait4(cur->pid, &pstat, 0, (struct rusage *)0);
202 } while (pid == -1 && errno == EINTR);
204 /* Remove the entry from the linked list. */
205 if (last == NULL)
206 pidlist = cur->next;
207 else
208 last->next = cur->next;
209 free(cur);
211 return (pid == -1 ? -1 : pstat);
214 pid_t
215 aura_pgetpid(FILE *iop)
217 struct pid *cur;
219 /* Find the appropriate file pointer. */
220 for (cur = pidlist; cur; cur = cur->next)
221 if (cur->fp == iop)
222 break;
223 if (cur == NULL)
224 return (-1);
225 return(cur->pid);
229 * Returns:
230 * 1 if all went well.
231 * 0 if an error occurred, in which case err is set to:
232 * AURA_PGETS_TIMEOUT: select() timed out.
233 * AURA_PGETS_SELECT_ERR: a select() error occurred.
234 * AURA_PGETS_EOF: end of file condition on pipe.
235 * AURA_PGETS_FGETS_ERR: a fgets() error occurred.
238 aura_pgets(FILE *p, char *buf, size_t len, long msec, int *err)
240 struct timeval tv;
241 struct timeval *tvp;
242 fd_set r;
243 int n;
245 tv.tv_sec = msec / 1000;
246 tv.tv_usec = (msec % 1000) * 1000;
247 tvp = (msec < 0 ? NULL : &tv);
249 FD_ZERO(&r);
250 FD_SET(fileno(p), &r);
252 *err = 0;
253 buf[0] = '\0';
255 if (feof(p)) {
256 *err = AURA_PGETS_EOF;
257 return(0);
259 if (ferror(p)) {
260 *err = AURA_PGETS_FGETS_ERR;
261 return(0);
264 n = select(fileno(p) + 1, &r, NULL, NULL, tvp);
266 if (n == 0) {
267 *err = AURA_PGETS_TIMEOUT;
268 return(0);
269 } else if (n < 0) {
270 *err = AURA_PGETS_SELECT_ERR;
271 return(0);
272 } else {
273 /* Data came in; read it. */
274 if (fgets(buf, len, p) == NULL) {
275 if (feof(p)) {
276 *err = AURA_PGETS_EOF;
277 } else {
278 *err = AURA_PGETS_FGETS_ERR;
280 return(0);
281 } else {
282 return(1);