popen.c revision 1.2 1 1.2 christos /* $NetBSD: popen.c,v 1.2 2010/05/06 18:53:17 christos Exp $ */
2 1.2 christos
3 1.1 christos /*
4 1.1 christos * Copyright (c) 1988, 1993, 1994
5 1.1 christos * The Regents of the University of California. All rights reserved.
6 1.1 christos *
7 1.1 christos * This code is derived from software written by Ken Arnold and
8 1.1 christos * published in UNIX Review, Vol. 6, No. 8.
9 1.1 christos *
10 1.1 christos * Redistribution and use in source and binary forms, with or without
11 1.1 christos * modification, are permitted provided that the following conditions
12 1.1 christos * are met:
13 1.1 christos * 1. Redistributions of source code must retain the above copyright
14 1.1 christos * notice, this list of conditions and the following disclaimer.
15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 christos * notice, this list of conditions and the following disclaimer in the
17 1.1 christos * documentation and/or other materials provided with the distribution.
18 1.1 christos * 3. All advertising materials mentioning features or use of this software
19 1.1 christos * must display the following acknowledgement:
20 1.1 christos * This product includes software developed by the University of
21 1.1 christos * California, Berkeley and its contributors.
22 1.1 christos *
23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.1 christos * SUCH DAMAGE.
34 1.1 christos *
35 1.1 christos */
36 1.1 christos
37 1.1 christos /* this came out of the ftpd sources; it's been modified to avoid the
38 1.1 christos * globbing stuff since we don't need it. also execvp instead of execv.
39 1.1 christos */
40 1.1 christos
41 1.2 christos #include <sys/cdefs.h>
42 1.1 christos #ifndef lint
43 1.1 christos #if 0
44 1.1 christos static sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
45 1.2 christos static char rcsid[] = "Id: popen.c,v 1.6 2003/02/16 04:40:01 vixie Exp";
46 1.1 christos #else
47 1.2 christos __RCSID("$NetBSD: popen.c,v 1.2 2010/05/06 18:53:17 christos Exp $");
48 1.1 christos #endif
49 1.1 christos #endif /* not lint */
50 1.1 christos
51 1.1 christos #include "cron.h"
52 1.1 christos
53 1.1 christos #define MAX_ARGV 100
54 1.1 christos #define MAX_GARGV 1000
55 1.1 christos
56 1.1 christos /*
57 1.1 christos * Special version of popen which avoids call to shell. This ensures noone
58 1.1 christos * may create a pipe to a hidden program as a side effect of a list or dir
59 1.1 christos * command.
60 1.1 christos */
61 1.1 christos static PID_T *pids;
62 1.2 christos static long fds;
63 1.1 christos
64 1.1 christos FILE *
65 1.2 christos cron_popen(char *program, const char *type, struct passwd *pw) {
66 1.1 christos char *cp;
67 1.1 christos FILE *iop;
68 1.1 christos int argc, pdes[2];
69 1.1 christos PID_T pid;
70 1.1 christos char *argv[MAX_ARGV];
71 1.1 christos
72 1.1 christos if ((*type != 'r' && *type != 'w') || type[1] != '\0')
73 1.1 christos return (NULL);
74 1.1 christos
75 1.1 christos if (!pids) {
76 1.2 christos size_t len;
77 1.1 christos if ((fds = sysconf(_SC_OPEN_MAX)) <= 0)
78 1.1 christos return (NULL);
79 1.2 christos len = fds * sizeof(*pids);
80 1.2 christos if ((pids = malloc(len)) == NULL)
81 1.1 christos return (NULL);
82 1.2 christos (void)memset(pids, 0, len);
83 1.1 christos }
84 1.1 christos if (pipe(pdes) < 0)
85 1.1 christos return (NULL);
86 1.1 christos
87 1.1 christos /* break up string into pieces */
88 1.1 christos for (argc = 0, cp = program; argc < MAX_ARGV - 1; cp = NULL)
89 1.1 christos if (!(argv[argc++] = strtok(cp, " \t\n")))
90 1.1 christos break;
91 1.1 christos argv[MAX_ARGV-1] = NULL;
92 1.1 christos
93 1.1 christos switch (pid = vfork()) {
94 1.1 christos case -1: /* error */
95 1.1 christos (void)close(pdes[0]);
96 1.1 christos (void)close(pdes[1]);
97 1.1 christos return (NULL);
98 1.1 christos /* NOTREACHED */
99 1.1 christos case 0: /* child */
100 1.1 christos if (pw) {
101 1.1 christos #ifdef LOGIN_CAP
102 1.1 christos if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) < 0) {
103 1.2 christos warnx("setusercontext failed for %s",
104 1.1 christos pw->pw_name);
105 1.1 christos _exit(ERROR_EXIT);
106 1.1 christos }
107 1.1 christos #else
108 1.1 christos if (setgid(pw->pw_gid) < 0 ||
109 1.1 christos initgroups(pw->pw_name, pw->pw_gid) < 0) {
110 1.2 christos warnx("unable to set groups for %s",
111 1.1 christos pw->pw_name);
112 1.1 christos _exit(1);
113 1.1 christos }
114 1.1 christos #if (defined(BSD)) && (BSD >= 199103)
115 1.1 christos setlogin(pw->pw_name);
116 1.1 christos #endif /* BSD */
117 1.1 christos if (setuid(pw->pw_uid)) {
118 1.2 christos warnx("unable to set uid for %s",
119 1.1 christos pw->pw_name);
120 1.1 christos _exit(1);
121 1.1 christos }
122 1.1 christos #endif /* LOGIN_CAP */
123 1.1 christos }
124 1.1 christos if (*type == 'r') {
125 1.1 christos if (pdes[1] != STDOUT) {
126 1.2 christos (void)dup2(pdes[1], STDOUT);
127 1.1 christos (void)close(pdes[1]);
128 1.1 christos }
129 1.2 christos (void)dup2(STDOUT, STDERR); /* stderr too! */
130 1.1 christos (void)close(pdes[0]);
131 1.1 christos } else {
132 1.1 christos if (pdes[0] != STDIN) {
133 1.2 christos (void)dup2(pdes[0], STDIN);
134 1.1 christos (void)close(pdes[0]);
135 1.1 christos }
136 1.1 christos (void)close(pdes[1]);
137 1.1 christos }
138 1.2 christos (void)execvp(argv[0], argv);
139 1.1 christos _exit(1);
140 1.1 christos }
141 1.1 christos
142 1.1 christos /* parent; assume fdopen can't fail... */
143 1.1 christos if (*type == 'r') {
144 1.1 christos iop = fdopen(pdes[0], type);
145 1.1 christos (void)close(pdes[1]);
146 1.1 christos } else {
147 1.1 christos iop = fdopen(pdes[1], type);
148 1.1 christos (void)close(pdes[0]);
149 1.1 christos }
150 1.1 christos pids[fileno(iop)] = pid;
151 1.1 christos
152 1.1 christos return (iop);
153 1.1 christos }
154 1.1 christos
155 1.1 christos int
156 1.1 christos cron_pclose(FILE *iop) {
157 1.1 christos int fdes;
158 1.1 christos PID_T pid;
159 1.1 christos WAIT_T status;
160 1.2 christos sigset_t sset, osset;
161 1.1 christos
162 1.1 christos /*
163 1.1 christos * pclose returns -1 if stream is not associated with a
164 1.1 christos * `popened' command, or, if already `pclosed'.
165 1.1 christos */
166 1.1 christos if (pids == 0 || pids[fdes = fileno(iop)] == 0)
167 1.1 christos return (-1);
168 1.1 christos (void)fclose(iop);
169 1.2 christos (void)sigemptyset(&sset);
170 1.2 christos (void)sigaddset(&sset, SIGINT);
171 1.2 christos (void)sigaddset(&sset, SIGQUIT);
172 1.2 christos (void)sigaddset(&sset, SIGHUP);
173 1.2 christos (void)sigprocmask(SIG_BLOCK, &sset, &osset);
174 1.1 christos while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
175 1.1 christos continue;
176 1.2 christos (void)sigprocmask(SIG_SETMASK, &osset, NULL);
177 1.1 christos pids[fdes] = 0;
178 1.1 christos if (pid < 0)
179 1.1 christos return (pid);
180 1.1 christos if (WIFEXITED(status))
181 1.1 christos return (WEXITSTATUS(status));
182 1.2 christos else
183 1.2 christos return WTERMSIG(status);
184 1.1 christos }
185