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 72 static int sig_alarm; 73 74 static RETSIGTYPE 75 sigtimeout(int sig) 76 { 77 sig_alarm = 1; 78 SIGRETURN(0); 79 } 80 81 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 82 wait_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 137 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 138 wait_for_process(pid_t pid) 139 { 140 return wait_for_process_timed(pid, NULL, NULL, 0); 141 } 142 143 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 144 pipe_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 232 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 233 simple_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 248 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 249 simple_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 */ 255 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 256 simple_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 271 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 272 simple_execve(const char *file, char *const args[], char *const envp[]) 273 { 274 return simple_execve_timed(file, args, envp, NULL, NULL, 0); 275 } 276 277 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 278 simple_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 294 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 295 simple_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