1 /* $OpenBSD: popen.c,v 1.25 2015/01/23 19:07:27 tedu 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. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 */ 35 36 /* this came out of the ftpd sources; it's been modified to avoid the 37 * globbing stuff since we don't need it. also execvp instead of execv. 38 */ 39 40 #include "cron.h" 41 42 #define MAX_ARGV 100 43 #define MAX_GARGV 1000 44 45 /* 46 * Special version of popen which avoids call to shell. This ensures noone 47 * may create a pipe to a hidden program as a side effect of a list or dir 48 * command. 49 */ 50 static pid_t *pids; 51 static int fds; 52 53 FILE * 54 cron_popen(char *program, char *type, struct passwd *pw) 55 { 56 char *cp; 57 FILE *iop; 58 int argc, pdes[2]; 59 pid_t pid; 60 char *argv[MAX_ARGV]; 61 62 if ((*type != 'r' && *type != 'w') || type[1] != '\0') 63 return (NULL); 64 65 if (!pids) { 66 if ((fds = sysconf(_SC_OPEN_MAX)) <= 0) 67 return (NULL); 68 if (!(pids = calloc(fds, sizeof(pid_t)))) 69 return (NULL); 70 } 71 if (pipe(pdes) < 0) 72 return (NULL); 73 74 /* break up string into pieces */ 75 for (argc = 0, cp = program; argc < MAX_ARGV - 1; cp = NULL) 76 if (!(argv[argc++] = strtok(cp, " \t\n"))) 77 break; 78 argv[MAX_ARGV-1] = NULL; 79 80 switch (pid = fork()) { 81 case -1: /* error */ 82 (void)close(pdes[0]); 83 (void)close(pdes[1]); 84 return (NULL); 85 /* NOTREACHED */ 86 case 0: /* child */ 87 if (pw) { 88 #ifdef LOGIN_CAP 89 if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0) { 90 fprintf(stderr, 91 "setusercontext failed for %s\n", 92 pw->pw_name); 93 _exit(EXIT_FAILURE); 94 } 95 #else 96 if (setgid(pw->pw_gid) < 0 || 97 initgroups(pw->pw_name, pw->pw_gid) < 0) { 98 fprintf(stderr, 99 "unable to set groups for %s\n", 100 pw->pw_name); 101 _exit(1); 102 } 103 #ifdef HAVE_SETLOGIN 104 setlogin(pw->pw_name); 105 #endif 106 if (setuid(pw->pw_uid)) { 107 fprintf(stderr, 108 "unable to set uid for %s\n", 109 pw->pw_name); 110 _exit(1); 111 } 112 #endif /* LOGIN_CAP */ 113 } 114 if (*type == 'r') { 115 if (pdes[1] != STDOUT_FILENO) { 116 dup2(pdes[1], STDOUT_FILENO); 117 (void)close(pdes[1]); 118 } 119 dup2(STDOUT_FILENO, STDERR_FILENO); 120 (void)close(pdes[0]); 121 } else { 122 if (pdes[0] != STDIN_FILENO) { 123 dup2(pdes[0], STDIN_FILENO); 124 (void)close(pdes[0]); 125 } 126 (void)close(pdes[1]); 127 } 128 execvp(argv[0], argv); 129 _exit(1); 130 } 131 132 /* parent; assume fdopen can't fail... */ 133 if (*type == 'r') { 134 iop = fdopen(pdes[0], type); 135 (void)close(pdes[1]); 136 } else { 137 iop = fdopen(pdes[1], type); 138 (void)close(pdes[0]); 139 } 140 pids[fileno(iop)] = pid; 141 142 return (iop); 143 } 144 145 int 146 cron_pclose(FILE *iop) 147 { 148 int fdes; 149 pid_t pid; 150 int status; 151 sigset_t sigset, osigset; 152 153 /* 154 * pclose returns -1 if stream is not associated with a 155 * `popened' command, or, if already `pclosed'. 156 */ 157 if (pids == 0 || pids[fdes = fileno(iop)] == 0) 158 return (-1); 159 (void)fclose(iop); 160 sigemptyset(&sigset); 161 sigaddset(&sigset, SIGINT); 162 sigaddset(&sigset, SIGQUIT); 163 sigaddset(&sigset, SIGHUP); 164 sigprocmask(SIG_BLOCK, &sigset, &osigset); 165 while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) 166 continue; 167 sigprocmask(SIG_SETMASK, &osigset, NULL); 168 pids[fdes] = 0; 169 if (pid < 0) 170 return (pid); 171 if (WIFEXITED(status)) 172 return (WEXITSTATUS(status)); 173 return (1); 174 } 175