popen.c revision 270096
133965Sjdp/*
2104834Sobrien * Copyright (c) 1988, 1993
3218822Sdim *	The Regents of the University of California.  All rights reserved.
433965Sjdp * Copyright (c) 2014 The FreeBSD Foundation
533965Sjdp * All rights reserved.
633965Sjdp *
733965Sjdp * This code is derived from software written by Ken Arnold and
860484Sobrien * published in UNIX Review, Vol. 6, No. 8.
933965Sjdp *
1033965Sjdp * Portions of this software were developed by Edward Tomasz Napierala
1133965Sjdp * under sponsorship from the FreeBSD Foundation.
1233965Sjdp *
1333965Sjdp * Redistribution and use in source and binary forms, with or without
1433965Sjdp * modification, are permitted provided that the following conditions
1533965Sjdp * are met:
1633965Sjdp * 1. Redistributions of source code must retain the above copyright
1733965Sjdp *    notice, this list of conditions and the following disclaimer.
1833965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1933965Sjdp *    notice, this list of conditions and the following disclaimer in the
2033965Sjdp *    documentation and/or other materials provided with the distribution.
2133965Sjdp * 4. Neither the name of the University nor the names of its contributors
2233965Sjdp *    may be used to endorse or promote products derived from this software
2333965Sjdp *    without specific prior written permission.
24218822Sdim *
25218822Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2633965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27218822Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28218822Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29218822Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3033965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32218822Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33218822Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34218822Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35218822Sdim * SUCH DAMAGE.
36218822Sdim *
37218822Sdim * $FreeBSD: head/usr.sbin/autofs/popen.c 270096 2014-08-17 09:44:42Z trasz $
38218822Sdim */
39218822Sdim
40218822Sdim#include <sys/param.h>
41218822Sdim#include <sys/queue.h>
42218822Sdim#include <sys/wait.h>
43218822Sdim
44218822Sdim#include <errno.h>
45218822Sdim#include <fcntl.h>
46218822Sdim#include <unistd.h>
47218822Sdim#include <stdarg.h>
48218822Sdim#include <stdio.h>
49218822Sdim#include <stdlib.h>
50218822Sdim#include <string.h>
5133965Sjdp#include <paths.h>
5233965Sjdp
5360484Sobrien#include "common.h"
5460484Sobrien
5533965Sjdpextern char **environ;
5633965Sjdp
5733965Sjdpstruct pid {
5833965Sjdp	SLIST_ENTRY(pid) next;
5933965Sjdp	FILE *outfp;
6077298Sobrien	pid_t pid;
6133965Sjdp	char *command;
6233965Sjdp};
63218822Sdimstatic SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
6489857Sobrien
6533965Sjdp#define	ARGV_LEN	42
66130561Sobrien
6733965Sjdp/*
68218822Sdim * Replacement for popen(3), without stdin (which we do not use), but with
69218822Sdim * stderr, proper logging, and improved command line arguments passing.
70218822Sdim * Error handling is built in - if it returns, then it succeeded.
71218822Sdim */
7260484SobrienFILE *
73130561Sobrienauto_popen(const char *argv0, ...)
74130561Sobrien{
7533965Sjdp	va_list ap;
7633965Sjdp	struct pid *cur, *p;
7733965Sjdp	pid_t pid;
7833965Sjdp	int error, i, nullfd, outfds[2];
79130561Sobrien	char *arg, *argv[ARGV_LEN], *command;
80130561Sobrien
8133965Sjdp	nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
8233965Sjdp	if (nullfd < 0)
8333965Sjdp		log_err(1, "cannot open %s", _PATH_DEVNULL);
8489857Sobrien
8533965Sjdp	error = pipe(outfds);
8633965Sjdp	if (error != 0)
87130561Sobrien		log_err(1, "pipe");
88130561Sobrien
89130561Sobrien	cur = malloc(sizeof(struct pid));
90130561Sobrien	if (cur == NULL)
91130561Sobrien		log_err(1, "malloc");
92130561Sobrien
93130561Sobrien	argv[0] = checked_strdup(argv0);
94130561Sobrien	command = argv[0];
95130561Sobrien
96130561Sobrien	va_start(ap, argv0);
9733965Sjdp	for (i = 1;; i++) {
98130561Sobrien		if (i >= ARGV_LEN)
99130561Sobrien			log_errx(1, "too many arguments to auto_popen");
10033965Sjdp		arg = va_arg(ap, char *);
101218822Sdim		argv[i] = arg;
102218822Sdim		if (arg == NULL)
10333965Sjdp			break;
10433965Sjdp
10533965Sjdp		command = separated_concat(command, arg, ' ');
10677298Sobrien	}
10733965Sjdp	va_end(ap);
108218822Sdim
109218822Sdim	cur->command = checked_strdup(command);
11033965Sjdp
111104834Sobrien	switch (pid = fork()) {
112218822Sdim	case -1:			/* Error. */
113218822Sdim		log_err(1, "fork");
114218822Sdim		/* NOTREACHED */
115218822Sdim	case 0:				/* Child. */
116218822Sdim		dup2(nullfd, STDIN_FILENO);
117218822Sdim		dup2(outfds[1], STDOUT_FILENO);
118218822Sdim
119218822Sdim		close(nullfd);
120218822Sdim		close(outfds[0]);
121218822Sdim		close(outfds[1]);
122218822Sdim
123218822Sdim		SLIST_FOREACH(p, &pidlist, next)
124218822Sdim			close(fileno(p->outfp));
125218822Sdim		execvp(argv[0], argv);
126218822Sdim		log_err(1, "failed to execute %s", argv[0]);
127218822Sdim		/* NOTREACHED */
128218822Sdim	}
129218822Sdim
130218822Sdim	log_debugx("executing \"%s\" as pid %d", command, pid);
131218822Sdim
132218822Sdim	/* Parent; assume fdopen cannot fail. */
133218822Sdim	cur->outfp = fdopen(outfds[0], "r");
134218822Sdim	close(nullfd);
13560484Sobrien	close(outfds[1]);
13689857Sobrien
13789857Sobrien	/* Link into list of file descriptors. */
13889857Sobrien	cur->pid = pid;
13989857Sobrien	SLIST_INSERT_HEAD(&pidlist, cur, next);
140104834Sobrien
141104834Sobrien	return (cur->outfp);
142218822Sdim}
14360484Sobrien
144104834Sobrienint
14589857Sobrienauto_pclose(FILE *iop)
146130561Sobrien{
14733965Sjdp	struct pid *cur, *last = NULL;
14833965Sjdp	int status;
14933965Sjdp	pid_t pid;
15033965Sjdp
15133965Sjdp	/*
152130561Sobrien	 * Find the appropriate file pointer and remove it from the list.
15333965Sjdp	 */
15433965Sjdp	SLIST_FOREACH(cur, &pidlist, next) {
15533965Sjdp		if (cur->outfp == iop)
15633965Sjdp			break;
15733965Sjdp		last = cur;
15838889Sjdp	}
15938889Sjdp	if (cur == NULL) {
16033965Sjdp		return (-1);
16138889Sjdp	}
16238889Sjdp	if (last == NULL)
16377298Sobrien		SLIST_REMOVE_HEAD(&pidlist, next);
16438889Sjdp	else
16577298Sobrien		SLIST_REMOVE_AFTER(last, next);
16638889Sjdp
16777298Sobrien	fclose(cur->outfp);
16877298Sobrien
16977298Sobrien	do {
17077298Sobrien		pid = wait4(cur->pid, &status, 0, NULL);
17138889Sjdp	} while (pid == -1 && errno == EINTR);
17233965Sjdp
17333965Sjdp	if (WIFSIGNALED(status)) {
17433965Sjdp		log_warnx("\"%s\", pid %d, terminated with signal %d",
17533965Sjdp		    cur->command, pid, WTERMSIG(status));
17633965Sjdp		return (status);
17733965Sjdp	}
17877298Sobrien
17977298Sobrien	if (WEXITSTATUS(status) != 0) {
18033965Sjdp		log_warnx("\"%s\", pid %d, terminated with exit status %d",
18177298Sobrien		    cur->command, pid, WEXITSTATUS(status));
18277298Sobrien		return (status);
18333965Sjdp	}
18433965Sjdp
18533965Sjdp	log_debugx("\"%s\", pid %d, terminated gracefully", cur->command, pid);
186130561Sobrien
18733965Sjdp	free(cur->command);
18877298Sobrien	free(cur);
18933965Sjdp
19033965Sjdp	return (pid == -1 ? -1 : status);
19177298Sobrien}
19233965Sjdp