#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <err.h>
#include <libprop/proplib.h>
#include "testcase.h"
#include "runlist.h"
#include "userland.h"
#include <dfregress.h>
static void
clean_child(pid_t pid)
{
kill(pid, SIGKILL);
}
static void
sig_handle(int sig __unused)
{
return;
}
int
run_userland(const char *binary, int argc, const char **argv, const char *interpreter,
int need_setuid, uid_t uid, struct timeval *timeout, int rc, int unify_output,
char *errbuf, size_t errbuf_sz, struct testcase_result *tr)
{
struct itimerval itim;
struct sigaction sa;
pid_t pid = -1, r_pid;
int r, status;
int fd_stdout = -1, fd_stderr = -1;
size_t sz_stdout, sz_stderr;
char stdout_file[256];
char stderr_file[256];
char **argv_copy;
bzero(tr, sizeof(*tr));
tr->result = RESULT_NOTRUN;
strcpy(stdout_file, "/tmp/dfregress.XXXXXXXXXXXX");
strcpy(stderr_file, "/tmp/dfregress.XXXXXXXXXXXX");
fd_stdout = mkostemp(stdout_file, O_SYNC);
if (fd_stdout == -1) {
if (errbuf)
snprintf(errbuf, errbuf_sz, "Could not mkostemp(): "
"%s\n", strerror(errno));
return -1;
}
if (!unify_output) {
fd_stderr = mkostemp(stderr_file, O_SYNC);
if (fd_stderr == -1) {
if (errbuf)
snprintf(errbuf, errbuf_sz, "Could not mkostemp(): "
"%s\n", strerror(errno));
return -1;
}
}
if ((pid = fork()) == -1) {
if (errbuf)
snprintf(errbuf, errbuf_sz, "Could not fork to run "
"binary %s: %s\n", binary, strerror(errno));
goto err_out;
} else if (pid > 0) {
if (timeout != NULL) {
bzero(&sa, sizeof(sa));
sa.sa_handler = sig_handle;
sigaction(SIGALRM, &sa, NULL);
itim.it_interval.tv_sec = 0;
itim.it_interval.tv_usec = 0;
itim.it_value = *timeout;
r = setitimer(ITIMER_REAL, &itim, NULL);
if (r == -1) {
if (errbuf)
snprintf(errbuf, errbuf_sz, "Could not "
"set up timer: %s", strerror(errno));
goto err_out;
}
}
r_pid = wait4(pid, &status, 0, &tr->rusage);
if (r_pid == -1) {
if (errno == EINTR) {
tr->result = RESULT_TIMEOUT;
clean_child(pid);
} else if (errno == ECHILD) {
tr->result = RESULT_UNKNOWN;
} else {
if (errbuf)
snprintf(errbuf, errbuf_sz, "Could not "
"wait4(): %s", strerror(errno));
goto err_out;
}
} else {
if (WIFEXITED(status)) {
tr->result = (WEXITSTATUS(status) == rc) ?
RESULT_PASS :
(WEXITSTATUS(status) == EXIT_NOTRUN) ?
RESULT_NOTRUN : RESULT_FAIL;
tr->exit_value = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
tr->result = RESULT_SIGNALLED;
tr->signal = WTERMSIG(status);
tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0;
} else {
tr->result = RESULT_UNKNOWN;
}
}
if (timeout != NULL) {
itim.it_value.tv_sec = 0;
itim.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &itim, NULL);
}
} else {
if (fd_stdout >= 0) {
dup2(fd_stdout, 1);
setvbuf(stdout, NULL, _IONBF, 0);
}
if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) {
dup2((unify_output) ? fd_stdout : fd_stderr, 2);
setvbuf((unify_output) ? stdout : stderr,
NULL, _IONBF, 0);
}
if (need_setuid) {
r = setuid(uid);
if (r == -1) {
fprintf(stderr, "ERR: NOT RUN (setuid): %s",
strerror(errno));
exit(EXIT_NOTRUN);
}
}
if (interpreter) {
argv_copy = (char **)calloc(argc + 3, sizeof(char *));
if (argv_copy == NULL) {
err(1, "could not calloc argv_copy memory");
}
argv_copy[0] = malloc(strlen(interpreter) + 1);
snprintf(argv_copy[0], strlen(interpreter) + 1, "%s",
interpreter);
for (int i = 0; i <= argc; i++) {
size_t len;
len = strlen(argv[i]) + 1;
argv_copy[i + 1] = malloc(len);
if (argv_copy[i] == NULL)
err(1, "could not malloc memory");
snprintf(argv_copy[i + 1], len, "%s",
argv[i]);
}
argv_copy[argc + 2] = NULL;
r = execvp(interpreter, argv_copy);
} else {
r = execvp(binary, __DECONST(char **, argv));
}
if (r == -1) {
fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno));
exit(EXIT_NOTRUN);
}
}
sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END);
lseek(fd_stdout, 0, SEEK_SET);
tr->stdout_buf = malloc(sz_stdout + 1);
if (tr->stdout_buf == NULL)
err(1, "could not malloc fd buf memory");
read(fd_stdout, tr->stdout_buf, sz_stdout);
tr->stdout_buf[sz_stdout] = '\0';
close(fd_stdout);
unlink(stdout_file);
if (!unify_output) {
sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END);
lseek(fd_stderr, 0, SEEK_SET);
tr->stderr_buf = malloc(sz_stderr + 1);
if (tr->stderr_buf == NULL)
err(1, "could not malloc fd buf memory");
read(fd_stderr, tr->stderr_buf, sz_stderr);
tr->stderr_buf[sz_stderr] = '\0';
close(fd_stderr);
unlink(stderr_file);
}
return 0;
err_out:
if (pid != -1)
clean_child(pid);
if (fd_stdout >= 0) {
close(fd_stdout);
unlink(stdout_file);
}
if (fd_stderr >= 0) {
close(fd_stderr);
unlink(stderr_file);
}
return -1;
}
int
run_simple_cmd(const char *binary, const char *arg, char *errbuf,
size_t errbuf_sz, struct testcase_result *tr)
{
const char *argv[3];
char *s;
s = strrchr(binary, '/');
argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
argv[1] = __DECONST(char *, arg);
argv[2] = NULL;
return run_userland(binary,
1,
argv,
NULL,
0,
0,
NULL,
0,
1,
errbuf,
errbuf_sz,
tr);
}