xref: /openbsd-src/usr.bin/mail/popen.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
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 *
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 *
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
98 Fclose(FILE *fp)
99 {
100 
101 	unregister_file(fp);
102 	return(fclose(fp));
103 }
104 
105 FILE *
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
474 spool_lock(void)
475 {
476 
477 	return(handle_spool_locks(1));
478 }
479 
480 int
481 spool_unlock(void)
482 {
483 
484 	return(handle_spool_locks(0));
485 }
486