Merge Samba3 and Samba4 together
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / roken / simple_exec.c
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$");
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 }