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