popen.c revision 90276
12311Sjkh/* 22311Sjkh * Copyright (c) 1988 The Regents of the University of California. 32311Sjkh * All rights reserved. 42311Sjkh * 52311Sjkh * This code is derived from software written by Ken Arnold and 62311Sjkh * published in UNIX Review, Vol. 6, No. 8. 72311Sjkh * 82311Sjkh * Redistribution and use in source and binary forms are permitted 92311Sjkh * provided that the above copyright notice and this paragraph are 102311Sjkh * duplicated in all such forms and that any documentation, 112311Sjkh * advertising materials, and other materials related to such 122311Sjkh * distribution and use acknowledge that the software was developed 132311Sjkh * by the University of California, Berkeley. The name of the 142311Sjkh * University may not be used to endorse or promote products derived 152311Sjkh * from this software without specific prior written permission. 162311Sjkh * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 172311Sjkh * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 182311Sjkh * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 192311Sjkh * 202311Sjkh */ 212311Sjkh 222311Sjkh/* this came out of the ftpd sources; it's been modified to avoid the 232311Sjkh * globbing stuff since we don't need it. also execvp instead of execv. 242311Sjkh */ 252311Sjkh 262311Sjkh#ifndef lint 2729452Scharnier#if 0 282311Sjkhstatic char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89"; 2929452Scharnier#endif 3029452Scharnierstatic const char rcsid[] = 3150479Speter "$FreeBSD: head/usr.sbin/cron/cron/popen.c 90276 2002-02-06 02:00:07Z bbraun $"; 322311Sjkh#endif /* not lint */ 332311Sjkh 342311Sjkh#include "cron.h" 352311Sjkh#include <sys/signal.h> 3645369Speter#include <fcntl.h> 3769793Sobrien#include <paths.h> 3862367Sache#if defined(SYSLOG) 3962367Sache# include <syslog.h> 4062367Sache#endif 4162367Sache#if defined(LOGIN_CAP) 4262367Sache# include <login_cap.h> 4362367Sache#endif 442311Sjkh 452311Sjkh 4620573Spst#define MAX_ARGS 100 472311Sjkh#define WANT_GLOBBING 0 482311Sjkh 492311Sjkh/* 502311Sjkh * Special version of popen which avoids call to shell. This insures noone 512311Sjkh * may create a pipe to a hidden program as a side effect of a list or dir 522311Sjkh * command. 532311Sjkh */ 542311Sjkhstatic PID_T *pids; 552311Sjkhstatic int fds; 562311Sjkh 572311SjkhFILE * 5862367Sachecron_popen(program, type, e) 592311Sjkh char *program, *type; 6062367Sache entry *e; 612311Sjkh{ 622311Sjkh register char *cp; 632311Sjkh FILE *iop; 642311Sjkh int argc, pdes[2]; 652311Sjkh PID_T pid; 6662367Sache char *usernm; 6720573Spst char *argv[MAX_ARGS + 1]; 6862367Sache# if defined(LOGIN_CAP) 6962367Sache struct passwd *pwd; 7062367Sache login_cap_t *lc; 7162367Sache# endif 722311Sjkh#if WANT_GLOBBING 732311Sjkh char **pop, *vv[2]; 742311Sjkh int gargc; 752311Sjkh char *gargv[1000]; 762311Sjkh extern char **glob(), **copyblk(); 772311Sjkh#endif 782311Sjkh 7929452Scharnier if ((*type != 'r' && *type != 'w') || type[1]) 802311Sjkh return(NULL); 812311Sjkh 822311Sjkh if (!pids) { 832311Sjkh if ((fds = getdtablesize()) <= 0) 842311Sjkh return(NULL); 852311Sjkh if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T))))) 862311Sjkh return(NULL); 872311Sjkh bzero((char *)pids, fds * sizeof(PID_T)); 882311Sjkh } 892311Sjkh if (pipe(pdes) < 0) 902311Sjkh return(NULL); 912311Sjkh 922311Sjkh /* break up string into pieces */ 9320573Spst for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL) 942311Sjkh if (!(argv[argc++] = strtok(cp, " \t\n"))) 952311Sjkh break; 9662365Sache argv[MAX_ARGS] = NULL; 972311Sjkh 982311Sjkh#if WANT_GLOBBING 992311Sjkh /* glob each piece */ 1002311Sjkh gargv[0] = argv[0]; 1012311Sjkh for (gargc = argc = 1; argv[argc]; argc++) { 1022311Sjkh if (!(pop = glob(argv[argc]))) { /* globbing failed */ 1032311Sjkh vv[0] = argv[argc]; 1042311Sjkh vv[1] = NULL; 1052311Sjkh pop = copyblk(vv); 1062311Sjkh } 1072311Sjkh argv[argc] = (char *)pop; /* save to free later */ 1082311Sjkh while (*pop && gargc < 1000) 1092311Sjkh gargv[gargc++] = *pop++; 1102311Sjkh } 1112311Sjkh gargv[gargc] = NULL; 1122311Sjkh#endif 1132311Sjkh 1142311Sjkh iop = NULL; 1152311Sjkh switch(pid = vfork()) { 1162311Sjkh case -1: /* error */ 1172311Sjkh (void)close(pdes[0]); 1182311Sjkh (void)close(pdes[1]); 1192311Sjkh goto pfree; 1202311Sjkh /* NOTREACHED */ 1212311Sjkh case 0: /* child */ 12262367Sache if (e != NULL) { 12362367Sache#ifdef SYSLOG 12462367Sache closelog(); 12562367Sache#endif 12662367Sache 12762367Sache /* get new pgrp, void tty, etc. 12862367Sache */ 12962367Sache (void) setsid(); 13062367Sache } 1312311Sjkh if (*type == 'r') { 13245369Speter /* Do not share our parent's stdin */ 13345369Speter (void)close(0); 13469793Sobrien (void)open(_PATH_DEVNULL, O_RDWR); 1352311Sjkh if (pdes[1] != 1) { 1362311Sjkh dup2(pdes[1], 1); 1372311Sjkh dup2(pdes[1], 2); /* stderr, too! */ 1382311Sjkh (void)close(pdes[1]); 1392311Sjkh } 1402311Sjkh (void)close(pdes[0]); 1412311Sjkh } else { 1422311Sjkh if (pdes[0] != 0) { 1432311Sjkh dup2(pdes[0], 0); 1442311Sjkh (void)close(pdes[0]); 1452311Sjkh } 14645369Speter /* Hack: stdout gets revoked */ 14745369Speter (void)close(1); 14869793Sobrien (void)open(_PATH_DEVNULL, O_RDWR); 14945369Speter (void)close(2); 15069793Sobrien (void)open(_PATH_DEVNULL, O_RDWR); 1512311Sjkh (void)close(pdes[1]); 1522311Sjkh } 15362367Sache if (e != NULL) { 15462367Sache /* Set user's entire context, but skip the environment 15562367Sache * as cron provides a separate interface for this 15662367Sache */ 15762367Sache usernm = env_get("LOGNAME", e->envp); 15890276Sbbraun# if defined(LOGIN_CAP) 15962367Sache if ((pwd = getpwnam(usernm)) == NULL) 16062367Sache pwd = getpwuid(e->uid); 16162367Sache lc = NULL; 16262367Sache if (pwd != NULL) { 16362367Sache pwd->pw_gid = e->gid; 16462367Sache if (e->class != NULL) 16562367Sache lc = login_getclass(e->class); 16662367Sache } 16762367Sache if (pwd && 16862367Sache setusercontext(lc, pwd, e->uid, 16962367Sache LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0) 17062367Sache (void) endpwent(); 17162367Sache else { 17262367Sache /* fall back to the old method */ 17362367Sache (void) endpwent(); 17462367Sache# endif 17562367Sache /* set our directory, uid and gid. Set gid first, 17662367Sache * since once we set uid, we've lost root privledges. 17762367Sache */ 17862367Sache setgid(e->gid); 17962367Sache# if defined(BSD) 18062367Sache initgroups(usernm, e->gid); 18162367Sache# endif 18262367Sache setlogin(usernm); 18362367Sache setuid(e->uid); /* we aren't root after this..*/ 18462367Sache#if defined(LOGIN_CAP) 18562367Sache } 18662376Sache if (lc != NULL) 18762376Sache login_close(lc); 18862367Sache#endif 18962367Sache chdir(env_get("HOME", e->envp)); 19062367Sache } 1912311Sjkh#if WANT_GLOBBING 1922311Sjkh execvp(gargv[0], gargv); 1932311Sjkh#else 1942311Sjkh execvp(argv[0], argv); 1952311Sjkh#endif 1962311Sjkh _exit(1); 1972311Sjkh } 1982311Sjkh /* parent; assume fdopen can't fail... */ 1992311Sjkh if (*type == 'r') { 2002311Sjkh iop = fdopen(pdes[0], type); 2012311Sjkh (void)close(pdes[1]); 2022311Sjkh } else { 2032311Sjkh iop = fdopen(pdes[1], type); 2042311Sjkh (void)close(pdes[0]); 2052311Sjkh } 2062311Sjkh pids[fileno(iop)] = pid; 2072311Sjkh 2082311Sjkhpfree: 2092311Sjkh#if WANT_GLOBBING 2102311Sjkh for (argc = 1; argv[argc] != NULL; argc++) { 2112311Sjkh/* blkfree((char **)argv[argc]); */ 2122311Sjkh free((char *)argv[argc]); 2132311Sjkh } 2142311Sjkh#endif 2152311Sjkh return(iop); 2162311Sjkh} 2172311Sjkh 2182311Sjkhint 2192311Sjkhcron_pclose(iop) 2202311Sjkh FILE *iop; 2212311Sjkh{ 2222311Sjkh register int fdes; 2232311Sjkh int omask; 2242311Sjkh WAIT_T stat_loc; 2252311Sjkh PID_T pid; 2262311Sjkh 2272311Sjkh /* 2282311Sjkh * pclose returns -1 if stream is not associated with a 2292311Sjkh * `popened' command, or, if already `pclosed'. 2302311Sjkh */ 2312311Sjkh if (pids == 0 || pids[fdes = fileno(iop)] == 0) 2322311Sjkh return(-1); 2332311Sjkh (void)fclose(iop); 2342311Sjkh omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 2352311Sjkh while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) 2362311Sjkh ; 2372311Sjkh (void)sigsetmask(omask); 2382311Sjkh pids[fdes] = 0; 2392311Sjkh return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); 2402311Sjkh} 241