1 /*        $NetBSD: simple_exec.c,v 1.2 2017/01/28 21:31:50 christos 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
sigtimeout(int sig)77 sigtimeout(int sig)
78 {
79     sig_alarm = 1;
80     SIGRETURN(0);
81 }
82 
83 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
wait_for_process_timed(pid_t pid,time_t (* func)(void *),void * ptr,time_t timeout)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
wait_for_process(pid_t pid)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
pipe_execv(FILE ** stdin_fd,FILE ** stdout_fd,FILE ** stderr_fd,const char * file,...)146 pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
147              const char *file, ...)
148 {
149     int in_fd[2] = {-1, -1};
150     int out_fd[2] = {-1, -1};
151     int err_fd[2] = {-1, -1};
152     pid_t pid;
153     va_list ap;
154     char **argv;
155     int ret = 0;
156 
157     if(stdin_fd != NULL)
158           ret = pipe(in_fd);
159     if(ret != -1 && stdout_fd != NULL)
160           ret = pipe(out_fd);
161     if(ret != -1 && stderr_fd != NULL)
162           ret = pipe(err_fd);
163 
164     if (ret == -1) {
165           close(in_fd[0]);
166           close(in_fd[1]);
167           close(out_fd[0]);
168           close(out_fd[1]);
169           close(err_fd[0]);
170           close(err_fd[1]);
171           return SE_E_UNSPECIFIED;
172     }
173 
174     pid = fork();
175     switch(pid) {
176     case 0:
177           va_start(ap, file);
178           argv = vstrcollect(&ap);
179           va_end(ap);
180           if(argv == NULL)
181               exit(-1);
182 
183           /* close pipes we're not interested in */
184           if(stdin_fd != NULL)
185               close(in_fd[1]);
186           if(stdout_fd != NULL)
187               close(out_fd[0]);
188           if(stderr_fd != NULL)
189               close(err_fd[0]);
190 
191           /* pipe everything caller doesn't care about to /dev/null */
192           if(stdin_fd == NULL)
193               in_fd[0] = open(_PATH_DEVNULL, O_RDONLY);
194           if(stdout_fd == NULL)
195               out_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
196           if(stderr_fd == NULL)
197               err_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
198 
199           /* move to proper descriptors */
200           if(in_fd[0] != STDIN_FILENO) {
201               dup2(in_fd[0], STDIN_FILENO);
202               close(in_fd[0]);
203           }
204           if(out_fd[1] != STDOUT_FILENO) {
205               dup2(out_fd[1], STDOUT_FILENO);
206               close(out_fd[1]);
207           }
208           if(err_fd[1] != STDERR_FILENO) {
209               dup2(err_fd[1], STDERR_FILENO);
210               close(err_fd[1]);
211           }
212 
213           closefrom(3);
214 
215           execv(file, argv);
216           exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
217     case -1:
218           if(stdin_fd != NULL) {
219               close(in_fd[0]);
220               close(in_fd[1]);
221           }
222           if(stdout_fd != NULL) {
223               close(out_fd[0]);
224               close(out_fd[1]);
225           }
226           if(stderr_fd != NULL) {
227               close(err_fd[0]);
228               close(err_fd[1]);
229           }
230           return SE_E_FORKFAILED;
231     default:
232           if(stdin_fd != NULL) {
233               close(in_fd[0]);
234               *stdin_fd = fdopen(in_fd[1], "w");
235           }
236           if(stdout_fd != NULL) {
237               close(out_fd[1]);
238               *stdout_fd = fdopen(out_fd[0], "r");
239           }
240           if(stderr_fd != NULL) {
241               close(err_fd[1]);
242               *stderr_fd = fdopen(err_fd[0], "r");
243           }
244     }
245     return pid;
246 }
247 
248 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)249 simple_execvp_timed(const char *file, char *const args[],
250                         time_t (*func)(void *), void *ptr, time_t timeout)
251 {
252     pid_t pid = fork();
253     switch(pid){
254     case -1:
255           return SE_E_FORKFAILED;
256     case 0:
257           execvp(file, args);
258           exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
259     default:
260           return wait_for_process_timed(pid, func, ptr, timeout);
261     }
262 }
263 
264 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execvp(const char * file,char * const args[])265 simple_execvp(const char *file, char *const args[])
266 {
267     return simple_execvp_timed(file, args, NULL, NULL, 0);
268 }
269 
270 /* gee, I'd like a execvpe */
271 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)272 simple_execve_timed(const char *file, char *const args[], char *const envp[],
273                         time_t (*func)(void *), void *ptr, time_t timeout)
274 {
275     pid_t pid = fork();
276     switch(pid){
277     case -1:
278           return SE_E_FORKFAILED;
279     case 0:
280           execve(file, args, envp);
281           exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
282     default:
283           return wait_for_process_timed(pid, func, ptr, timeout);
284     }
285 }
286 
287 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execve(const char * file,char * const args[],char * const envp[])288 simple_execve(const char *file, char *const args[], char *const envp[])
289 {
290     return simple_execve_timed(file, args, envp, NULL, NULL, 0);
291 }
292 
293 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execlp(const char * file,...)294 simple_execlp(const char *file, ...)
295 {
296     va_list ap;
297     char **argv;
298     int ret;
299 
300     va_start(ap, file);
301     argv = vstrcollect(&ap);
302     va_end(ap);
303     if(argv == NULL)
304           return SE_E_UNSPECIFIED;
305     ret = simple_execvp(file, argv);
306     free(argv);
307     return ret;
308 }
309 
310 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
simple_execle(const char * file,...)311 simple_execle(const char *file, ... /* ,char *const envp[] */)
312 {
313     va_list ap;
314     char **argv;
315     char *const* envp;
316     int ret;
317 
318     va_start(ap, file);
319     argv = vstrcollect(&ap);
320     envp = va_arg(ap, char **);
321     va_end(ap);
322     if(argv == NULL)
323           return SE_E_UNSPECIFIED;
324     ret = simple_execve(file, argv, envp);
325     free(argv);
326     return ret;
327 }
328