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