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