155682Smarkm/*
2178825Sdfr * Copyright (c) 1998 - 2001, 2004 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifdef HAVE_CONFIG_H
3555682Smarkm#include <config.h>
36178825SdfrRCSID("$Id: simple_exec.c 21005 2007-06-08 01:54:35Z lha $");
3755682Smarkm#endif
3855682Smarkm
3955682Smarkm#include <stdarg.h>
4055682Smarkm#include <stdlib.h>
4155682Smarkm#ifdef HAVE_SYS_TYPES_H
4255682Smarkm#include <sys/types.h>
4355682Smarkm#endif
4455682Smarkm#ifdef HAVE_SYS_WAIT_H
4555682Smarkm#include <sys/wait.h>
4655682Smarkm#endif
4755682Smarkm#ifdef HAVE_UNISTD_H
4855682Smarkm#include <unistd.h>
4955682Smarkm#endif
5055682Smarkm#include <errno.h>
5155682Smarkm
52178825Sdfr#include "roken.h"
5355682Smarkm
5455682Smarkm#define EX_NOEXEC	126
5555682Smarkm#define EX_NOTFOUND	127
5655682Smarkm
5755682Smarkm/* return values:
5855682Smarkm   -1   on `unspecified' system errors
5955682Smarkm   -2   on fork failures
6055682Smarkm   -3   on waitpid errors
61178825Sdfr   -4   exec timeout
6255682Smarkm   0-   is return value from subprocess
6355682Smarkm   126  if the program couldn't be executed
6455682Smarkm   127  if the program couldn't be found
6555682Smarkm   128- is 128 + signal that killed subprocess
66178825Sdfr
67178825Sdfr   possible values `func' can return:
68178825Sdfr   ((time_t)-2)		exit loop w/o killing child and return
69178825Sdfr   			`exec timeout'/-4 from simple_exec
70178825Sdfr   ((time_t)-1)		kill child with SIGTERM and wait for child to exit
71178825Sdfr   0			don't timeout again
72178825Sdfr   n			seconds to next timeout
7355682Smarkm   */
7455682Smarkm
75178825Sdfrstatic int sig_alarm;
76178825Sdfr
77178825Sdfrstatic RETSIGTYPE
78178825Sdfrsigtimeout(int sig)
7955682Smarkm{
80178825Sdfr    sig_alarm = 1;
81178825Sdfr    SIGRETURN(0);
82178825Sdfr}
83178825Sdfr
84178825Sdfrint ROKEN_LIB_FUNCTION
85178825Sdfrwait_for_process_timed(pid_t pid, time_t (*func)(void *),
86178825Sdfr		       void *ptr, time_t timeout)
87178825Sdfr{
88178825Sdfr    RETSIGTYPE (*old_func)(int sig) = NULL;
89178825Sdfr    unsigned int oldtime = 0;
90178825Sdfr    int ret;
91178825Sdfr
92178825Sdfr    sig_alarm = 0;
93178825Sdfr
94178825Sdfr    if (func) {
95178825Sdfr	old_func = signal(SIGALRM, sigtimeout);
96178825Sdfr	oldtime = alarm(timeout);
97178825Sdfr    }
98178825Sdfr
9955682Smarkm    while(1) {
10055682Smarkm	int status;
10155682Smarkm
102178825Sdfr	while(waitpid(pid, &status, 0) < 0) {
103178825Sdfr	    if (errno != EINTR) {
104178825Sdfr		ret = -3;
105178825Sdfr		goto out;
106178825Sdfr	    }
107178825Sdfr	    if (func == NULL)
108178825Sdfr		continue;
109178825Sdfr	    if (sig_alarm == 0)
110178825Sdfr		continue;
111178825Sdfr	    timeout = (*func)(ptr);
112178825Sdfr	    if (timeout == (time_t)-1) {
113178825Sdfr		kill(pid, SIGTERM);
114178825Sdfr		continue;
115178825Sdfr	    } else if (timeout == (time_t)-2) {
116178825Sdfr		ret = -4;
117178825Sdfr		goto out;
118178825Sdfr	    }
119178825Sdfr	    alarm(timeout);
120178825Sdfr	}
12155682Smarkm	if(WIFSTOPPED(status))
12255682Smarkm	    continue;
123178825Sdfr	if(WIFEXITED(status)) {
124178825Sdfr	    ret = WEXITSTATUS(status);
125178825Sdfr	    break;
126178825Sdfr	}
127178825Sdfr	if(WIFSIGNALED(status)) {
128178825Sdfr	    ret = WTERMSIG(status) + 128;
129178825Sdfr	    break;
130178825Sdfr	}
13155682Smarkm    }
132178825Sdfr out:
133178825Sdfr    if (func) {
134178825Sdfr	signal(SIGALRM, old_func);
135178825Sdfr	alarm(oldtime);
136178825Sdfr    }
137178825Sdfr    return ret;
13855682Smarkm}
13955682Smarkm
140178825Sdfrint ROKEN_LIB_FUNCTION
141178825Sdfrwait_for_process(pid_t pid)
142178825Sdfr{
143178825Sdfr    return wait_for_process_timed(pid, NULL, NULL, 0);
144178825Sdfr}
145178825Sdfr
146178825Sdfrint ROKEN_LIB_FUNCTION
14790926Snectarpipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
14890926Snectar	   const char *file, ...)
14990926Snectar{
15090926Snectar    int in_fd[2], out_fd[2], err_fd[2];
15190926Snectar    pid_t pid;
15290926Snectar    va_list ap;
15390926Snectar    char **argv;
15490926Snectar
15590926Snectar    if(stdin_fd != NULL)
15690926Snectar	pipe(in_fd);
15790926Snectar    if(stdout_fd != NULL)
15890926Snectar	pipe(out_fd);
15990926Snectar    if(stderr_fd != NULL)
16090926Snectar	pipe(err_fd);
16190926Snectar    pid = fork();
16290926Snectar    switch(pid) {
16390926Snectar    case 0:
16490926Snectar	va_start(ap, file);
16590926Snectar	argv = vstrcollect(&ap);
16690926Snectar	va_end(ap);
16790926Snectar	if(argv == NULL)
16890926Snectar	    exit(-1);
16990926Snectar
17090926Snectar	/* close pipes we're not interested in */
17190926Snectar	if(stdin_fd != NULL)
17290926Snectar	    close(in_fd[1]);
17390926Snectar	if(stdout_fd != NULL)
17490926Snectar	    close(out_fd[0]);
17590926Snectar	if(stderr_fd != NULL)
17690926Snectar	    close(err_fd[0]);
17790926Snectar
17890926Snectar	/* pipe everything caller doesn't care about to /dev/null */
17990926Snectar	if(stdin_fd == NULL)
18090926Snectar	    in_fd[0] = open(_PATH_DEVNULL, O_RDONLY);
18190926Snectar	if(stdout_fd == NULL)
18290926Snectar	    out_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
18390926Snectar	if(stderr_fd == NULL)
18490926Snectar	    err_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
18590926Snectar
18690926Snectar	/* move to proper descriptors */
18790926Snectar	if(in_fd[0] != STDIN_FILENO) {
18890926Snectar	    dup2(in_fd[0], STDIN_FILENO);
18990926Snectar	    close(in_fd[0]);
19090926Snectar	}
19190926Snectar	if(out_fd[1] != STDOUT_FILENO) {
19290926Snectar	    dup2(out_fd[1], STDOUT_FILENO);
19390926Snectar	    close(out_fd[1]);
19490926Snectar	}
19590926Snectar	if(err_fd[1] != STDERR_FILENO) {
19690926Snectar	    dup2(err_fd[1], STDERR_FILENO);
19790926Snectar	    close(err_fd[1]);
19890926Snectar	}
19990926Snectar
200178825Sdfr	closefrom(3);
201178825Sdfr
20290926Snectar	execv(file, argv);
20390926Snectar	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
20490926Snectar    case -1:
20590926Snectar	if(stdin_fd != NULL) {
20690926Snectar	    close(in_fd[0]);
20790926Snectar	    close(in_fd[1]);
20890926Snectar	}
20990926Snectar	if(stdout_fd != NULL) {
21090926Snectar	    close(out_fd[0]);
21190926Snectar	    close(out_fd[1]);
21290926Snectar	}
21390926Snectar	if(stderr_fd != NULL) {
21490926Snectar	    close(err_fd[0]);
21590926Snectar	    close(err_fd[1]);
21690926Snectar	}
21790926Snectar	return -2;
21890926Snectar    default:
21990926Snectar	if(stdin_fd != NULL) {
22090926Snectar	    close(in_fd[0]);
22190926Snectar	    *stdin_fd = fdopen(in_fd[1], "w");
22290926Snectar	}
22390926Snectar	if(stdout_fd != NULL) {
22490926Snectar	    close(out_fd[1]);
22590926Snectar	    *stdout_fd = fdopen(out_fd[0], "r");
22690926Snectar	}
22790926Snectar	if(stderr_fd != NULL) {
22890926Snectar	    close(err_fd[1]);
22990926Snectar	    *stderr_fd = fdopen(err_fd[0], "r");
23090926Snectar	}
23190926Snectar    }
23290926Snectar    return pid;
23390926Snectar}
23490926Snectar
235178825Sdfrint ROKEN_LIB_FUNCTION
236178825Sdfrsimple_execvp_timed(const char *file, char *const args[],
237178825Sdfr		    time_t (*func)(void *), void *ptr, time_t timeout)
23855682Smarkm{
23955682Smarkm    pid_t pid = fork();
24055682Smarkm    switch(pid){
24155682Smarkm    case -1:
24255682Smarkm	return -2;
24355682Smarkm    case 0:
24455682Smarkm	execvp(file, args);
24555682Smarkm	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
24655682Smarkm    default:
247178825Sdfr	return wait_for_process_timed(pid, func, ptr, timeout);
24855682Smarkm    }
24955682Smarkm}
25055682Smarkm
251178825Sdfrint ROKEN_LIB_FUNCTION
252178825Sdfrsimple_execvp(const char *file, char *const args[])
253178825Sdfr{
254178825Sdfr    return simple_execvp_timed(file, args, NULL, NULL, 0);
255178825Sdfr}
256178825Sdfr
25755682Smarkm/* gee, I'd like a execvpe */
258178825Sdfrint ROKEN_LIB_FUNCTION
259178825Sdfrsimple_execve_timed(const char *file, char *const args[], char *const envp[],
260178825Sdfr		    time_t (*func)(void *), void *ptr, time_t timeout)
26155682Smarkm{
26255682Smarkm    pid_t pid = fork();
26355682Smarkm    switch(pid){
26455682Smarkm    case -1:
26555682Smarkm	return -2;
26655682Smarkm    case 0:
26755682Smarkm	execve(file, args, envp);
26855682Smarkm	exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
26955682Smarkm    default:
270178825Sdfr	return wait_for_process_timed(pid, func, ptr, timeout);
27155682Smarkm    }
27255682Smarkm}
27355682Smarkm
274178825Sdfrint ROKEN_LIB_FUNCTION
275178825Sdfrsimple_execve(const char *file, char *const args[], char *const envp[])
276178825Sdfr{
277178825Sdfr    return simple_execve_timed(file, args, envp, NULL, NULL, 0);
278178825Sdfr}
279178825Sdfr
280178825Sdfrint ROKEN_LIB_FUNCTION
28155682Smarkmsimple_execlp(const char *file, ...)
28255682Smarkm{
28355682Smarkm    va_list ap;
28455682Smarkm    char **argv;
28555682Smarkm    int ret;
28655682Smarkm
28755682Smarkm    va_start(ap, file);
28857416Smarkm    argv = vstrcollect(&ap);
28955682Smarkm    va_end(ap);
29055682Smarkm    if(argv == NULL)
29155682Smarkm	return -1;
29255682Smarkm    ret = simple_execvp(file, argv);
29355682Smarkm    free(argv);
29455682Smarkm    return ret;
29555682Smarkm}
29655682Smarkm
297178825Sdfrint ROKEN_LIB_FUNCTION
29855682Smarkmsimple_execle(const char *file, ... /* ,char *const envp[] */)
29955682Smarkm{
30055682Smarkm    va_list ap;
30155682Smarkm    char **argv;
30255682Smarkm    char *const* envp;
30355682Smarkm    int ret;
30455682Smarkm
30555682Smarkm    va_start(ap, file);
30657416Smarkm    argv = vstrcollect(&ap);
30755682Smarkm    envp = va_arg(ap, char **);
30855682Smarkm    va_end(ap);
30955682Smarkm    if(argv == NULL)
31055682Smarkm	return -1;
31155682Smarkm    ret = simple_execve(file, argv, envp);
31255682Smarkm    free(argv);
31355682Smarkm    return ret;
31455682Smarkm}
31572445Sassar
316178825Sdfrint ROKEN_LIB_FUNCTION
31772445Sassarsimple_execl(const char *file, ...)
31872445Sassar{
31972445Sassar    va_list ap;
32072445Sassar    char **argv;
32172445Sassar    int ret;
32272445Sassar
32372445Sassar    va_start(ap, file);
32472445Sassar    argv = vstrcollect(&ap);
32572445Sassar    va_end(ap);
32672445Sassar    if(argv == NULL)
32772445Sassar	return -1;
32872445Sassar    ret = simple_execve(file, argv, environ);
32972445Sassar    free(argv);
33072445Sassar    return ret;
33172445Sassar}
332