popen.c revision 62376
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 62376 2000-07-02 04:15:15Z 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 if (lc != NULL) 186 login_close(lc); 187#endif 188 chdir(env_get("HOME", e->envp)); 189 } 190#if WANT_GLOBBING 191 execvp(gargv[0], gargv); 192#else 193 execvp(argv[0], argv); 194#endif 195 _exit(1); 196 } 197 /* parent; assume fdopen can't fail... */ 198 if (*type == 'r') { 199 iop = fdopen(pdes[0], type); 200 (void)close(pdes[1]); 201 } else { 202 iop = fdopen(pdes[1], type); 203 (void)close(pdes[0]); 204 } 205 pids[fileno(iop)] = pid; 206 207pfree: 208#if WANT_GLOBBING 209 for (argc = 1; argv[argc] != NULL; argc++) { 210/* blkfree((char **)argv[argc]); */ 211 free((char *)argv[argc]); 212 } 213#endif 214 return(iop); 215} 216 217int 218cron_pclose(iop) 219 FILE *iop; 220{ 221 register int fdes; 222 int omask; 223 WAIT_T stat_loc; 224 PID_T pid; 225 226 /* 227 * pclose returns -1 if stream is not associated with a 228 * `popened' command, or, if already `pclosed'. 229 */ 230 if (pids == 0 || pids[fdes = fileno(iop)] == 0) 231 return(-1); 232 (void)fclose(iop); 233 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 234 while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) 235 ; 236 (void)sigsetmask(omask); 237 pids[fdes] = 0; 238 return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); 239} 240