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