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