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#include <config.h> 35 36#include <stdarg.h> 37#include <stdlib.h> 38#ifdef HAVE_SYS_TYPES_H 39#include <sys/types.h> 40#endif 41#ifdef HAVE_SYS_WAIT_H 42#include <sys/wait.h> 43#endif 44#ifdef HAVE_UNISTD_H 45#include <unistd.h> 46#endif 47#include <errno.h> 48 49#include "roken.h" 50 51#define EX_NOEXEC 126 52#define EX_NOTFOUND 127 53 54/* return values: 55 SE_E_UNSPECIFIED on `unspecified' system errors 56 SE_E_FORKFAILED on fork failures 57 SE_E_WAITPIDFAILED on waitpid errors 58 SE_E_EXECTIMEOUT exec timeout 59 0- is return value from subprocess 60 SE_E_NOEXEC if the program couldn't be executed 61 SE_E_NOTFOUND if the program couldn't be found 62 128- is 128 + signal that killed subprocess 63 64 possible values `func' can return: 65 ((time_t)-2) exit loop w/o killing child and return 66 `exec timeout'/-4 from simple_exec 67 ((time_t)-1) kill child with SIGTERM and wait for child to exit 68 0 don't timeout again 69 n seconds to next timeout 70 */ 71 72static int sig_alarm; 73 74static RETSIGTYPE 75sigtimeout(int sig) 76{ 77 sig_alarm = 1; 78 SIGRETURN(0); 79} 80 81ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 82wait_for_process_timed(pid_t pid, time_t (*func)(void *), 83 void *ptr, time_t timeout) 84{ 85 RETSIGTYPE (*old_func)(int sig) = NULL; 86 unsigned int oldtime = 0; 87 int ret; 88 89 sig_alarm = 0; 90 91 if (func) { 92 old_func = signal(SIGALRM, sigtimeout); 93 oldtime = alarm(timeout); 94 } 95 96 while(1) { 97 int status; 98 99 while(waitpid(pid, &status, 0) < 0) { 100 if (errno != EINTR) { 101 ret = SE_E_WAITPIDFAILED; 102 goto out; 103 } 104 if (func == NULL) 105 continue; 106 if (sig_alarm == 0) 107 continue; 108 timeout = (*func)(ptr); 109 if (timeout == (time_t)-1) { 110 kill(pid, SIGTERM); 111 continue; 112 } else if (timeout == (time_t)-2) { 113 ret = SE_E_EXECTIMEOUT; 114 goto out; 115 } 116 alarm(timeout); 117 } 118 if(WIFSTOPPED(status)) 119 continue; 120 if(WIFEXITED(status)) { 121 ret = WEXITSTATUS(status); 122 break; 123 } 124 if(WIFSIGNALED(status)) { 125 ret = WTERMSIG(status) + 128; 126 break; 127 } 128 } 129 out: 130 if (func) { 131 signal(SIGALRM, old_func); 132 alarm(oldtime); 133 } 134 return ret; 135} 136 137ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 138wait_for_process(pid_t pid) 139{ 140 return wait_for_process_timed(pid, NULL, NULL, 0); 141} 142 143ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 144pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd, 145 const char *file, ...) 146{ 147 int in_fd[2], out_fd[2], err_fd[2]; 148 pid_t pid; 149 va_list ap; 150 char **argv; 151 152 if(stdin_fd != NULL) 153 pipe(in_fd); 154 if(stdout_fd != NULL) 155 pipe(out_fd); 156 if(stderr_fd != NULL) 157 pipe(err_fd); 158 pid = fork(); 159 switch(pid) { 160 case 0: 161 va_start(ap, file); 162 argv = vstrcollect(&ap); 163 va_end(ap); 164 if(argv == NULL) 165 exit(-1); 166 167 /* close pipes we're not interested in */ 168 if(stdin_fd != NULL) 169 close(in_fd[1]); 170 if(stdout_fd != NULL) 171 close(out_fd[0]); 172 if(stderr_fd != NULL) 173 close(err_fd[0]); 174 175 /* pipe everything caller doesn't care about to /dev/null */ 176 if(stdin_fd == NULL) 177 in_fd[0] = open(_PATH_DEVNULL, O_RDONLY); 178 if(stdout_fd == NULL) 179 out_fd[1] = open(_PATH_DEVNULL, O_WRONLY); 180 if(stderr_fd == NULL) 181 err_fd[1] = open(_PATH_DEVNULL, O_WRONLY); 182 183 /* move to proper descriptors */ 184 if(in_fd[0] != STDIN_FILENO) { 185 dup2(in_fd[0], STDIN_FILENO); 186 close(in_fd[0]); 187 } 188 if(out_fd[1] != STDOUT_FILENO) { 189 dup2(out_fd[1], STDOUT_FILENO); 190 close(out_fd[1]); 191 } 192 if(err_fd[1] != STDERR_FILENO) { 193 dup2(err_fd[1], STDERR_FILENO); 194 close(err_fd[1]); 195 } 196 197 closefrom(3); 198 199 execv(file, argv); 200 exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC); 201 case -1: 202 if(stdin_fd != NULL) { 203 close(in_fd[0]); 204 close(in_fd[1]); 205 } 206 if(stdout_fd != NULL) { 207 close(out_fd[0]); 208 close(out_fd[1]); 209 } 210 if(stderr_fd != NULL) { 211 close(err_fd[0]); 212 close(err_fd[1]); 213 } 214 return SE_E_FORKFAILED; 215 default: 216 if(stdin_fd != NULL) { 217 close(in_fd[0]); 218 *stdin_fd = fdopen(in_fd[1], "w"); 219 } 220 if(stdout_fd != NULL) { 221 close(out_fd[1]); 222 *stdout_fd = fdopen(out_fd[0], "r"); 223 } 224 if(stderr_fd != NULL) { 225 close(err_fd[1]); 226 *stderr_fd = fdopen(err_fd[0], "r"); 227 } 228 } 229 return pid; 230} 231 232ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 233simple_execvp_timed(const char *file, char *const args[], 234 time_t (*func)(void *), void *ptr, time_t timeout) 235{ 236 pid_t pid = fork(); 237 switch(pid){ 238 case -1: 239 return SE_E_FORKFAILED; 240 case 0: 241 execvp(file, args); 242 exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC); 243 default: 244 return wait_for_process_timed(pid, func, ptr, timeout); 245 } 246} 247 248ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 249simple_execvp(const char *file, char *const args[]) 250{ 251 return simple_execvp_timed(file, args, NULL, NULL, 0); 252} 253 254/* gee, I'd like a execvpe */ 255ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 256simple_execve_timed(const char *file, char *const args[], char *const envp[], 257 time_t (*func)(void *), void *ptr, time_t timeout) 258{ 259 pid_t pid = fork(); 260 switch(pid){ 261 case -1: 262 return SE_E_FORKFAILED; 263 case 0: 264 execve(file, args, envp); 265 exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC); 266 default: 267 return wait_for_process_timed(pid, func, ptr, timeout); 268 } 269} 270 271ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 272simple_execve(const char *file, char *const args[], char *const envp[]) 273{ 274 return simple_execve_timed(file, args, envp, NULL, NULL, 0); 275} 276 277ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 278simple_execlp(const char *file, ...) 279{ 280 va_list ap; 281 char **argv; 282 int ret; 283 284 va_start(ap, file); 285 argv = vstrcollect(&ap); 286 va_end(ap); 287 if(argv == NULL) 288 return SE_E_UNSPECIFIED; 289 ret = simple_execvp(file, argv); 290 free(argv); 291 return ret; 292} 293 294ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 295simple_execle(const char *file, ... /* ,char *const envp[] */) 296{ 297 va_list ap; 298 char **argv; 299 char *const* envp; 300 int ret; 301 302 va_start(ap, file); 303 argv = vstrcollect(&ap); 304 envp = va_arg(ap, char **); 305 va_end(ap); 306 if(argv == NULL) 307 return SE_E_UNSPECIFIED; 308 ret = simple_execve(file, argv, envp); 309 free(argv); 310 return ret; 311} 312