xref: /freebsd/crypto/heimdal/lib/roken/simple_exec.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*
2*ae771770SStanislav Sedov  * Copyright (c) 1998 - 2001, 2004 Kungliga Tekniska Högskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #include <config.h>
35b528cefcSMark Murray 
36b528cefcSMark Murray #include <stdarg.h>
37b528cefcSMark Murray #include <stdlib.h>
38b528cefcSMark Murray #ifdef HAVE_SYS_TYPES_H
39b528cefcSMark Murray #include <sys/types.h>
40b528cefcSMark Murray #endif
41b528cefcSMark Murray #ifdef HAVE_SYS_WAIT_H
42b528cefcSMark Murray #include <sys/wait.h>
43b528cefcSMark Murray #endif
44b528cefcSMark Murray #ifdef HAVE_UNISTD_H
45b528cefcSMark Murray #include <unistd.h>
46b528cefcSMark Murray #endif
47b528cefcSMark Murray #include <errno.h>
48b528cefcSMark Murray 
49c19800e8SDoug Rabson #include "roken.h"
50b528cefcSMark Murray 
51b528cefcSMark Murray #define EX_NOEXEC	126
52b528cefcSMark Murray #define EX_NOTFOUND	127
53b528cefcSMark Murray 
54b528cefcSMark Murray /* return values:
55*ae771770SStanislav Sedov    SE_E_UNSPECIFIED   on `unspecified' system errors
56*ae771770SStanislav Sedov    SE_E_FORKFAILED    on fork failures
57*ae771770SStanislav Sedov    SE_E_WAITPIDFAILED on waitpid errors
58*ae771770SStanislav Sedov    SE_E_EXECTIMEOUT   exec timeout
59b528cefcSMark Murray    0-   is return value from subprocess
60*ae771770SStanislav Sedov    SE_E_NOEXEC        if the program couldn't be executed
61*ae771770SStanislav Sedov    SE_E_NOTFOUND      if the program couldn't be found
62b528cefcSMark Murray    128- is 128 + signal that killed subprocess
63c19800e8SDoug Rabson 
64c19800e8SDoug Rabson    possible values `func' can return:
65c19800e8SDoug Rabson    ((time_t)-2)		exit loop w/o killing child and return
66c19800e8SDoug Rabson    			`exec timeout'/-4 from simple_exec
67c19800e8SDoug Rabson    ((time_t)-1)		kill child with SIGTERM and wait for child to exit
68c19800e8SDoug Rabson    0			don't timeout again
69c19800e8SDoug Rabson    n			seconds to next timeout
70b528cefcSMark Murray    */
71b528cefcSMark Murray 
72c19800e8SDoug Rabson static int sig_alarm;
73c19800e8SDoug Rabson 
74c19800e8SDoug Rabson static RETSIGTYPE
sigtimeout(int sig)75c19800e8SDoug Rabson sigtimeout(int sig)
76b528cefcSMark Murray {
77c19800e8SDoug Rabson     sig_alarm = 1;
78c19800e8SDoug Rabson     SIGRETURN(0);
79c19800e8SDoug Rabson }
80c19800e8SDoug Rabson 
81*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
wait_for_process_timed(pid_t pid,time_t (* func)(void *),void * ptr,time_t timeout)82c19800e8SDoug Rabson wait_for_process_timed(pid_t pid, time_t (*func)(void *),
83c19800e8SDoug Rabson 		       void *ptr, time_t timeout)
84c19800e8SDoug Rabson {
85c19800e8SDoug Rabson     RETSIGTYPE (*old_func)(int sig) = NULL;
86c19800e8SDoug Rabson     unsigned int oldtime = 0;
87c19800e8SDoug Rabson     int ret;
88c19800e8SDoug Rabson 
89c19800e8SDoug Rabson     sig_alarm = 0;
90c19800e8SDoug Rabson 
91c19800e8SDoug Rabson     if (func) {
92c19800e8SDoug Rabson 	old_func = signal(SIGALRM, sigtimeout);
93c19800e8SDoug Rabson 	oldtime = alarm(timeout);
94c19800e8SDoug Rabson     }
95c19800e8SDoug Rabson 
96b528cefcSMark Murray     while(1) {
97b528cefcSMark Murray 	int status;
98b528cefcSMark Murray 
99c19800e8SDoug Rabson 	while(waitpid(pid, &status, 0) < 0) {
100c19800e8SDoug Rabson 	    if (errno != EINTR) {
101*ae771770SStanislav Sedov 		ret = SE_E_WAITPIDFAILED;
102c19800e8SDoug Rabson 		goto out;
103c19800e8SDoug Rabson 	    }
104c19800e8SDoug Rabson 	    if (func == NULL)
105c19800e8SDoug Rabson 		continue;
106c19800e8SDoug Rabson 	    if (sig_alarm == 0)
107c19800e8SDoug Rabson 		continue;
108c19800e8SDoug Rabson 	    timeout = (*func)(ptr);
109c19800e8SDoug Rabson 	    if (timeout == (time_t)-1) {
110c19800e8SDoug Rabson 		kill(pid, SIGTERM);
111c19800e8SDoug Rabson 		continue;
112c19800e8SDoug Rabson 	    } else if (timeout == (time_t)-2) {
113*ae771770SStanislav Sedov 		ret = SE_E_EXECTIMEOUT;
114c19800e8SDoug Rabson 		goto out;
115c19800e8SDoug Rabson 	    }
116c19800e8SDoug Rabson 	    alarm(timeout);
117c19800e8SDoug Rabson 	}
118b528cefcSMark Murray 	if(WIFSTOPPED(status))
119b528cefcSMark Murray 	    continue;
120c19800e8SDoug Rabson 	if(WIFEXITED(status)) {
121c19800e8SDoug Rabson 	    ret = WEXITSTATUS(status);
122c19800e8SDoug Rabson 	    break;
123b528cefcSMark Murray 	}
124c19800e8SDoug Rabson 	if(WIFSIGNALED(status)) {
125c19800e8SDoug Rabson 	    ret = WTERMSIG(status) + 128;
126c19800e8SDoug Rabson 	    break;
127c19800e8SDoug Rabson 	}
128c19800e8SDoug Rabson     }
129c19800e8SDoug Rabson  out:
130c19800e8SDoug Rabson     if (func) {
131c19800e8SDoug Rabson 	signal(SIGALRM, old_func);
132c19800e8SDoug Rabson 	alarm(oldtime);
133c19800e8SDoug Rabson     }
134c19800e8SDoug Rabson     return ret;
135b528cefcSMark Murray }
136b528cefcSMark Murray 
137*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
wait_for_process(pid_t pid)138c19800e8SDoug Rabson wait_for_process(pid_t pid)
139c19800e8SDoug Rabson {
140c19800e8SDoug Rabson     return wait_for_process_timed(pid, NULL, NULL, 0);
141c19800e8SDoug Rabson }
142c19800e8SDoug Rabson 
143*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
pipe_execv(FILE ** stdin_fd,FILE ** stdout_fd,FILE ** stderr_fd,const char * file,...)1444137ff4cSJacques Vidrine pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
1454137ff4cSJacques Vidrine 	   const char *file, ...)
1464137ff4cSJacques Vidrine {
1474137ff4cSJacques Vidrine     int in_fd[2], out_fd[2], err_fd[2];
1484137ff4cSJacques Vidrine     pid_t pid;
1494137ff4cSJacques Vidrine     va_list ap;
1504137ff4cSJacques Vidrine     char **argv;
1514137ff4cSJacques Vidrine 
1524137ff4cSJacques Vidrine     if(stdin_fd != NULL)
1534137ff4cSJacques Vidrine 	pipe(in_fd);
1544137ff4cSJacques Vidrine     if(stdout_fd != NULL)
1554137ff4cSJacques Vidrine 	pipe(out_fd);
1564137ff4cSJacques Vidrine     if(stderr_fd != NULL)
1574137ff4cSJacques Vidrine 	pipe(err_fd);
1584137ff4cSJacques Vidrine     pid = fork();
1594137ff4cSJacques Vidrine     switch(pid) {
1604137ff4cSJacques Vidrine     case 0:
1614137ff4cSJacques Vidrine 	va_start(ap, file);
1624137ff4cSJacques Vidrine 	argv = vstrcollect(&ap);
1634137ff4cSJacques Vidrine 	va_end(ap);
1644137ff4cSJacques Vidrine 	if(argv == NULL)
1654137ff4cSJacques Vidrine 	    exit(-1);
1664137ff4cSJacques Vidrine 
1674137ff4cSJacques Vidrine 	/* close pipes we're not interested in */
1684137ff4cSJacques Vidrine 	if(stdin_fd != NULL)
1694137ff4cSJacques Vidrine 	    close(in_fd[1]);
1704137ff4cSJacques Vidrine 	if(stdout_fd != NULL)
1714137ff4cSJacques Vidrine 	    close(out_fd[0]);
1724137ff4cSJacques Vidrine 	if(stderr_fd != NULL)
1734137ff4cSJacques Vidrine 	    close(err_fd[0]);
1744137ff4cSJacques Vidrine 
1754137ff4cSJacques Vidrine 	/* pipe everything caller doesn't care about to /dev/null */
1764137ff4cSJacques Vidrine 	if(stdin_fd == NULL)
1774137ff4cSJacques Vidrine 	    in_fd[0] = open(_PATH_DEVNULL, O_RDONLY);
1784137ff4cSJacques Vidrine 	if(stdout_fd == NULL)
1794137ff4cSJacques Vidrine 	    out_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
1804137ff4cSJacques Vidrine 	if(stderr_fd == NULL)
1814137ff4cSJacques Vidrine 	    err_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
1824137ff4cSJacques Vidrine 
1834137ff4cSJacques Vidrine 	/* move to proper descriptors */
1844137ff4cSJacques Vidrine 	if(in_fd[0] != STDIN_FILENO) {
1854137ff4cSJacques Vidrine 	    dup2(in_fd[0], STDIN_FILENO);
1864137ff4cSJacques Vidrine 	    close(in_fd[0]);
1874137ff4cSJacques Vidrine 	}
1884137ff4cSJacques Vidrine 	if(out_fd[1] != STDOUT_FILENO) {
1894137ff4cSJacques Vidrine 	    dup2(out_fd[1], STDOUT_FILENO);
1904137ff4cSJacques Vidrine 	    close(out_fd[1]);
1914137ff4cSJacques Vidrine 	}
1924137ff4cSJacques Vidrine 	if(err_fd[1] != STDERR_FILENO) {
1934137ff4cSJacques Vidrine 	    dup2(err_fd[1], STDERR_FILENO);
1944137ff4cSJacques Vidrine 	    close(err_fd[1]);
1954137ff4cSJacques Vidrine 	}
1964137ff4cSJacques Vidrine 
197c19800e8SDoug Rabson 	closefrom(3);
198c19800e8SDoug Rabson 
1994137ff4cSJacques Vidrine 	execv(file, argv);
2004137ff4cSJacques Vidrine 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
2014137ff4cSJacques Vidrine     case -1:
2024137ff4cSJacques Vidrine 	if(stdin_fd != NULL) {
2034137ff4cSJacques Vidrine 	    close(in_fd[0]);
2044137ff4cSJacques Vidrine 	    close(in_fd[1]);
2054137ff4cSJacques Vidrine 	}
2064137ff4cSJacques Vidrine 	if(stdout_fd != NULL) {
2074137ff4cSJacques Vidrine 	    close(out_fd[0]);
2084137ff4cSJacques Vidrine 	    close(out_fd[1]);
2094137ff4cSJacques Vidrine 	}
2104137ff4cSJacques Vidrine 	if(stderr_fd != NULL) {
2114137ff4cSJacques Vidrine 	    close(err_fd[0]);
2124137ff4cSJacques Vidrine 	    close(err_fd[1]);
2134137ff4cSJacques Vidrine 	}
214*ae771770SStanislav Sedov 	return SE_E_FORKFAILED;
2154137ff4cSJacques Vidrine     default:
2164137ff4cSJacques Vidrine 	if(stdin_fd != NULL) {
2174137ff4cSJacques Vidrine 	    close(in_fd[0]);
2184137ff4cSJacques Vidrine 	    *stdin_fd = fdopen(in_fd[1], "w");
2194137ff4cSJacques Vidrine 	}
2204137ff4cSJacques Vidrine 	if(stdout_fd != NULL) {
2214137ff4cSJacques Vidrine 	    close(out_fd[1]);
2224137ff4cSJacques Vidrine 	    *stdout_fd = fdopen(out_fd[0], "r");
2234137ff4cSJacques Vidrine 	}
2244137ff4cSJacques Vidrine 	if(stderr_fd != NULL) {
2254137ff4cSJacques Vidrine 	    close(err_fd[1]);
2264137ff4cSJacques Vidrine 	    *stderr_fd = fdopen(err_fd[0], "r");
2274137ff4cSJacques Vidrine 	}
2284137ff4cSJacques Vidrine     }
2294137ff4cSJacques Vidrine     return pid;
2304137ff4cSJacques Vidrine }
2314137ff4cSJacques Vidrine 
232*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execvp_timed(const char * file,char * const args[],time_t (* func)(void *),void * ptr,time_t timeout)233c19800e8SDoug Rabson simple_execvp_timed(const char *file, char *const args[],
234c19800e8SDoug Rabson 		    time_t (*func)(void *), void *ptr, time_t timeout)
235b528cefcSMark Murray {
236b528cefcSMark Murray     pid_t pid = fork();
237b528cefcSMark Murray     switch(pid){
238b528cefcSMark Murray     case -1:
239*ae771770SStanislav Sedov 	return SE_E_FORKFAILED;
240b528cefcSMark Murray     case 0:
241b528cefcSMark Murray 	execvp(file, args);
242b528cefcSMark Murray 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
243b528cefcSMark Murray     default:
244c19800e8SDoug Rabson 	return wait_for_process_timed(pid, func, ptr, timeout);
245b528cefcSMark Murray     }
246b528cefcSMark Murray }
247b528cefcSMark Murray 
248*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execvp(const char * file,char * const args[])249c19800e8SDoug Rabson simple_execvp(const char *file, char *const args[])
250c19800e8SDoug Rabson {
251c19800e8SDoug Rabson     return simple_execvp_timed(file, args, NULL, NULL, 0);
252c19800e8SDoug Rabson }
253c19800e8SDoug Rabson 
254b528cefcSMark Murray /* gee, I'd like a execvpe */
255*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execve_timed(const char * file,char * const args[],char * const envp[],time_t (* func)(void *),void * ptr,time_t timeout)256c19800e8SDoug Rabson simple_execve_timed(const char *file, char *const args[], char *const envp[],
257c19800e8SDoug Rabson 		    time_t (*func)(void *), void *ptr, time_t timeout)
258b528cefcSMark Murray {
259b528cefcSMark Murray     pid_t pid = fork();
260b528cefcSMark Murray     switch(pid){
261b528cefcSMark Murray     case -1:
262*ae771770SStanislav Sedov 	return SE_E_FORKFAILED;
263b528cefcSMark Murray     case 0:
264b528cefcSMark Murray 	execve(file, args, envp);
265b528cefcSMark Murray 	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
266b528cefcSMark Murray     default:
267c19800e8SDoug Rabson 	return wait_for_process_timed(pid, func, ptr, timeout);
268b528cefcSMark Murray     }
269b528cefcSMark Murray }
270b528cefcSMark Murray 
271*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execve(const char * file,char * const args[],char * const envp[])272c19800e8SDoug Rabson simple_execve(const char *file, char *const args[], char *const envp[])
273c19800e8SDoug Rabson {
274c19800e8SDoug Rabson     return simple_execve_timed(file, args, envp, NULL, NULL, 0);
275c19800e8SDoug Rabson }
276c19800e8SDoug Rabson 
277*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execlp(const char * file,...)278b528cefcSMark Murray simple_execlp(const char *file, ...)
279b528cefcSMark Murray {
280b528cefcSMark Murray     va_list ap;
281b528cefcSMark Murray     char **argv;
282b528cefcSMark Murray     int ret;
283b528cefcSMark Murray 
284b528cefcSMark Murray     va_start(ap, file);
28513e3f4d6SMark Murray     argv = vstrcollect(&ap);
286b528cefcSMark Murray     va_end(ap);
287b528cefcSMark Murray     if(argv == NULL)
288*ae771770SStanislav Sedov 	return SE_E_UNSPECIFIED;
289b528cefcSMark Murray     ret = simple_execvp(file, argv);
290b528cefcSMark Murray     free(argv);
291b528cefcSMark Murray     return ret;
292b528cefcSMark Murray }
293b528cefcSMark Murray 
294*ae771770SStanislav Sedov ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execle(const char * file,...)295b528cefcSMark Murray simple_execle(const char *file, ... /* ,char *const envp[] */)
296b528cefcSMark Murray {
297b528cefcSMark Murray     va_list ap;
298b528cefcSMark Murray     char **argv;
299b528cefcSMark Murray     char *const* envp;
300b528cefcSMark Murray     int ret;
301b528cefcSMark Murray 
302b528cefcSMark Murray     va_start(ap, file);
30313e3f4d6SMark Murray     argv = vstrcollect(&ap);
304b528cefcSMark Murray     envp = va_arg(ap, char **);
305b528cefcSMark Murray     va_end(ap);
306b528cefcSMark Murray     if(argv == NULL)
307*ae771770SStanislav Sedov 	return SE_E_UNSPECIFIED;
308b528cefcSMark Murray     ret = simple_execve(file, argv, envp);
309b528cefcSMark Murray     free(argv);
310b528cefcSMark Murray     return ret;
311b528cefcSMark Murray }
312