13914ddf8SEdward Tomasz Napierala /*
2*abdd3945SEdward Tomasz Napierala * SPDX-License-Identifier: BSD-3-Clause
3*abdd3945SEdward Tomasz Napierala *
43914ddf8SEdward Tomasz Napierala * Copyright (c) 1988, 1993
53914ddf8SEdward Tomasz Napierala * The Regents of the University of California. All rights reserved.
63914ddf8SEdward Tomasz Napierala * Copyright (c) 2014 The FreeBSD Foundation
73914ddf8SEdward Tomasz Napierala * All rights reserved.
83914ddf8SEdward Tomasz Napierala *
93914ddf8SEdward Tomasz Napierala * This code is derived from software written by Ken Arnold and
103914ddf8SEdward Tomasz Napierala * published in UNIX Review, Vol. 6, No. 8.
113914ddf8SEdward Tomasz Napierala *
123914ddf8SEdward Tomasz Napierala * Portions of this software were developed by Edward Tomasz Napierala
133914ddf8SEdward Tomasz Napierala * under sponsorship from the FreeBSD Foundation.
143914ddf8SEdward Tomasz Napierala *
153914ddf8SEdward Tomasz Napierala * Redistribution and use in source and binary forms, with or without
163914ddf8SEdward Tomasz Napierala * modification, are permitted provided that the following conditions
173914ddf8SEdward Tomasz Napierala * are met:
183914ddf8SEdward Tomasz Napierala * 1. Redistributions of source code must retain the above copyright
193914ddf8SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer.
203914ddf8SEdward Tomasz Napierala * 2. Redistributions in binary form must reproduce the above copyright
213914ddf8SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer in the
223914ddf8SEdward Tomasz Napierala * documentation and/or other materials provided with the distribution.
23fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors
243914ddf8SEdward Tomasz Napierala * may be used to endorse or promote products derived from this software
253914ddf8SEdward Tomasz Napierala * without specific prior written permission.
263914ddf8SEdward Tomasz Napierala *
273914ddf8SEdward Tomasz Napierala * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
283914ddf8SEdward Tomasz Napierala * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
293914ddf8SEdward Tomasz Napierala * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
303914ddf8SEdward Tomasz Napierala * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
313914ddf8SEdward Tomasz Napierala * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
323914ddf8SEdward Tomasz Napierala * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
333914ddf8SEdward Tomasz Napierala * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
343914ddf8SEdward Tomasz Napierala * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
353914ddf8SEdward Tomasz Napierala * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
363914ddf8SEdward Tomasz Napierala * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
373914ddf8SEdward Tomasz Napierala * SUCH DAMAGE.
383914ddf8SEdward Tomasz Napierala *
393914ddf8SEdward Tomasz Napierala */
403914ddf8SEdward Tomasz Napierala
413914ddf8SEdward Tomasz Napierala #include <sys/param.h>
423914ddf8SEdward Tomasz Napierala #include <sys/queue.h>
433914ddf8SEdward Tomasz Napierala #include <sys/wait.h>
443914ddf8SEdward Tomasz Napierala
453914ddf8SEdward Tomasz Napierala #include <errno.h>
463914ddf8SEdward Tomasz Napierala #include <fcntl.h>
473914ddf8SEdward Tomasz Napierala #include <unistd.h>
483914ddf8SEdward Tomasz Napierala #include <stdarg.h>
493914ddf8SEdward Tomasz Napierala #include <stdio.h>
503914ddf8SEdward Tomasz Napierala #include <stdlib.h>
513914ddf8SEdward Tomasz Napierala #include <string.h>
523914ddf8SEdward Tomasz Napierala #include <paths.h>
533914ddf8SEdward Tomasz Napierala
543914ddf8SEdward Tomasz Napierala #include "common.h"
553914ddf8SEdward Tomasz Napierala
563914ddf8SEdward Tomasz Napierala extern char **environ;
573914ddf8SEdward Tomasz Napierala
583914ddf8SEdward Tomasz Napierala struct pid {
593914ddf8SEdward Tomasz Napierala SLIST_ENTRY(pid) next;
603914ddf8SEdward Tomasz Napierala FILE *outfp;
613914ddf8SEdward Tomasz Napierala pid_t pid;
623914ddf8SEdward Tomasz Napierala char *command;
633914ddf8SEdward Tomasz Napierala };
643914ddf8SEdward Tomasz Napierala static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
653914ddf8SEdward Tomasz Napierala
663914ddf8SEdward Tomasz Napierala #define ARGV_LEN 42
673914ddf8SEdward Tomasz Napierala
683914ddf8SEdward Tomasz Napierala /*
693914ddf8SEdward Tomasz Napierala * Replacement for popen(3), without stdin (which we do not use), but with
703914ddf8SEdward Tomasz Napierala * stderr, proper logging, and improved command line arguments passing.
713914ddf8SEdward Tomasz Napierala * Error handling is built in - if it returns, then it succeeded.
723914ddf8SEdward Tomasz Napierala */
733914ddf8SEdward Tomasz Napierala FILE *
auto_popen(const char * argv0,...)743914ddf8SEdward Tomasz Napierala auto_popen(const char *argv0, ...)
753914ddf8SEdward Tomasz Napierala {
763914ddf8SEdward Tomasz Napierala va_list ap;
773914ddf8SEdward Tomasz Napierala struct pid *cur, *p;
783914ddf8SEdward Tomasz Napierala pid_t pid;
793914ddf8SEdward Tomasz Napierala int error, i, nullfd, outfds[2];
803914ddf8SEdward Tomasz Napierala char *arg, *argv[ARGV_LEN], *command;
813914ddf8SEdward Tomasz Napierala
823914ddf8SEdward Tomasz Napierala nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
833914ddf8SEdward Tomasz Napierala if (nullfd < 0)
843914ddf8SEdward Tomasz Napierala log_err(1, "cannot open %s", _PATH_DEVNULL);
853914ddf8SEdward Tomasz Napierala
863914ddf8SEdward Tomasz Napierala error = pipe(outfds);
873914ddf8SEdward Tomasz Napierala if (error != 0)
883914ddf8SEdward Tomasz Napierala log_err(1, "pipe");
893914ddf8SEdward Tomasz Napierala
903914ddf8SEdward Tomasz Napierala cur = malloc(sizeof(struct pid));
913914ddf8SEdward Tomasz Napierala if (cur == NULL)
923914ddf8SEdward Tomasz Napierala log_err(1, "malloc");
933914ddf8SEdward Tomasz Napierala
943914ddf8SEdward Tomasz Napierala argv[0] = checked_strdup(argv0);
953914ddf8SEdward Tomasz Napierala command = argv[0];
963914ddf8SEdward Tomasz Napierala
973914ddf8SEdward Tomasz Napierala va_start(ap, argv0);
983914ddf8SEdward Tomasz Napierala for (i = 1;; i++) {
993914ddf8SEdward Tomasz Napierala if (i >= ARGV_LEN)
1003914ddf8SEdward Tomasz Napierala log_errx(1, "too many arguments to auto_popen");
1013914ddf8SEdward Tomasz Napierala arg = va_arg(ap, char *);
1023914ddf8SEdward Tomasz Napierala argv[i] = arg;
1033914ddf8SEdward Tomasz Napierala if (arg == NULL)
1043914ddf8SEdward Tomasz Napierala break;
1053914ddf8SEdward Tomasz Napierala
1066d8e60c3SEdward Tomasz Napierala command = concat(command, ' ', arg);
1073914ddf8SEdward Tomasz Napierala }
1083914ddf8SEdward Tomasz Napierala va_end(ap);
1093914ddf8SEdward Tomasz Napierala
1103914ddf8SEdward Tomasz Napierala cur->command = checked_strdup(command);
1113914ddf8SEdward Tomasz Napierala
1123914ddf8SEdward Tomasz Napierala switch (pid = fork()) {
1133914ddf8SEdward Tomasz Napierala case -1: /* Error. */
1143914ddf8SEdward Tomasz Napierala log_err(1, "fork");
1153914ddf8SEdward Tomasz Napierala /* NOTREACHED */
1163914ddf8SEdward Tomasz Napierala case 0: /* Child. */
1173914ddf8SEdward Tomasz Napierala dup2(nullfd, STDIN_FILENO);
1183914ddf8SEdward Tomasz Napierala dup2(outfds[1], STDOUT_FILENO);
1193914ddf8SEdward Tomasz Napierala
1203914ddf8SEdward Tomasz Napierala close(nullfd);
1213914ddf8SEdward Tomasz Napierala close(outfds[0]);
1223914ddf8SEdward Tomasz Napierala close(outfds[1]);
1233914ddf8SEdward Tomasz Napierala
1243914ddf8SEdward Tomasz Napierala SLIST_FOREACH(p, &pidlist, next)
1253914ddf8SEdward Tomasz Napierala close(fileno(p->outfp));
1263914ddf8SEdward Tomasz Napierala execvp(argv[0], argv);
1273914ddf8SEdward Tomasz Napierala log_err(1, "failed to execute %s", argv[0]);
1283914ddf8SEdward Tomasz Napierala /* NOTREACHED */
1293914ddf8SEdward Tomasz Napierala }
1303914ddf8SEdward Tomasz Napierala
1313914ddf8SEdward Tomasz Napierala log_debugx("executing \"%s\" as pid %d", command, pid);
1323914ddf8SEdward Tomasz Napierala
1333914ddf8SEdward Tomasz Napierala /* Parent; assume fdopen cannot fail. */
1343914ddf8SEdward Tomasz Napierala cur->outfp = fdopen(outfds[0], "r");
1353914ddf8SEdward Tomasz Napierala close(nullfd);
1363914ddf8SEdward Tomasz Napierala close(outfds[1]);
1373914ddf8SEdward Tomasz Napierala
1383914ddf8SEdward Tomasz Napierala /* Link into list of file descriptors. */
1393914ddf8SEdward Tomasz Napierala cur->pid = pid;
1403914ddf8SEdward Tomasz Napierala SLIST_INSERT_HEAD(&pidlist, cur, next);
1413914ddf8SEdward Tomasz Napierala
1423914ddf8SEdward Tomasz Napierala return (cur->outfp);
1433914ddf8SEdward Tomasz Napierala }
1443914ddf8SEdward Tomasz Napierala
1453914ddf8SEdward Tomasz Napierala int
auto_pclose(FILE * iop)1463914ddf8SEdward Tomasz Napierala auto_pclose(FILE *iop)
1473914ddf8SEdward Tomasz Napierala {
1483914ddf8SEdward Tomasz Napierala struct pid *cur, *last = NULL;
1493914ddf8SEdward Tomasz Napierala int status;
1503914ddf8SEdward Tomasz Napierala pid_t pid;
1513914ddf8SEdward Tomasz Napierala
1523914ddf8SEdward Tomasz Napierala /*
1533914ddf8SEdward Tomasz Napierala * Find the appropriate file pointer and remove it from the list.
1543914ddf8SEdward Tomasz Napierala */
1553914ddf8SEdward Tomasz Napierala SLIST_FOREACH(cur, &pidlist, next) {
1563914ddf8SEdward Tomasz Napierala if (cur->outfp == iop)
1573914ddf8SEdward Tomasz Napierala break;
1583914ddf8SEdward Tomasz Napierala last = cur;
1593914ddf8SEdward Tomasz Napierala }
1603914ddf8SEdward Tomasz Napierala if (cur == NULL) {
1613914ddf8SEdward Tomasz Napierala return (-1);
1623914ddf8SEdward Tomasz Napierala }
1633914ddf8SEdward Tomasz Napierala if (last == NULL)
1643914ddf8SEdward Tomasz Napierala SLIST_REMOVE_HEAD(&pidlist, next);
1653914ddf8SEdward Tomasz Napierala else
1663914ddf8SEdward Tomasz Napierala SLIST_REMOVE_AFTER(last, next);
1673914ddf8SEdward Tomasz Napierala
1683914ddf8SEdward Tomasz Napierala fclose(cur->outfp);
1693914ddf8SEdward Tomasz Napierala
1703914ddf8SEdward Tomasz Napierala do {
1713914ddf8SEdward Tomasz Napierala pid = wait4(cur->pid, &status, 0, NULL);
1723914ddf8SEdward Tomasz Napierala } while (pid == -1 && errno == EINTR);
1733914ddf8SEdward Tomasz Napierala
1743914ddf8SEdward Tomasz Napierala if (WIFSIGNALED(status)) {
1753914ddf8SEdward Tomasz Napierala log_warnx("\"%s\", pid %d, terminated with signal %d",
1763914ddf8SEdward Tomasz Napierala cur->command, pid, WTERMSIG(status));
1773914ddf8SEdward Tomasz Napierala return (status);
1783914ddf8SEdward Tomasz Napierala }
1793914ddf8SEdward Tomasz Napierala
1803914ddf8SEdward Tomasz Napierala if (WEXITSTATUS(status) != 0) {
1813914ddf8SEdward Tomasz Napierala log_warnx("\"%s\", pid %d, terminated with exit status %d",
1823914ddf8SEdward Tomasz Napierala cur->command, pid, WEXITSTATUS(status));
1833914ddf8SEdward Tomasz Napierala return (status);
1843914ddf8SEdward Tomasz Napierala }
1853914ddf8SEdward Tomasz Napierala
1863914ddf8SEdward Tomasz Napierala log_debugx("\"%s\", pid %d, terminated gracefully", cur->command, pid);
1873914ddf8SEdward Tomasz Napierala
1883914ddf8SEdward Tomasz Napierala free(cur->command);
1893914ddf8SEdward Tomasz Napierala free(cur);
1903914ddf8SEdward Tomasz Napierala
1913914ddf8SEdward Tomasz Napierala return (pid == -1 ? -1 : status);
1923914ddf8SEdward Tomasz Napierala }
193