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