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: simple_exec.c 21005 2007-06-08 01:54:35Z lha $"); 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 } 332