popen.c revision 293132
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: stable/10/usr.sbin/cron/cron/popen.c 293132 2016-01-04 03:20:41Z pfg $"; 32#endif /* not lint */ 33 34#include "cron.h" 35#include <sys/signal.h> 36#include <fcntl.h> 37#include <paths.h> 38#if defined(SYSLOG) 39# include <syslog.h> 40#endif 41#if defined(LOGIN_CAP) 42# include <login_cap.h> 43#endif 44 45 46#define MAX_ARGS 100 47#define WANT_GLOBBING 0 48 49/* 50 * Special version of popen which avoids call to shell. This insures noone 51 * may create a pipe to a hidden program as a side effect of a list or dir 52 * command. 53 */ 54static PID_T *pids; 55static int fds; 56 57FILE * 58cron_popen(program, type, e) 59 char *program, *type; 60 entry *e; 61{ 62 register char *cp; 63 FILE *iop; 64 int argc, pdes[2]; 65 PID_T pid; 66 char *usernm; 67 char *argv[MAX_ARGS + 1]; 68# if defined(LOGIN_CAP) 69 struct passwd *pwd; 70 login_cap_t *lc; 71# endif 72#if WANT_GLOBBING 73 char **pop, *vv[2]; 74 int gargc; 75 char *gargv[1000]; 76 extern char **glob(), **copyblk(); 77#endif 78 79 if ((*type != 'r' && *type != 'w') || type[1]) 80 return(NULL); 81 82 if (!pids) { 83 if ((fds = getdtablesize()) <= 0) 84 return(NULL); 85 if (!(pids = calloc(fds, sizeof(PID_T)))) 86 return(NULL); 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(_PATH_DEVNULL, 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(_PATH_DEVNULL, O_RDWR); 148 (void)close(2); 149 (void)open(_PATH_DEVNULL, O_RDWR); 150 (void)close(pdes[1]); 151 } 152 if (e != NULL) { 153 /* Set user's entire context, but skip the environment 154 * as cron provides a separate interface for this 155 */ 156 usernm = env_get("LOGNAME", e->envp); 157# if defined(LOGIN_CAP) 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 /* 175 * Set our directory, uid and gid. Set gid 176 * first since once we set uid, we've lost 177 * root privileges. 178 */ 179 if (setgid(e->gid) != 0) 180 _exit(ERROR_EXIT); 181# if defined(BSD) 182 if (initgroups(usernm, e->gid) != 0) 183 _exit(ERROR_EXIT); 184# endif 185 if (setlogin(usernm) != 0) 186 _exit(ERROR_EXIT); 187 if (setuid(e->uid) != 0) 188 _exit(ERROR_EXIT); 189 /* we aren't root after this..*/ 190#if defined(LOGIN_CAP) 191 } 192 if (lc != NULL) 193 login_close(lc); 194#endif 195 chdir(env_get("HOME", e->envp)); 196 } 197#if WANT_GLOBBING 198 execvp(gargv[0], gargv); 199#else 200 execvp(argv[0], argv); 201#endif 202 _exit(1); 203 } 204 /* parent; assume fdopen can't fail... */ 205 if (*type == 'r') { 206 iop = fdopen(pdes[0], type); 207 (void)close(pdes[1]); 208 } else { 209 iop = fdopen(pdes[1], type); 210 (void)close(pdes[0]); 211 } 212 pids[fileno(iop)] = pid; 213 214pfree: 215#if WANT_GLOBBING 216 for (argc = 1; argv[argc] != NULL; argc++) { 217/* blkfree((char **)argv[argc]); */ 218 free((char *)argv[argc]); 219 } 220#endif 221 return(iop); 222} 223 224int 225cron_pclose(iop) 226 FILE *iop; 227{ 228 register int fdes; 229 int omask; 230 WAIT_T stat_loc; 231 PID_T pid; 232 233 /* 234 * pclose returns -1 if stream is not associated with a 235 * `popened' command, or, if already `pclosed'. 236 */ 237 if (pids == 0 || pids[fdes = fileno(iop)] == 0) 238 return(-1); 239 (void)fclose(iop); 240 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 241 while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) 242 ; 243 (void)sigsetmask(omask); 244 pids[fdes] = 0; 245 return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); 246} 247