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