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