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.4 2004/03/10 18:27:26 dillon 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: (void)close(pdes[0]);
111: (void)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: (void) setsid();
123: }
124: if (*type == 'r') {
125: /* Do not share our parent's stdin */
126: (void)close(0);
127: (void)open(_PATH_DEVNULL, O_RDWR);
128: if (pdes[1] != 1) {
129: dup2(pdes[1], 1);
130: dup2(pdes[1], 2); /* stderr, too! */
131: (void)close(pdes[1]);
132: }
133: (void)close(pdes[0]);
134: } else {
135: if (pdes[0] != 0) {
136: dup2(pdes[0], 0);
137: (void)close(pdes[0]);
138: }
139: /* Hack: stdout gets revoked */
140: (void)close(1);
141: (void)open(_PATH_DEVNULL, O_RDWR);
142: (void)close(2);
143: (void)open(_PATH_DEVNULL, O_RDWR);
144: (void)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: (void) endpwent();
164: else {
165: /* fall back to the old method */
166: (void) 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: (void)close(pdes[1]);
195: } else {
196: iop = fdopen(pdes[1], type);
197: (void)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: (void)fclose(iop);
226: omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
227: while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
228: ;
229: (void)sigsetmask(omask);
230: pids[fdes] = 0;
231: return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
232: }