xref: /openbsd-src/usr.bin/mail/popen.c (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
1 /*	$OpenBSD: popen.c,v 1.23 1998/09/27 21:16:42 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 char sccsid[] = "@(#)popen.c	8.1 (Berkeley) 6/6/93";
40 #else
41 static char rcsid[] = "$OpenBSD: popen.c,v 1.23 1998/09/27 21:16:42 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 "extern.h"
50 
51 #define READ 0
52 #define WRITE 1
53 
54 struct fp {
55 	FILE *fp;
56 	int pipe;
57 	int pid;
58 	struct fp *link;
59 };
60 static struct fp *fp_head;
61 
62 struct child {
63 	int pid;
64 	char done;
65 	char free;
66 	int status;
67 	struct child *link;
68 };
69 static struct child *child, *child_freelist = NULL;
70 static struct child *findchild __P((int, int));
71 static void delchild __P((struct child *));
72 static int file_pid __P((FILE *));
73 static int handle_spool_locks __P((int));
74 
75 FILE *
76 Fopen(file, mode)
77 	char *file, *mode;
78 {
79 	FILE *fp;
80 
81 	if ((fp = fopen(file, mode)) != NULL) {
82 		register_file(fp, 0, 0);
83 		(void)fcntl(fileno(fp), F_SETFD, 1);
84 	}
85 	return(fp);
86 }
87 
88 FILE *
89 Fdopen(fd, mode)
90 	int fd;
91 	char *mode;
92 {
93 	FILE *fp;
94 
95 	if ((fp = fdopen(fd, mode)) != NULL) {
96 		register_file(fp, 0, 0);
97 		(void)fcntl(fileno(fp), F_SETFD, 1);
98 	}
99 	return(fp);
100 }
101 
102 int
103 Fclose(fp)
104 	FILE *fp;
105 {
106 	unregister_file(fp);
107 	return(fclose(fp));
108 }
109 
110 FILE *
111 Popen(cmd, mode)
112 	char *cmd;
113 	char *mode;
114 {
115 	int p[2];
116 	int myside, hisside, fd0, fd1;
117 	int pid;
118 	sigset_t nset;
119 	FILE *fp;
120 
121 	if (pipe(p) < 0)
122 		return(NULL);
123 	(void)fcntl(p[READ], F_SETFD, 1);
124 	(void)fcntl(p[WRITE], F_SETFD, 1);
125 	if (*mode == 'r') {
126 		myside = p[READ];
127 		fd0 = -1;
128 		hisside = fd1 = p[WRITE];
129 	} else {
130 		myside = p[WRITE];
131 		hisside = fd0 = p[READ];
132 		fd1 = -1;
133 	}
134 	sigemptyset(&nset);
135 	if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) {
136 		(void)close(p[READ]);
137 		(void)close(p[WRITE]);
138 		return(NULL);
139 	}
140 	(void)close(hisside);
141 	if ((fp = fdopen(myside, mode)) != NULL)
142 		register_file(fp, 1, pid);
143 	return(fp);
144 }
145 
146 int
147 Pclose(ptr)
148 	FILE *ptr;
149 {
150 	int i;
151 	sigset_t nset, oset;
152 
153 	i = file_pid(ptr);
154 	unregister_file(ptr);
155 	(void)fclose(ptr);
156 	sigemptyset(&nset);
157 	sigaddset(&nset, SIGINT);
158 	sigaddset(&nset, SIGHUP);
159 	sigprocmask(SIG_BLOCK, &nset, &oset);
160 	i = wait_child(i);
161 	sigprocmask(SIG_SETMASK, &oset, NULL);
162 	return(i);
163 }
164 
165 void
166 close_all_files()
167 {
168 
169 	while (fp_head)
170 		if (fp_head->pipe)
171 			(void)Pclose(fp_head->fp);
172 		else
173 			(void)Fclose(fp_head->fp);
174 }
175 
176 void
177 register_file(fp, pipe, pid)
178 	FILE *fp;
179 	int pipe, pid;
180 {
181 	struct fp *fpp;
182 
183 	if ((fpp = (struct fp *)malloc(sizeof(*fpp))) == NULL)
184 		errx(1, "Out of memory");
185 	fpp->fp = fp;
186 	fpp->pipe = pipe;
187 	fpp->pid = pid;
188 	fpp->link = fp_head;
189 	fp_head = fpp;
190 }
191 
192 void
193 unregister_file(fp)
194 	FILE *fp;
195 {
196 	struct fp **pp, *p;
197 
198 	for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
199 		if (p->fp == fp) {
200 			*pp = p->link;
201 			(void)free(p);
202 			return;
203 		}
204 	errx(1, "Invalid file pointer");
205 }
206 
207 static int
208 file_pid(fp)
209 	FILE *fp;
210 {
211 	struct fp *p;
212 
213 	for (p = fp_head; p; p = p->link)
214 		if (p->fp == fp)
215 			return(p->pid);
216 	errx(1, "Invalid file pointer");
217 	/*NOTREACHED*/
218 }
219 
220 /*
221  * Run a command without a shell, with optional arguments and splicing
222  * of stdin and stdout.  The command name can be a sequence of words.
223  * Signals must be handled by the caller.
224  * "nset" contains the signals to ignore in the new process.
225  * SIGINT is enabled unless it's in "nset".
226  */
227 /*VARARGS4*/
228 int
229 run_command(cmd, nset, infd, outfd, a0, a1, a2)
230 	char *cmd;
231 	sigset_t *nset;
232 	int infd, outfd;
233 	char *a0, *a1, *a2;
234 {
235 	int pid;
236 
237 	if ((pid = start_command(cmd, nset, infd, outfd, a0, a1, a2)) < 0)
238 		return(-1);
239 	return(wait_command(pid));
240 }
241 
242 /*VARARGS4*/
243 int
244 start_command(cmd, nset, infd, outfd, a0, a1, a2)
245 	char *cmd;
246 	sigset_t *nset;
247 	int infd, outfd;
248 	char *a0, *a1, *a2;
249 {
250 	int pid;
251 
252 	if ((pid = vfork()) < 0) {
253 		warn("fork");
254 		return(-1);
255 	}
256 	if (pid == 0) {
257 		char *argv[100];
258 		int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv));
259 
260 		if ((argv[i++] = a0) != NULL &&
261 		    (argv[i++] = a1) != NULL &&
262 		    (argv[i++] = a2) != NULL)
263 			argv[i] = NULL;
264 		prepare_child(nset, infd, outfd);
265 		execvp(argv[0], argv);
266 		warn(argv[0]);
267 		_exit(1);
268 	}
269 	return(pid);
270 }
271 
272 void
273 prepare_child(nset, infd, outfd)
274 	sigset_t *nset;
275 	int infd, outfd;
276 {
277 	int i;
278 	sigset_t eset;
279 
280 	/*
281 	 * All file descriptors other than 0, 1, and 2 are supposed to be
282 	 * close-on-exec.
283 	 */
284 	if (infd >= 0)
285 		dup2(infd, 0);
286 	if (outfd >= 0)
287 		dup2(outfd, 1);
288 	if (nset == NULL)
289 		return;
290 	if (nset != NULL) {
291 		for (i = 1; i < NSIG; i++)
292 			if (sigismember(nset, i))
293 				(void)signal(i, SIG_IGN);
294 	}
295 	if (nset == NULL || !sigismember(nset, SIGINT))
296 		(void)signal(SIGINT, SIG_DFL);
297 	sigemptyset(&eset);
298 	(void)sigprocmask(SIG_SETMASK, &eset, NULL);
299 }
300 
301 int
302 wait_command(pid)
303 	int 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, dont_alloc)
315 	int pid;
316 	int dont_alloc;
317 {
318 	struct child **cpp;
319 
320 	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
321 	     cpp = &(*cpp)->link)
322 			;
323 	if (*cpp == NULL) {
324 		if (dont_alloc)
325 			return(NULL);
326 		if (child_freelist) {
327 			*cpp = child_freelist;
328 			child_freelist = (*cpp)->link;
329 		} else
330 			*cpp = (struct child *)malloc(sizeof(struct child));
331 		(*cpp)->pid = pid;
332 		(*cpp)->done = (*cpp)->free = 0;
333 		(*cpp)->link = NULL;
334 	}
335 	return(*cpp);
336 }
337 
338 static void
339 delchild(cp)
340 	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(signo)
353 	int signo;
354 {
355 	int pid;
356 	int status;
357 	struct child *cp;
358 	int save_errno = errno;
359 
360 	while ((pid =
361 	    waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
362 		cp = findchild(pid, 1);
363 		if (!cp)
364 			continue;
365 		if (cp->free)
366 			delchild(cp);
367 		else {
368 			cp->done = 1;
369 			cp->status = status;
370 		}
371 	}
372 	errno = save_errno;
373 }
374 
375 int wait_status;
376 
377 /*
378  * Wait for a specific child to die.
379  */
380 int
381 wait_child(pid)
382 	int pid;
383 {
384 	struct child *cp;
385 	sigset_t nset, oset;
386 	int rv = 0;
387 
388 	sigemptyset(&nset);
389 	sigaddset(&nset, SIGCHLD);
390 	sigprocmask(SIG_BLOCK, &nset, &oset);
391 	/*
392 	 * If we have not already waited on the pid (via sigchild)
393 	 * wait on it now.  Otherwise, use the wait status stashed
394 	 * by sigchild.
395 	 */
396 	cp = findchild(pid, 1);
397 	if (cp == NULL || !cp->done)
398 		rv = waitpid(pid, &wait_status, 0);
399 	else
400 		wait_status = cp->status;
401 	if (cp != NULL)
402 		delchild(cp);
403 	sigprocmask(SIG_SETMASK, &oset, NULL);
404 	if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
405 		return(-1);
406 	else
407 		return(0);
408 }
409 
410 /*
411  * Mark a child as don't care.
412  */
413 void
414 free_child(pid)
415 	int pid;
416 {
417 	struct child *cp;
418 	sigset_t nset, oset;
419 
420 	sigemptyset(&nset);
421 	sigaddset(&nset, SIGCHLD);
422 	sigprocmask(SIG_BLOCK, &nset, &oset);
423 	if ((cp = findchild(pid, 0)) != NULL) {
424 		if (cp->done)
425 			delchild(cp);
426 		else
427 			cp->free = 1;
428 	}
429 	sigprocmask(SIG_SETMASK, &oset, NULL);
430 }
431 
432 /*
433  * Lock(1)/unlock(0) mail spool using lockspool(1).
434  * Returns 1 for success, 0 for failure, -1 for bad usage.
435  */
436 static int
437 handle_spool_locks(action)
438 	int action;
439 {
440 	static FILE *lockfp = NULL;
441 	static int lock_pid;
442 
443 	if (action == 0) {
444 		/* Clear the lock */
445 		if (lockfp == NULL) {
446 			fputs("handle_spool_locks: no spool lock to remove.\n",
447 			    stderr);
448 			return(-1);
449 		}
450 		(void)kill(lock_pid, SIGTERM);
451 		(void)Pclose(lockfp);
452 		lockfp = NULL;
453 	} else if (action == 1) {
454 		char *cmd = _PATH_LOCKSPOOL;
455 
456 		/* XXX - lockspool requires root for user arg, we do not */
457 		if (uflag && asprintf(&cmd, "%s %s", _PATH_LOCKSPOOL,
458 		    myname) == -1)
459 			errx(1, "Out of memory");
460 
461 		/* Create the lock */
462 		lockfp = Popen(cmd, "r");
463 		if (uflag)
464 			free(cmd);
465 		if (lockfp == NULL || getc(lockfp) != '1') {
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()
481 {
482 	return(handle_spool_locks(1));
483 }
484 
485 int
486 spool_unlock()
487 {
488 	return(handle_spool_locks(0));
489 }
490