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