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