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