1 1.6 christos /* $NetBSD: popen.c,v 1.6 2018/06/14 22:04:28 christos Exp $ */ 2 1.2 christos 3 1.1 christos /* 4 1.1 christos * Copyright (c) 1988, 1993, 1994 5 1.1 christos * The Regents of the University of California. All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software written by Ken Arnold and 8 1.1 christos * published in UNIX Review, Vol. 6, No. 8. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer in the 17 1.1 christos * documentation and/or other materials provided with the distribution. 18 1.1 christos * 3. All advertising materials mentioning features or use of this software 19 1.1 christos * must display the following acknowledgement: 20 1.1 christos * This product includes software developed by the University of 21 1.1 christos * California, Berkeley and its contributors. 22 1.1 christos * 23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 1.1 christos * SUCH DAMAGE. 34 1.1 christos * 35 1.1 christos */ 36 1.1 christos 37 1.1 christos /* this came out of the ftpd sources; it's been modified to avoid the 38 1.1 christos * globbing stuff since we don't need it. also execvp instead of execv. 39 1.1 christos */ 40 1.1 christos 41 1.2 christos #include <sys/cdefs.h> 42 1.1 christos #ifndef lint 43 1.1 christos #if 0 44 1.1 christos static sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; 45 1.2 christos static char rcsid[] = "Id: popen.c,v 1.6 2003/02/16 04:40:01 vixie Exp"; 46 1.1 christos #else 47 1.6 christos __RCSID("$NetBSD: popen.c,v 1.6 2018/06/14 22:04:28 christos Exp $"); 48 1.1 christos #endif 49 1.1 christos #endif /* not lint */ 50 1.1 christos 51 1.1 christos #include "cron.h" 52 1.1 christos 53 1.1 christos #define MAX_ARGV 100 54 1.1 christos #define MAX_GARGV 1000 55 1.1 christos 56 1.1 christos /* 57 1.1 christos * Special version of popen which avoids call to shell. This ensures noone 58 1.1 christos * may create a pipe to a hidden program as a side effect of a list or dir 59 1.1 christos * command. 60 1.1 christos */ 61 1.1 christos static PID_T *pids; 62 1.1 christos 63 1.1 christos FILE * 64 1.2 christos cron_popen(char *program, const char *type, struct passwd *pw) { 65 1.1 christos char *cp; 66 1.1 christos FILE *iop; 67 1.1 christos int argc, pdes[2]; 68 1.1 christos PID_T pid; 69 1.1 christos char *argv[MAX_ARGV]; 70 1.1 christos 71 1.1 christos if ((*type != 'r' && *type != 'w') || type[1] != '\0') 72 1.1 christos return (NULL); 73 1.1 christos 74 1.1 christos if (!pids) { 75 1.2 christos size_t len; 76 1.4 christos long fds; 77 1.1 christos if ((fds = sysconf(_SC_OPEN_MAX)) <= 0) 78 1.1 christos return (NULL); 79 1.4 christos len = (size_t)fds * sizeof(*pids); 80 1.2 christos if ((pids = malloc(len)) == NULL) 81 1.1 christos return (NULL); 82 1.2 christos (void)memset(pids, 0, len); 83 1.1 christos } 84 1.1 christos if (pipe(pdes) < 0) 85 1.1 christos return (NULL); 86 1.1 christos 87 1.1 christos /* break up string into pieces */ 88 1.1 christos for (argc = 0, cp = program; argc < MAX_ARGV - 1; cp = NULL) 89 1.1 christos if (!(argv[argc++] = strtok(cp, " \t\n"))) 90 1.1 christos break; 91 1.1 christos argv[MAX_ARGV-1] = NULL; 92 1.1 christos 93 1.1 christos switch (pid = vfork()) { 94 1.1 christos case -1: /* error */ 95 1.1 christos (void)close(pdes[0]); 96 1.1 christos (void)close(pdes[1]); 97 1.1 christos return (NULL); 98 1.1 christos /* NOTREACHED */ 99 1.1 christos case 0: /* child */ 100 1.1 christos if (pw) { 101 1.3 christos if (setsid() == -1) 102 1.3 christos warn("setsid() failed for %s", pw->pw_name); 103 1.1 christos #ifdef LOGIN_CAP 104 1.3 christos if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0) 105 1.3 christos { 106 1.3 christos warn("setusercontext() failed for %s", 107 1.1 christos pw->pw_name); 108 1.1 christos _exit(ERROR_EXIT); 109 1.1 christos } 110 1.1 christos #else 111 1.1 christos if (setgid(pw->pw_gid) < 0 || 112 1.1 christos initgroups(pw->pw_name, pw->pw_gid) < 0) { 113 1.3 christos warn("unable to set groups for %s", 114 1.1 christos pw->pw_name); 115 1.3 christos _exit(ERROR_EXIT); 116 1.1 christos } 117 1.1 christos #if (defined(BSD)) && (BSD >= 199103) 118 1.3 christos if (setlogin(pw->pw_name) < 0) { 119 1.3 christos warn("setlogin() failed for %s", 120 1.3 christos pw->pw_name); 121 1.3 christos _exit(ERROR_EXIT); 122 1.3 christos } 123 1.1 christos #endif /* BSD */ 124 1.5 christos #ifdef USE_PAM 125 1.5 christos if (!cron_pam_setcred()) 126 1.5 christos _exit(1); 127 1.5 christos cron_pam_child_close(); 128 1.5 christos #endif 129 1.1 christos if (setuid(pw->pw_uid)) { 130 1.3 christos warn("unable to set uid for %s", pw->pw_name); 131 1.3 christos _exit(ERROR_EXIT); 132 1.1 christos } 133 1.1 christos #endif /* LOGIN_CAP */ 134 1.1 christos } 135 1.1 christos if (*type == 'r') { 136 1.1 christos if (pdes[1] != STDOUT) { 137 1.2 christos (void)dup2(pdes[1], STDOUT); 138 1.1 christos (void)close(pdes[1]); 139 1.1 christos } 140 1.2 christos (void)dup2(STDOUT, STDERR); /* stderr too! */ 141 1.1 christos (void)close(pdes[0]); 142 1.1 christos } else { 143 1.1 christos if (pdes[0] != STDIN) { 144 1.2 christos (void)dup2(pdes[0], STDIN); 145 1.1 christos (void)close(pdes[0]); 146 1.1 christos } 147 1.1 christos (void)close(pdes[1]); 148 1.1 christos } 149 1.2 christos (void)execvp(argv[0], argv); 150 1.3 christos _exit(ERROR_EXIT); 151 1.1 christos } 152 1.1 christos 153 1.1 christos /* parent; assume fdopen can't fail... */ 154 1.1 christos if (*type == 'r') { 155 1.1 christos iop = fdopen(pdes[0], type); 156 1.1 christos (void)close(pdes[1]); 157 1.1 christos } else { 158 1.1 christos iop = fdopen(pdes[1], type); 159 1.1 christos (void)close(pdes[0]); 160 1.1 christos } 161 1.1 christos pids[fileno(iop)] = pid; 162 1.1 christos 163 1.1 christos return (iop); 164 1.1 christos } 165 1.1 christos 166 1.6 christos static int 167 1.6 christos cron_finalize(FILE *iop, int sig) { 168 1.1 christos int fdes; 169 1.1 christos PID_T pid; 170 1.1 christos WAIT_T status; 171 1.2 christos sigset_t sset, osset; 172 1.1 christos 173 1.1 christos /* 174 1.1 christos * pclose returns -1 if stream is not associated with a 175 1.1 christos * `popened' command, or, if already `pclosed'. 176 1.1 christos */ 177 1.1 christos if (pids == 0 || pids[fdes = fileno(iop)] == 0) 178 1.1 christos return (-1); 179 1.6 christos 180 1.6 christos if (sig) { 181 1.6 christos if (kill(pids[fdes], sig) == -1) 182 1.6 christos return -1; 183 1.6 christos } else { 184 1.6 christos (void)fclose(iop); 185 1.6 christos } 186 1.2 christos (void)sigemptyset(&sset); 187 1.2 christos (void)sigaddset(&sset, SIGINT); 188 1.2 christos (void)sigaddset(&sset, SIGQUIT); 189 1.2 christos (void)sigaddset(&sset, SIGHUP); 190 1.2 christos (void)sigprocmask(SIG_BLOCK, &sset, &osset); 191 1.1 christos while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) 192 1.1 christos continue; 193 1.2 christos (void)sigprocmask(SIG_SETMASK, &osset, NULL); 194 1.6 christos if (sig) 195 1.6 christos (void)fclose(iop); 196 1.1 christos pids[fdes] = 0; 197 1.1 christos if (pid < 0) 198 1.6 christos return pid; 199 1.1 christos if (WIFEXITED(status)) 200 1.6 christos return WEXITSTATUS(status); 201 1.2 christos else 202 1.2 christos return WTERMSIG(status); 203 1.1 christos } 204 1.6 christos 205 1.6 christos int 206 1.6 christos cron_pclose(FILE *iop) { 207 1.6 christos return cron_finalize(iop, 0); 208 1.6 christos } 209 1.6 christos 210 1.6 christos int 211 1.6 christos cron_pabort(FILE *iop) { 212 1.6 christos int e = cron_finalize(iop, SIGKILL); 213 1.6 christos return e == SIGKILL ? 0 : e; 214 1.6 christos } 215