1 /* 2 * Copyright (c) 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software written by Ken Arnold and 6 * published in UNIX Review, Vol. 6, No. 8. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the University of California, Berkeley. The name of the 14 * University may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * @(#)popen.c 5.7 (Berkeley) 2/14/89 21 * $FreeBSD: src/usr.sbin/cron/cron/popen.c,v 1.7.2.3 2000/12/11 01:03:31 obrien Exp $ 22 */ 23 24 /* this came out of the ftpd sources; it's been modified to avoid the 25 * globbing stuff since we don't need it. also execvp instead of execv. 26 */ 27 28 #include "cron.h" 29 #include <sys/signal.h> 30 #include <fcntl.h> 31 #include <paths.h> 32 #if defined(SYSLOG) 33 # include <syslog.h> 34 #endif 35 #if defined(LOGIN_CAP) 36 # include <login_cap.h> 37 #endif 38 39 40 #define MAX_ARGS 100 41 #define WANT_GLOBBING 0 42 43 /* 44 * Special version of popen which avoids call to shell. This insures noone 45 * may create a pipe to a hidden program as a side effect of a list or dir 46 * command. 47 */ 48 static PID_T *pids; 49 static int fds; 50 51 FILE * 52 cron_popen(char *program, char *type, entry *e) 53 { 54 char *cp; 55 FILE *iop; 56 int argc, pdes[2]; 57 PID_T pid; 58 char *usernm; 59 char *argv[MAX_ARGS + 1]; 60 # if defined(LOGIN_CAP) 61 struct passwd *pwd; 62 login_cap_t *lc; 63 # endif 64 #if WANT_GLOBBING 65 char **pop, *vv[2]; 66 int gargc; 67 char *gargv[1000]; 68 extern char **glob(), **copyblk(); 69 #endif 70 71 if ((*type != 'r' && *type != 'w') || type[1]) 72 return(NULL); 73 74 if (!pids) { 75 if ((fds = getdtablesize()) <= 0) 76 return(NULL); 77 if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T))))) 78 return(NULL); 79 bzero((char *)pids, fds * sizeof(PID_T)); 80 } 81 if (pipe(pdes) < 0) 82 return(NULL); 83 84 /* break up string into pieces */ 85 for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL) 86 if (!(argv[argc++] = strtok(cp, " \t\n"))) 87 break; 88 argv[MAX_ARGS] = NULL; 89 90 #if WANT_GLOBBING 91 /* glob each piece */ 92 gargv[0] = argv[0]; 93 for (gargc = argc = 1; argv[argc]; argc++) { 94 if (!(pop = glob(argv[argc]))) { /* globbing failed */ 95 vv[0] = argv[argc]; 96 vv[1] = NULL; 97 pop = copyblk(vv); 98 } 99 argv[argc] = (char *)pop; /* save to free later */ 100 while (*pop && gargc < 1000) 101 gargv[gargc++] = *pop++; 102 } 103 gargv[gargc] = NULL; 104 #endif 105 106 iop = NULL; 107 switch(pid = vfork()) { 108 case -1: /* error */ 109 close(pdes[0]); 110 close(pdes[1]); 111 goto pfree; 112 /* NOTREACHED */ 113 case 0: /* child */ 114 if (e != NULL) { 115 #ifdef SYSLOG 116 closelog(); 117 #endif 118 119 /* get new pgrp, void tty, etc. 120 */ 121 setsid(); 122 } 123 if (*type == 'r') { 124 /* Do not share our parent's stdin */ 125 close(0); 126 open(_PATH_DEVNULL, O_RDWR); 127 if (pdes[1] != 1) { 128 dup2(pdes[1], 1); 129 dup2(pdes[1], 2); /* stderr, too! */ 130 close(pdes[1]); 131 } 132 close(pdes[0]); 133 } else { 134 if (pdes[0] != 0) { 135 dup2(pdes[0], 0); 136 close(pdes[0]); 137 } 138 /* Hack: stdout gets revoked */ 139 close(1); 140 open(_PATH_DEVNULL, O_RDWR); 141 close(2); 142 open(_PATH_DEVNULL, O_RDWR); 143 close(pdes[1]); 144 } 145 # if defined(LOGIN_CAP) 146 if (e != NULL) { 147 /* Set user's entire context, but skip the environment 148 * as cron provides a separate interface for this 149 */ 150 usernm = env_get("LOGNAME", e->envp); 151 if ((pwd = getpwnam(usernm)) == NULL) 152 pwd = getpwuid(e->uid); 153 lc = NULL; 154 if (pwd != NULL) { 155 pwd->pw_gid = e->gid; 156 if (e->class != NULL) 157 lc = login_getclass(e->class); 158 } 159 if (pwd && 160 setusercontext(lc, pwd, e->uid, 161 LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0) 162 endpwent(); 163 else { 164 /* fall back to the old method */ 165 endpwent(); 166 # endif 167 /* set our directory, uid and gid. Set gid first, 168 * since once we set uid, we've lost root privledges. 169 */ 170 setgid(e->gid); 171 # if defined(BSD) 172 initgroups(usernm, e->gid); 173 # endif 174 setlogin(usernm); 175 setuid(e->uid); /* we aren't root after this..*/ 176 #if defined(LOGIN_CAP) 177 } 178 if (lc != NULL) 179 login_close(lc); 180 #endif 181 chdir(env_get("HOME", e->envp)); 182 } 183 #if WANT_GLOBBING 184 execvp(gargv[0], gargv); 185 #else 186 execvp(argv[0], argv); 187 #endif 188 _exit(1); 189 } 190 /* parent; assume fdopen can't fail... */ 191 if (*type == 'r') { 192 iop = fdopen(pdes[0], type); 193 close(pdes[1]); 194 } else { 195 iop = fdopen(pdes[1], type); 196 close(pdes[0]); 197 } 198 pids[fileno(iop)] = pid; 199 200 pfree: 201 #if WANT_GLOBBING 202 for (argc = 1; argv[argc] != NULL; argc++) { 203 /* blkfree((char **)argv[argc]); */ 204 free((char *)argv[argc]); 205 } 206 #endif 207 return(iop); 208 } 209 210 int 211 cron_pclose(FILE *iop) 212 { 213 int fdes; 214 int omask; 215 WAIT_T stat_loc; 216 PID_T pid; 217 218 /* 219 * pclose returns -1 if stream is not associated with a 220 * `popened' command, or, if already `pclosed'. 221 */ 222 if (pids == NULL || pids[fdes = fileno(iop)] == 0) 223 return(-1); 224 fclose(iop); 225 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 226 while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) 227 ; 228 sigsetmask(omask); 229 pids[fdes] = 0; 230 return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); 231 } 232