1 /* $OpenBSD: popen.c,v 1.34 2004/09/15 22:21:11 deraadt Exp $ */
2 /* $NetBSD: popen.c,v 1.6 1997/05/13 06:48:42 mikel Exp $ */
3
4 /*
5 * Copyright (c) 1980, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #ifndef lint
34 #if 0
35 static const char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93";
36 #else
37 static const char rcsid[] = "$OpenBSD: popen.c,v 1.34 2004/09/15 22:21:11 deraadt Exp $";
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include <sys/wait.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <stdarg.h>
46 #include "extern.h"
47
48 #define READ 0
49 #define WRITE 1
50
51 struct fp {
52 FILE *fp;
53 int pipe;
54 pid_t pid;
55 struct fp *link;
56 };
57 static struct fp *fp_head;
58
59 struct child {
60 pid_t pid;
61 char done;
62 char free;
63 int status;
64 struct child *link;
65 };
66 static struct child *child, *child_freelist = NULL;
67
68 static struct child *findchild(pid_t, int);
69 static void delchild(struct child *);
70 static pid_t file_pid(FILE *);
71 static int handle_spool_locks(int);
72
73 FILE *
Fopen(char * file,char * mode)74 Fopen(char *file, char *mode)
75 {
76 FILE *fp;
77
78 if ((fp = fopen(file, mode)) != NULL) {
79 register_file(fp, 0, 0);
80 (void)fcntl(fileno(fp), F_SETFD, 1);
81 }
82 return(fp);
83 }
84
85 FILE *
Fdopen(int fd,char * mode)86 Fdopen(int fd, char *mode)
87 {
88 FILE *fp;
89
90 if ((fp = fdopen(fd, mode)) != NULL) {
91 register_file(fp, 0, 0);
92 (void)fcntl(fileno(fp), F_SETFD, 1);
93 }
94 return(fp);
95 }
96
97 int
Fclose(FILE * fp)98 Fclose(FILE *fp)
99 {
100
101 unregister_file(fp);
102 return(fclose(fp));
103 }
104
105 FILE *
Popen(char * cmd,char * mode)106 Popen(char *cmd, char *mode)
107 {
108 int p[2];
109 int myside, hisside, fd0, fd1;
110 pid_t pid;
111 sigset_t nset;
112 FILE *fp;
113
114 if (pipe(p) < 0)
115 return(NULL);
116 (void)fcntl(p[READ], F_SETFD, 1);
117 (void)fcntl(p[WRITE], F_SETFD, 1);
118 if (*mode == 'r') {
119 myside = p[READ];
120 hisside = fd0 = fd1 = p[WRITE];
121 } else {
122 myside = p[WRITE];
123 hisside = fd0 = p[READ];
124 fd1 = -1;
125 }
126 sigemptyset(&nset);
127 pid = start_command(value("SHELL"), &nset, fd0, fd1, "-c", cmd, NULL);
128 if (pid < 0) {
129 (void)close(p[READ]);
130 (void)close(p[WRITE]);
131 return(NULL);
132 }
133 (void)close(hisside);
134 if ((fp = fdopen(myside, mode)) != NULL)
135 register_file(fp, 1, pid);
136 return(fp);
137 }
138
139 int
Pclose(FILE * ptr)140 Pclose(FILE *ptr)
141 {
142 int i;
143 sigset_t nset, oset;
144
145 i = file_pid(ptr);
146 unregister_file(ptr);
147 (void)fclose(ptr);
148 sigemptyset(&nset);
149 sigaddset(&nset, SIGINT);
150 sigaddset(&nset, SIGHUP);
151 sigprocmask(SIG_BLOCK, &nset, &oset);
152 i = wait_child(i);
153 sigprocmask(SIG_SETMASK, &oset, NULL);
154 return(i);
155 }
156
157 void
close_all_files(void)158 close_all_files(void)
159 {
160
161 while (fp_head)
162 if (fp_head->pipe)
163 (void)Pclose(fp_head->fp);
164 else
165 (void)Fclose(fp_head->fp);
166 }
167
168 void
register_file(FILE * fp,int pipe,pid_t pid)169 register_file(FILE *fp, int pipe, pid_t pid)
170 {
171 struct fp *fpp;
172
173 if ((fpp = (struct fp *)malloc(sizeof(*fpp))) == NULL)
174 errx(1, "Out of memory");
175 fpp->fp = fp;
176 fpp->pipe = pipe;
177 fpp->pid = pid;
178 fpp->link = fp_head;
179 fp_head = fpp;
180 }
181
182 void
unregister_file(FILE * fp)183 unregister_file(FILE *fp)
184 {
185 struct fp **pp, *p;
186
187 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
188 if (p->fp == fp) {
189 *pp = p->link;
190 (void)free(p);
191 return;
192 }
193 errx(1, "Invalid file pointer");
194 }
195
196 static pid_t
file_pid(FILE * fp)197 file_pid(FILE *fp)
198 {
199 struct fp *p;
200
201 for (p = fp_head; p; p = p->link)
202 if (p->fp == fp)
203 return(p->pid);
204 errx(1, "Invalid file pointer");
205 /*NOTREACHED*/
206 }
207
208 /*
209 * Run a command without a shell, with optional arguments and splicing
210 * of stdin (-1 means none) and stdout. The command name can be a sequence
211 * of words.
212 * Signals must be handled by the caller.
213 * "nset" contains the signals to ignore in the new process.
214 * SIGINT is enabled unless it's in "nset".
215 */
216 pid_t
start_commandv(char * cmd,sigset_t * nset,int infd,int outfd,va_list args)217 start_commandv(char *cmd, sigset_t *nset, int infd, int outfd, va_list args)
218 {
219 pid_t pid;
220
221 if ((pid = fork()) < 0) {
222 warn("fork");
223 return(-1);
224 }
225 if (pid == 0) {
226 char *argv[100];
227 int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv));
228
229 while ((argv[i++] = va_arg(args, char *)))
230 ;
231 argv[i] = NULL;
232 prepare_child(nset, infd, outfd);
233 execvp(argv[0], argv);
234 warn("%s", argv[0]);
235 _exit(1);
236 }
237 return(pid);
238 }
239
240 int
run_command(char * cmd,sigset_t * nset,int infd,int outfd,...)241 run_command(char *cmd, sigset_t *nset, int infd, int outfd, ...)
242 {
243 pid_t pid;
244 va_list args;
245
246 va_start(args, outfd);
247 pid = start_commandv(cmd, nset, infd, outfd, args);
248 va_end(args);
249 if (pid < 0)
250 return(-1);
251 return(wait_command(pid));
252 }
253
254 int
start_command(char * cmd,sigset_t * nset,int infd,int outfd,...)255 start_command(char *cmd, sigset_t *nset, int infd, int outfd, ...)
256 {
257 va_list args;
258 int r;
259
260 va_start(args, outfd);
261 r = start_commandv(cmd, nset, infd, outfd, args);
262 va_end(args);
263 return(r);
264 }
265
266 void
prepare_child(sigset_t * nset,int infd,int outfd)267 prepare_child(sigset_t *nset, int infd, int outfd)
268 {
269 int i;
270 sigset_t eset;
271
272 /*
273 * All file descriptors other than 0, 1, and 2 are supposed to be
274 * close-on-exec.
275 */
276 if (infd > 0) {
277 dup2(infd, 0);
278 } else if (infd != 0) {
279 /* we don't want the child stealing my stdin input */
280 close(0);
281 open(_PATH_DEVNULL, O_RDONLY, 0);
282 }
283 if (outfd >= 0 && outfd != 1)
284 dup2(outfd, 1);
285 if (nset == NULL)
286 return;
287 if (nset != NULL) {
288 for (i = 1; i < NSIG; i++)
289 if (sigismember(nset, i))
290 (void)signal(i, SIG_IGN);
291 }
292 if (nset == NULL || !sigismember(nset, SIGINT))
293 (void)signal(SIGINT, SIG_DFL);
294 sigemptyset(&eset);
295 (void)sigprocmask(SIG_SETMASK, &eset, NULL);
296 }
297
298 int
wait_command(pid_t pid)299 wait_command(pid_t pid)
300 {
301
302 if (wait_child(pid) < 0) {
303 puts("Fatal error in process.");
304 return(-1);
305 }
306 return(0);
307 }
308
309 static struct child *
findchild(pid_t pid,int dont_alloc)310 findchild(pid_t pid, int dont_alloc)
311 {
312 struct child **cpp;
313
314 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
315 cpp = &(*cpp)->link)
316 ;
317 if (*cpp == NULL) {
318 if (dont_alloc)
319 return(NULL);
320 if (child_freelist) {
321 *cpp = child_freelist;
322 child_freelist = (*cpp)->link;
323 } else {
324 *cpp = (struct child *)malloc(sizeof(struct child));
325 if (*cpp == NULL)
326 errx(1, "Out of memory");
327 }
328 (*cpp)->pid = pid;
329 (*cpp)->done = (*cpp)->free = 0;
330 (*cpp)->link = NULL;
331 }
332 return(*cpp);
333 }
334
335 static void
delchild(struct child * cp)336 delchild(struct child *cp)
337 {
338 struct child **cpp;
339
340 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
341 ;
342 *cpp = cp->link;
343 cp->link = child_freelist;
344 child_freelist = cp;
345 }
346
347 /* ARGSUSED */
348 void
sigchild(int signo)349 sigchild(int signo)
350 {
351 pid_t pid;
352 int status;
353 struct child *cp;
354 int save_errno = errno;
355
356 while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
357 cp = findchild(pid, 1);
358 if (!cp)
359 continue;
360 if (cp->free)
361 delchild(cp);
362 else {
363 cp->done = 1;
364 cp->status = status;
365 }
366 }
367 errno = save_errno;
368 }
369
370 int wait_status;
371
372 /*
373 * Wait for a specific child to die.
374 */
375 int
wait_child(pid_t pid)376 wait_child(pid_t pid)
377 {
378 struct child *cp;
379 sigset_t nset, oset;
380 pid_t rv = 0;
381
382 sigemptyset(&nset);
383 sigaddset(&nset, SIGCHLD);
384 sigprocmask(SIG_BLOCK, &nset, &oset);
385 /*
386 * If we have not already waited on the pid (via sigchild)
387 * wait on it now. Otherwise, use the wait status stashed
388 * by sigchild.
389 */
390 cp = findchild(pid, 1);
391 if (cp == NULL || !cp->done)
392 rv = waitpid(pid, &wait_status, 0);
393 else
394 wait_status = cp->status;
395 if (cp != NULL)
396 delchild(cp);
397 sigprocmask(SIG_SETMASK, &oset, NULL);
398 if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
399 return(-1);
400 else
401 return(0);
402 }
403
404 /*
405 * Mark a child as don't care.
406 */
407 void
free_child(pid_t pid)408 free_child(pid_t pid)
409 {
410 struct child *cp;
411 sigset_t nset, oset;
412
413 sigemptyset(&nset);
414 sigaddset(&nset, SIGCHLD);
415 sigprocmask(SIG_BLOCK, &nset, &oset);
416 if ((cp = findchild(pid, 0)) != NULL) {
417 if (cp->done)
418 delchild(cp);
419 else
420 cp->free = 1;
421 }
422 sigprocmask(SIG_SETMASK, &oset, NULL);
423 }
424
425 /*
426 * Lock(1)/unlock(0) mail spool using lockspool(1).
427 * Returns 1 for success, 0 for failure, -1 for bad usage.
428 */
429 static int
handle_spool_locks(int action)430 handle_spool_locks(int action)
431 {
432 static FILE *lockfp = NULL;
433
434 if (action == 0) {
435 /* Clear the lock */
436 if (lockfp == NULL) {
437 fputs("handle_spool_locks: no spool lock to remove.\n",
438 stderr);
439 return(-1);
440 }
441 (void)Pclose(lockfp);
442 lockfp = NULL;
443 } else if (action == 1) {
444 char *cmd;
445 char buf[sizeof(_PATH_LOCKSPOOL) + MAXLOGNAME + 1];
446
447 /* XXX - lockspool requires root for user arg, we do not */
448 if (uflag) {
449 snprintf(buf, sizeof(buf), "%s %s", _PATH_LOCKSPOOL,
450 myname);
451 cmd = buf;
452 } else
453 cmd = _PATH_LOCKSPOOL;
454
455 /* Create the lock */
456 lockfp = Popen(cmd, "r");
457 if (lockfp == NULL)
458 return(0);
459 if (getc(lockfp) != '1') {
460 Pclose(lockfp);
461 lockfp = NULL;
462 return(0);
463 }
464 } else {
465 (void)fprintf(stderr, "handle_spool_locks: unknown action %d\n",
466 action);
467 return(-1);
468 }
469
470 return(1);
471 }
472
473 int
spool_lock(void)474 spool_lock(void)
475 {
476
477 return(handle_spool_locks(1));
478 }
479
480 int
spool_unlock(void)481 spool_unlock(void)
482 {
483
484 return(handle_spool_locks(0));
485 }
486