/*1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1988, 19934* The Regents of the University of California. All rights reserved.5* Copyright (c) 2014 The FreeBSD Foundation6* All rights reserved.7*8* This code is derived from software written by Ken Arnold and9* published in UNIX Review, Vol. 6, No. 8.10*11* Portions of this software were developed by Edward Tomasz Napierala12* under sponsorship from the FreeBSD Foundation.13*14* Redistribution and use in source and binary forms, with or without15* modification, are permitted provided that the following conditions16* are met:17* 1. Redistributions of source code must retain the above copyright18* notice, this list of conditions and the following disclaimer.19* 2. Redistributions in binary form must reproduce the above copyright20* notice, this list of conditions and the following disclaimer in the21* documentation and/or other materials provided with the distribution.22* 3. Neither the name of the University nor the names of its contributors23* may be used to endorse or promote products derived from this software24* without specific prior written permission.25*26* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND27* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE28* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE29* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE30* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL31* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS32* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)33* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT34* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY35* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF36* SUCH DAMAGE.37*38*/3940#include <sys/param.h>41#include <sys/queue.h>42#include <sys/wait.h>4344#include <errno.h>45#include <fcntl.h>46#include <unistd.h>47#include <stdarg.h>48#include <stdio.h>49#include <stdlib.h>50#include <string.h>51#include <paths.h>5253#include "common.h"5455extern char **environ;5657struct pid {58SLIST_ENTRY(pid) next;59FILE *outfp;60pid_t pid;61char *command;62};63static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);6465#define ARGV_LEN 426667/*68* Replacement for popen(3), without stdin (which we do not use), but with69* stderr, proper logging, and improved command line arguments passing.70* Error handling is built in - if it returns, then it succeeded.71*/72FILE *73auto_popen(const char *argv0, ...)74{75va_list ap;76struct pid *cur, *p;77pid_t pid;78int error, i, nullfd, outfds[2];79char *arg, *argv[ARGV_LEN], *command;8081nullfd = open(_PATH_DEVNULL, O_RDWR, 0);82if (nullfd < 0)83log_err(1, "cannot open %s", _PATH_DEVNULL);8485error = pipe(outfds);86if (error != 0)87log_err(1, "pipe");8889cur = malloc(sizeof(struct pid));90if (cur == NULL)91log_err(1, "malloc");9293argv[0] = checked_strdup(argv0);94command = argv[0];9596va_start(ap, argv0);97for (i = 1;; i++) {98if (i >= ARGV_LEN)99log_errx(1, "too many arguments to auto_popen");100arg = va_arg(ap, char *);101argv[i] = arg;102if (arg == NULL)103break;104105command = concat(command, ' ', arg);106}107va_end(ap);108109cur->command = checked_strdup(command);110111switch (pid = fork()) {112case -1: /* Error. */113log_err(1, "fork");114/* NOTREACHED */115case 0: /* Child. */116dup2(nullfd, STDIN_FILENO);117dup2(outfds[1], STDOUT_FILENO);118119close(nullfd);120close(outfds[0]);121close(outfds[1]);122123SLIST_FOREACH(p, &pidlist, next)124close(fileno(p->outfp));125execvp(argv[0], argv);126log_err(1, "failed to execute %s", argv[0]);127/* NOTREACHED */128}129130log_debugx("executing \"%s\" as pid %d", command, pid);131132/* Parent; assume fdopen cannot fail. */133cur->outfp = fdopen(outfds[0], "r");134close(nullfd);135close(outfds[1]);136137/* Link into list of file descriptors. */138cur->pid = pid;139SLIST_INSERT_HEAD(&pidlist, cur, next);140141return (cur->outfp);142}143144int145auto_pclose(FILE *iop)146{147struct pid *cur, *last = NULL;148int status;149pid_t pid;150151/*152* Find the appropriate file pointer and remove it from the list.153*/154SLIST_FOREACH(cur, &pidlist, next) {155if (cur->outfp == iop)156break;157last = cur;158}159if (cur == NULL) {160return (-1);161}162if (last == NULL)163SLIST_REMOVE_HEAD(&pidlist, next);164else165SLIST_REMOVE_AFTER(last, next);166167fclose(cur->outfp);168169do {170pid = wait4(cur->pid, &status, 0, NULL);171} while (pid == -1 && errno == EINTR);172173if (WIFSIGNALED(status)) {174log_warnx("\"%s\", pid %d, terminated with signal %d",175cur->command, pid, WTERMSIG(status));176return (status);177}178179if (WEXITSTATUS(status) != 0) {180log_warnx("\"%s\", pid %d, terminated with exit status %d",181cur->command, pid, WEXITSTATUS(status));182return (status);183}184185log_debugx("\"%s\", pid %d, terminated gracefully", cur->command, pid);186187free(cur->command);188free(cur);189190return (pid == -1 ? -1 : status);191}192193194