1 /*	$NetBSD: simple_exec.c,v 1.1.1.1 2011/04/13 18:15:43 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 1998 - 2001, 2004 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <config.h>
37 
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #ifdef HAVE_SYS_WAIT_H
44 #include <sys/wait.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #include <errno.h>
50 
51 #include <krb5/roken.h>
52 
53 #define EX_NOEXEC	126
54 #define EX_NOTFOUND	127
55 
56 /* return values:
57    SE_E_UNSPECIFIED   on `unspecified' system errors
58    SE_E_FORKFAILED    on fork failures
59    SE_E_WAITPIDFAILED on waitpid errors
60    SE_E_EXECTIMEOUT   exec timeout
61    0-   is return value from subprocess
62    SE_E_NOEXEC        if the program couldn't be executed
63    SE_E_NOTFOUND      if the program couldn't be found
64    128- is 128 + signal that killed subprocess
65 
66    possible values `func' can return:
67    ((time_t)-2)		exit loop w/o killing child and return
68    			`exec timeout'/-4 from simple_exec
69    ((time_t)-1)		kill child with SIGTERM and wait for child to exit
70    0			don't timeout again
71    n			seconds to next timeout
72    */
73 
74 static int sig_alarm;
75 
76 static RETSIGTYPE
77 sigtimeout(int sig)
78 {
79     sig_alarm = 1;
80     SIGRETURN(0);
81 }
82 
83 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
84 wait_for_process_timed(pid_t pid, time_t (*func)(void *),
85 		       void *ptr, time_t timeout)
86 {
87     RETSIGTYPE (*old_func)(int sig) = NULL;
88     unsigned int oldtime = 0;
89     int ret;
90 
91     sig_alarm = 0;
92 
93     if (func) {
94 	old_func = signal(SIGALRM, sigtimeout);
95 	oldtime = alarm(timeout);
96     }
97 
98     while(1) {
99 	int status;
100 
101 	while(waitpid(pid, &status, 0) < 0) {
102 	    if (errno != EINTR) {
103 		ret = SE_E_WAITPIDFAILED;
104 		goto out;
105 	    }
106 	    if (func == NULL)
107 		continue;
108 	    if (sig_alarm == 0)
109 		continue;
110 	    timeout = (*func)(ptr);
111 	    if (timeout == (time_t)-1) {
112 		kill(pid, SIGTERM);
113 		continue;
114 	    } else if (timeout == (time_t)-2) {
115 		ret = SE_E_EXECTIMEOUT;
116 		goto out;
117 	    }
118 	    alarm(timeout);
119 	}
120 	if(WIFSTOPPED(status))
121 	    continue;
122 	if(WIFEXITED(status)) {
123 	    ret = WEXITSTATUS(status);
124 	    break;
125 	}
126 	if(WIFSIGNALED(status)) {
127 	    ret = WTERMSIG(status) + 128;
128 	    break;
129 	}
130     }
131  out:
132     if (func) {
133 	signal(SIGALRM, old_func);
134 	alarm(oldtime);
135     }
136     return ret;
137 }
138 
139 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
140 wait_for_process(pid_t pid)
141 {
142     return wait_for_process_timed(pid, NULL, NULL, 0);
143 }
144 
145 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
146 pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
147 	   const char *file, ...)
148 {
149     int in_fd[2], out_fd[2], err_fd[2];
150     pid_t pid;
151     va_list ap;
152     char **argv;
153 
154     if(stdin_fd != NULL)
155 	pipe(in_fd);
156     if(stdout_fd != NULL)
157 	pipe(out_fd);
158     if(stderr_fd != NULL)
159 	pipe(err_fd);
160     pid = fork();
161     switch(pid) {
162     case 0:
163 	va_start(ap, file);
164 	argv = vstrcollect(&ap);
165 	va_end(ap);
166 	if(argv == NULL)
167 	    exit(-1);
168 
169 	/* close pipes we're not interested in */
170 	if(stdin_fd != NULL)
171 	    close(in_fd[1]);
172 	if(stdout_fd != NULL)
173 	    close(out_fd[0]);
174 	if(stderr_fd != NULL)
175 	    close(err_fd[0]);
176 
177 	/* pipe everything caller doesn't care about to /dev/null */
178 	if(stdin_fd == NULL)
179 	    in_fd[0] = open(_PATH_DEVNULL, O_RDONLY);
180 	if(stdout_fd == NULL)
181 	    out_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
182 	if(stderr_fd == NULL)
183 	    err_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
184 
185 	/* move to proper descriptors */
186 	if(in_fd[0] != STDIN_FILENO) {
187 	    dup2(in_fd[0], STDIN_FILENO);
188 	    close(in_fd[0]);
189 	}
190 	if(out_fd[1] != STDOUT_FILENO) {
191 	    dup2(out_fd[1], STDOUT_FILENO);
192 	    close(out_fd[1]);
193 	}
194 	if(err_fd[1] != STDERR_FILENO) {
195 	    dup2(err_fd[1], STDERR_FILENO);
196 	    close(err_fd[1]);
197 	}
198 
199 	closefrom(3);
200 
201 	execv(file, argv);
202 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
203     case -1:
204 	if(stdin_fd != NULL) {
205 	    close(in_fd[0]);
206 	    close(in_fd[1]);
207 	}
208 	if(stdout_fd != NULL) {
209 	    close(out_fd[0]);
210 	    close(out_fd[1]);
211 	}
212 	if(stderr_fd != NULL) {
213 	    close(err_fd[0]);
214 	    close(err_fd[1]);
215 	}
216 	return SE_E_FORKFAILED;
217     default:
218 	if(stdin_fd != NULL) {
219 	    close(in_fd[0]);
220 	    *stdin_fd = fdopen(in_fd[1], "w");
221 	}
222 	if(stdout_fd != NULL) {
223 	    close(out_fd[1]);
224 	    *stdout_fd = fdopen(out_fd[0], "r");
225 	}
226 	if(stderr_fd != NULL) {
227 	    close(err_fd[1]);
228 	    *stderr_fd = fdopen(err_fd[0], "r");
229 	}
230     }
231     return pid;
232 }
233 
234 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
235 simple_execvp_timed(const char *file, char *const args[],
236 		    time_t (*func)(void *), void *ptr, time_t timeout)
237 {
238     pid_t pid = fork();
239     switch(pid){
240     case -1:
241 	return SE_E_FORKFAILED;
242     case 0:
243 	execvp(file, args);
244 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
245     default:
246 	return wait_for_process_timed(pid, func, ptr, timeout);
247     }
248 }
249 
250 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
251 simple_execvp(const char *file, char *const args[])
252 {
253     return simple_execvp_timed(file, args, NULL, NULL, 0);
254 }
255 
256 /* gee, I'd like a execvpe */
257 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
258 simple_execve_timed(const char *file, char *const args[], char *const envp[],
259 		    time_t (*func)(void *), void *ptr, time_t timeout)
260 {
261     pid_t pid = fork();
262     switch(pid){
263     case -1:
264 	return SE_E_FORKFAILED;
265     case 0:
266 	execve(file, args, envp);
267 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
268     default:
269 	return wait_for_process_timed(pid, func, ptr, timeout);
270     }
271 }
272 
273 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
274 simple_execve(const char *file, char *const args[], char *const envp[])
275 {
276     return simple_execve_timed(file, args, envp, NULL, NULL, 0);
277 }
278 
279 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
280 simple_execlp(const char *file, ...)
281 {
282     va_list ap;
283     char **argv;
284     int ret;
285 
286     va_start(ap, file);
287     argv = vstrcollect(&ap);
288     va_end(ap);
289     if(argv == NULL)
290 	return SE_E_UNSPECIFIED;
291     ret = simple_execvp(file, argv);
292     free(argv);
293     return ret;
294 }
295 
296 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
297 simple_execle(const char *file, ... /* ,char *const envp[] */)
298 {
299     va_list ap;
300     char **argv;
301     char *const* envp;
302     int ret;
303 
304     va_start(ap, file);
305     argv = vstrcollect(&ap);
306     envp = va_arg(ap, char **);
307     va_end(ap);
308     if(argv == NULL)
309 	return SE_E_UNSPECIFIED;
310     ret = simple_execve(file, argv, envp);
311     free(argv);
312     return ret;
313 }
314