1270096Strasz/*
2332597Strasz * SPDX-License-Identifier: BSD-3-Clause
3332597Strasz *
4270096Strasz * Copyright (c) 1988, 1993
5270096Strasz *	The Regents of the University of California.  All rights reserved.
6270096Strasz * Copyright (c) 2014 The FreeBSD Foundation
7270096Strasz * All rights reserved.
8270096Strasz *
9270096Strasz * This code is derived from software written by Ken Arnold and
10270096Strasz * published in UNIX Review, Vol. 6, No. 8.
11270096Strasz *
12270096Strasz * Portions of this software were developed by Edward Tomasz Napierala
13270096Strasz * under sponsorship from the FreeBSD Foundation.
14270096Strasz *
15270096Strasz * Redistribution and use in source and binary forms, with or without
16270096Strasz * modification, are permitted provided that the following conditions
17270096Strasz * are met:
18270096Strasz * 1. Redistributions of source code must retain the above copyright
19270096Strasz *    notice, this list of conditions and the following disclaimer.
20270096Strasz * 2. Redistributions in binary form must reproduce the above copyright
21270096Strasz *    notice, this list of conditions and the following disclaimer in the
22270096Strasz *    documentation and/or other materials provided with the distribution.
23270096Strasz * 4. Neither the name of the University nor the names of its contributors
24270096Strasz *    may be used to endorse or promote products derived from this software
25270096Strasz *    without specific prior written permission.
26270096Strasz *
27270096Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30270096Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37270096Strasz * SUCH DAMAGE.
38270096Strasz *
39270096Strasz */
40270096Strasz
41270276Strasz#include <sys/cdefs.h>
42270276Strasz__FBSDID("$FreeBSD: stable/11/usr.sbin/autofs/popen.c 332597 2018-04-16 16:16:24Z trasz $");
43270276Strasz
44270096Strasz#include <sys/param.h>
45270096Strasz#include <sys/queue.h>
46270096Strasz#include <sys/wait.h>
47270096Strasz
48270096Strasz#include <errno.h>
49270096Strasz#include <fcntl.h>
50270096Strasz#include <unistd.h>
51270096Strasz#include <stdarg.h>
52270096Strasz#include <stdio.h>
53270096Strasz#include <stdlib.h>
54270096Strasz#include <string.h>
55270096Strasz#include <paths.h>
56270096Strasz
57270096Strasz#include "common.h"
58270096Strasz
59270096Straszextern char **environ;
60270096Strasz
61270096Straszstruct pid {
62270096Strasz	SLIST_ENTRY(pid) next;
63270096Strasz	FILE *outfp;
64270096Strasz	pid_t pid;
65270096Strasz	char *command;
66270096Strasz};
67270096Straszstatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
68270096Strasz
69270096Strasz#define	ARGV_LEN	42
70270096Strasz
71270096Strasz/*
72270096Strasz * Replacement for popen(3), without stdin (which we do not use), but with
73270096Strasz * stderr, proper logging, and improved command line arguments passing.
74270096Strasz * Error handling is built in - if it returns, then it succeeded.
75270096Strasz */
76270096StraszFILE *
77270096Straszauto_popen(const char *argv0, ...)
78270096Strasz{
79270096Strasz	va_list ap;
80270096Strasz	struct pid *cur, *p;
81270096Strasz	pid_t pid;
82270096Strasz	int error, i, nullfd, outfds[2];
83270096Strasz	char *arg, *argv[ARGV_LEN], *command;
84270096Strasz
85270096Strasz	nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
86270096Strasz	if (nullfd < 0)
87270096Strasz		log_err(1, "cannot open %s", _PATH_DEVNULL);
88270096Strasz
89270096Strasz	error = pipe(outfds);
90270096Strasz	if (error != 0)
91270096Strasz		log_err(1, "pipe");
92270096Strasz
93270096Strasz	cur = malloc(sizeof(struct pid));
94270096Strasz	if (cur == NULL)
95270096Strasz		log_err(1, "malloc");
96270096Strasz
97270096Strasz	argv[0] = checked_strdup(argv0);
98270096Strasz	command = argv[0];
99270096Strasz
100270096Strasz	va_start(ap, argv0);
101270096Strasz	for (i = 1;; i++) {
102270096Strasz		if (i >= ARGV_LEN)
103270096Strasz			log_errx(1, "too many arguments to auto_popen");
104270096Strasz		arg = va_arg(ap, char *);
105270096Strasz		argv[i] = arg;
106270096Strasz		if (arg == NULL)
107270096Strasz			break;
108270096Strasz
109279813Strasz		command = concat(command, ' ', arg);
110270096Strasz	}
111270096Strasz	va_end(ap);
112270096Strasz
113270096Strasz	cur->command = checked_strdup(command);
114270096Strasz
115270096Strasz	switch (pid = fork()) {
116270096Strasz	case -1:			/* Error. */
117270096Strasz		log_err(1, "fork");
118270096Strasz		/* NOTREACHED */
119270096Strasz	case 0:				/* Child. */
120270096Strasz		dup2(nullfd, STDIN_FILENO);
121270096Strasz		dup2(outfds[1], STDOUT_FILENO);
122270096Strasz
123270096Strasz		close(nullfd);
124270096Strasz		close(outfds[0]);
125270096Strasz		close(outfds[1]);
126270096Strasz
127270096Strasz		SLIST_FOREACH(p, &pidlist, next)
128270096Strasz			close(fileno(p->outfp));
129270096Strasz		execvp(argv[0], argv);
130270096Strasz		log_err(1, "failed to execute %s", argv[0]);
131270096Strasz		/* NOTREACHED */
132270096Strasz	}
133270096Strasz
134270096Strasz	log_debugx("executing \"%s\" as pid %d", command, pid);
135270096Strasz
136270096Strasz	/* Parent; assume fdopen cannot fail. */
137270096Strasz	cur->outfp = fdopen(outfds[0], "r");
138270096Strasz	close(nullfd);
139270096Strasz	close(outfds[1]);
140270096Strasz
141270096Strasz	/* Link into list of file descriptors. */
142270096Strasz	cur->pid = pid;
143270096Strasz	SLIST_INSERT_HEAD(&pidlist, cur, next);
144270096Strasz
145270096Strasz	return (cur->outfp);
146270096Strasz}
147270096Strasz
148270096Straszint
149270096Straszauto_pclose(FILE *iop)
150270096Strasz{
151270096Strasz	struct pid *cur, *last = NULL;
152270096Strasz	int status;
153270096Strasz	pid_t pid;
154270096Strasz
155270096Strasz	/*
156270096Strasz	 * Find the appropriate file pointer and remove it from the list.
157270096Strasz	 */
158270096Strasz	SLIST_FOREACH(cur, &pidlist, next) {
159270096Strasz		if (cur->outfp == iop)
160270096Strasz			break;
161270096Strasz		last = cur;
162270096Strasz	}
163270096Strasz	if (cur == NULL) {
164270096Strasz		return (-1);
165270096Strasz	}
166270096Strasz	if (last == NULL)
167270096Strasz		SLIST_REMOVE_HEAD(&pidlist, next);
168270096Strasz	else
169270096Strasz		SLIST_REMOVE_AFTER(last, next);
170270096Strasz
171270096Strasz	fclose(cur->outfp);
172270096Strasz
173270096Strasz	do {
174270096Strasz		pid = wait4(cur->pid, &status, 0, NULL);
175270096Strasz	} while (pid == -1 && errno == EINTR);
176270096Strasz
177270096Strasz	if (WIFSIGNALED(status)) {
178270096Strasz		log_warnx("\"%s\", pid %d, terminated with signal %d",
179270096Strasz		    cur->command, pid, WTERMSIG(status));
180270096Strasz		return (status);
181270096Strasz	}
182270096Strasz
183270096Strasz	if (WEXITSTATUS(status) != 0) {
184270096Strasz		log_warnx("\"%s\", pid %d, terminated with exit status %d",
185270096Strasz		    cur->command, pid, WEXITSTATUS(status));
186270096Strasz		return (status);
187270096Strasz	}
188270096Strasz
189270096Strasz	log_debugx("\"%s\", pid %d, terminated gracefully", cur->command, pid);
190270096Strasz
191270096Strasz	free(cur->command);
192270096Strasz	free(cur);
193270096Strasz
194270096Strasz	return (pid == -1 ? -1 : status);
195270096Strasz}
196