1a563ca70SAlex Hornung /*
2a563ca70SAlex Hornung * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3a563ca70SAlex Hornung * All rights reserved.
4a563ca70SAlex Hornung *
5a563ca70SAlex Hornung * Redistribution and use in source and binary forms, with or without
6a563ca70SAlex Hornung * modification, are permitted provided that the following conditions
7a563ca70SAlex Hornung * are met:
8a563ca70SAlex Hornung *
9a563ca70SAlex Hornung * 1. Redistributions of source code must retain the above copyright
10a563ca70SAlex Hornung * notice, this list of conditions and the following disclaimer.
11a563ca70SAlex Hornung * 2. Redistributions in binary form must reproduce the above copyright
12a563ca70SAlex Hornung * notice, this list of conditions and the following disclaimer in
13a563ca70SAlex Hornung * the documentation and/or other materials provided with the
14a563ca70SAlex Hornung * distribution.
15a563ca70SAlex Hornung *
16a563ca70SAlex Hornung * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17a563ca70SAlex Hornung * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18a563ca70SAlex Hornung * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19a563ca70SAlex Hornung * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20a563ca70SAlex Hornung * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21a563ca70SAlex Hornung * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22a563ca70SAlex Hornung * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23a563ca70SAlex Hornung * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24a563ca70SAlex Hornung * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25a563ca70SAlex Hornung * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26a563ca70SAlex Hornung * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a563ca70SAlex Hornung * SUCH DAMAGE.
28a563ca70SAlex Hornung */
29a563ca70SAlex Hornung
30a563ca70SAlex Hornung #include <sys/resource.h>
31a563ca70SAlex Hornung #include <sys/time.h>
32a563ca70SAlex Hornung #include <sys/types.h>
33a563ca70SAlex Hornung #include <sys/wait.h>
34a563ca70SAlex Hornung
35a563ca70SAlex Hornung #include <errno.h>
36a563ca70SAlex Hornung #include <fcntl.h>
37a563ca70SAlex Hornung #include <signal.h>
38a563ca70SAlex Hornung #include <stdio.h>
39a563ca70SAlex Hornung #include <stdlib.h>
40a563ca70SAlex Hornung #include <stdint.h>
41a563ca70SAlex Hornung #include <string.h>
42a563ca70SAlex Hornung #include <unistd.h>
43a563ca70SAlex Hornung #include <pwd.h>
44a563ca70SAlex Hornung
45a563ca70SAlex Hornung #include <err.h>
46a563ca70SAlex Hornung
47a563ca70SAlex Hornung #include <libprop/proplib.h>
48a563ca70SAlex Hornung
49a563ca70SAlex Hornung #include "testcase.h"
50a563ca70SAlex Hornung #include "runlist.h"
51a563ca70SAlex Hornung #include "userland.h"
52a563ca70SAlex Hornung #include <dfregress.h>
53a563ca70SAlex Hornung
54a563ca70SAlex Hornung static void
clean_child(pid_t pid)55a563ca70SAlex Hornung clean_child(pid_t pid)
56a563ca70SAlex Hornung {
57a563ca70SAlex Hornung kill(pid, SIGKILL);
58a563ca70SAlex Hornung }
59a563ca70SAlex Hornung
60a563ca70SAlex Hornung static void
sig_handle(int sig __unused)61a563ca70SAlex Hornung sig_handle(int sig __unused)
62a563ca70SAlex Hornung {
63a563ca70SAlex Hornung return;
64a563ca70SAlex Hornung }
65a563ca70SAlex Hornung
66a563ca70SAlex Hornung 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)670d575305SAntonio Huete Jimenez run_userland(const char *binary, int argc, const char **argv, const char *interpreter,
68*039caa27SAntonio Huete Jimenez int need_setuid, uid_t uid, struct timeval *timeout, int rc, int unify_output,
690d575305SAntonio Huete Jimenez char *errbuf, size_t errbuf_sz, struct testcase_result *tr)
70a563ca70SAlex Hornung {
71a563ca70SAlex Hornung struct itimerval itim;
72a563ca70SAlex Hornung struct sigaction sa;
738135efafSAlex Hornung pid_t pid = -1, r_pid;
74a563ca70SAlex Hornung int r, status;
75a563ca70SAlex Hornung int fd_stdout = -1, fd_stderr = -1;
76a563ca70SAlex Hornung size_t sz_stdout, sz_stderr;
77a563ca70SAlex Hornung char stdout_file[256];
78a563ca70SAlex Hornung char stderr_file[256];
790d575305SAntonio Huete Jimenez char **argv_copy;
80a563ca70SAlex Hornung
81a563ca70SAlex Hornung /* Set sane defaults */
82a563ca70SAlex Hornung bzero(tr, sizeof(*tr));
83a563ca70SAlex Hornung tr->result = RESULT_NOTRUN;
84a563ca70SAlex Hornung
85a563ca70SAlex Hornung strcpy(stdout_file, "/tmp/dfregress.XXXXXXXXXXXX");
86a563ca70SAlex Hornung strcpy(stderr_file, "/tmp/dfregress.XXXXXXXXXXXX");
87902eed00SSascha Wildner fd_stdout = mkostemp(stdout_file, O_SYNC);
88902eed00SSascha Wildner if (fd_stdout == -1) {
89a563ca70SAlex Hornung if (errbuf)
90902eed00SSascha Wildner snprintf(errbuf, errbuf_sz, "Could not mkostemp(): "
91a563ca70SAlex Hornung "%s\n", strerror(errno));
92a563ca70SAlex Hornung return -1;
93a563ca70SAlex Hornung }
94a563ca70SAlex Hornung
95a563ca70SAlex Hornung if (!unify_output) {
96902eed00SSascha Wildner fd_stderr = mkostemp(stderr_file, O_SYNC);
97902eed00SSascha Wildner if (fd_stderr == -1) {
98a563ca70SAlex Hornung if (errbuf)
99902eed00SSascha Wildner snprintf(errbuf, errbuf_sz, "Could not mkostemp(): "
100a563ca70SAlex Hornung "%s\n", strerror(errno));
101a563ca70SAlex Hornung return -1;
102a563ca70SAlex Hornung }
103a563ca70SAlex Hornung }
104a563ca70SAlex Hornung
105a563ca70SAlex Hornung
106a563ca70SAlex Hornung if ((pid = fork()) == -1) {
107a563ca70SAlex Hornung if (errbuf)
108a563ca70SAlex Hornung snprintf(errbuf, errbuf_sz, "Could not fork to run "
109a563ca70SAlex Hornung "binary %s: %s\n", binary, strerror(errno));
110a563ca70SAlex Hornung
111a563ca70SAlex Hornung goto err_out;
112a563ca70SAlex Hornung } else if (pid > 0) {
113a563ca70SAlex Hornung /* parent */
114a563ca70SAlex Hornung
115a563ca70SAlex Hornung if (timeout != NULL) {
116a563ca70SAlex Hornung /* Ignore SIGALRM */
117a563ca70SAlex Hornung bzero(&sa, sizeof(sa));
118a563ca70SAlex Hornung sa.sa_handler = sig_handle;
119a563ca70SAlex Hornung sigaction(SIGALRM, &sa, NULL);
120a563ca70SAlex Hornung
121a563ca70SAlex Hornung /* Set up timeout */
122a563ca70SAlex Hornung itim.it_interval.tv_sec = 0;
123a563ca70SAlex Hornung itim.it_interval.tv_usec = 0;
124a563ca70SAlex Hornung itim.it_value = *timeout;
125a563ca70SAlex Hornung r = setitimer(ITIMER_REAL, &itim, NULL);
126a563ca70SAlex Hornung if (r == -1) {
127a563ca70SAlex Hornung if (errbuf)
128a563ca70SAlex Hornung snprintf(errbuf, errbuf_sz, "Could not "
129a563ca70SAlex Hornung "set up timer: %s", strerror(errno));
130a563ca70SAlex Hornung
131a563ca70SAlex Hornung /* Clean up child process! */
132a563ca70SAlex Hornung goto err_out;
133a563ca70SAlex Hornung }
134a563ca70SAlex Hornung }
135a563ca70SAlex Hornung
136a563ca70SAlex Hornung r_pid = wait4(pid, &status, 0, &tr->rusage);
137a563ca70SAlex Hornung if (r_pid == -1) {
138a563ca70SAlex Hornung if (errno == EINTR) {
139a563ca70SAlex Hornung /* Alarm timed out */
140a563ca70SAlex Hornung tr->result = RESULT_TIMEOUT;
141a563ca70SAlex Hornung
142a563ca70SAlex Hornung /* Clean up child process! */
143a563ca70SAlex Hornung clean_child(pid);
144a563ca70SAlex Hornung } else if (errno == ECHILD) {
145a563ca70SAlex Hornung /* Child already exited somehow */
146a563ca70SAlex Hornung tr->result = RESULT_UNKNOWN;
147a563ca70SAlex Hornung } else {
148a563ca70SAlex Hornung /* EFAULT */
149a563ca70SAlex Hornung if (errbuf)
150a563ca70SAlex Hornung snprintf(errbuf, errbuf_sz, "Could not "
151a563ca70SAlex Hornung "wait4(): %s", strerror(errno));
152a563ca70SAlex Hornung
153a563ca70SAlex Hornung goto err_out;
154a563ca70SAlex Hornung }
155a563ca70SAlex Hornung } else {
156a563ca70SAlex Hornung if (WIFEXITED(status)) {
157*039caa27SAntonio Huete Jimenez tr->result = (WEXITSTATUS(status) == rc) ?
158a563ca70SAlex Hornung RESULT_PASS :
159a563ca70SAlex Hornung (WEXITSTATUS(status) == EXIT_NOTRUN) ?
160a563ca70SAlex Hornung RESULT_NOTRUN : RESULT_FAIL;
161a563ca70SAlex Hornung
162a563ca70SAlex Hornung tr->exit_value = WEXITSTATUS(status);
163a563ca70SAlex Hornung } else if (WIFSIGNALED(status)) {
164a563ca70SAlex Hornung tr->result = RESULT_SIGNALLED;
165a563ca70SAlex Hornung tr->signal = WTERMSIG(status);
166a563ca70SAlex Hornung tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0;
167a563ca70SAlex Hornung } else {
168a563ca70SAlex Hornung tr->result = RESULT_UNKNOWN;
169a563ca70SAlex Hornung }
170a563ca70SAlex Hornung }
171a563ca70SAlex Hornung
172a563ca70SAlex Hornung if (timeout != NULL) {
173a563ca70SAlex Hornung /* Disable timer */
174a563ca70SAlex Hornung itim.it_value.tv_sec = 0;
175a563ca70SAlex Hornung itim.it_value.tv_usec = 0;
176a563ca70SAlex Hornung setitimer(ITIMER_REAL, &itim, NULL);
177a563ca70SAlex Hornung }
178a563ca70SAlex Hornung } else {
179a563ca70SAlex Hornung /* pid == 0, so we are the child */
180a563ca70SAlex Hornung
181a563ca70SAlex Hornung /* Redirect stdout and stderr */
182a563ca70SAlex Hornung if (fd_stdout >= 0) {
183a563ca70SAlex Hornung dup2(fd_stdout, 1);
184a563ca70SAlex Hornung setvbuf(stdout, NULL, _IONBF, 0);
185a563ca70SAlex Hornung }
186a563ca70SAlex Hornung
187a563ca70SAlex Hornung if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) {
188a563ca70SAlex Hornung dup2((unify_output) ? fd_stdout : fd_stderr, 2);
189a563ca70SAlex Hornung setvbuf((unify_output) ? stdout : stderr,
190a563ca70SAlex Hornung NULL, _IONBF, 0);
191a563ca70SAlex Hornung }
192a563ca70SAlex Hornung
193a563ca70SAlex Hornung /* Set uid if necessary */
194a563ca70SAlex Hornung if (need_setuid) {
195a563ca70SAlex Hornung r = setuid(uid);
196a563ca70SAlex Hornung if (r == -1) {
197a563ca70SAlex Hornung fprintf(stderr, "ERR: NOT RUN (setuid): %s",
198a563ca70SAlex Hornung strerror(errno));
199a563ca70SAlex Hornung exit(EXIT_NOTRUN);
200a563ca70SAlex Hornung }
201a563ca70SAlex Hornung }
202a563ca70SAlex Hornung
2030d575305SAntonio Huete Jimenez if (interpreter) {
2040d575305SAntonio Huete Jimenez /*
2050d575305SAntonio Huete Jimenez * Allocate argc + 3 arguments more as shown below:
2060d575305SAntonio Huete Jimenez * argv_copy[0] = interpreter
2070d575305SAntonio Huete Jimenez * argv_copy[1] = argv[0]
2080d575305SAntonio Huete Jimenez * argv_copy[argc+2] = NULL
2090d575305SAntonio Huete Jimenez *
2100d575305SAntonio Huete Jimenez * execvp requires the array to end with NULL.
2110d575305SAntonio Huete Jimenez */
2120d575305SAntonio Huete Jimenez argv_copy = (char **)calloc(argc + 3, sizeof(char *));
2130d575305SAntonio Huete Jimenez if (argv_copy == NULL) {
2140d575305SAntonio Huete Jimenez err(1, "could not calloc argv_copy memory");
2150d575305SAntonio Huete Jimenez
2160d575305SAntonio Huete Jimenez }
2170d575305SAntonio Huete Jimenez /* Insert the interpreter at pos 0 */
2180d575305SAntonio Huete Jimenez argv_copy[0] = malloc(strlen(interpreter) + 1);
2190d575305SAntonio Huete Jimenez snprintf(argv_copy[0], strlen(interpreter) + 1, "%s",
2200d575305SAntonio Huete Jimenez interpreter);
2210d575305SAntonio Huete Jimenez
2220d575305SAntonio Huete Jimenez /* We still need argv[0] when argc is 0 */
2230d575305SAntonio Huete Jimenez for (int i = 0; i <= argc; i++) {
2240d575305SAntonio Huete Jimenez size_t len;
2250d575305SAntonio Huete Jimenez len = strlen(argv[i]) + 1; /* NULL-terminated */
2260d575305SAntonio Huete Jimenez
2270d575305SAntonio Huete Jimenez argv_copy[i + 1] = malloc(len);
2280d575305SAntonio Huete Jimenez if (argv_copy[i] == NULL)
2290d575305SAntonio Huete Jimenez err(1, "could not malloc memory");
2300d575305SAntonio Huete Jimenez
2310d575305SAntonio Huete Jimenez snprintf(argv_copy[i + 1], len, "%s",
2320d575305SAntonio Huete Jimenez argv[i]);
2330d575305SAntonio Huete Jimenez
2340d575305SAntonio Huete Jimenez }
2350d575305SAntonio Huete Jimenez /* Null terminate the array */
2360d575305SAntonio Huete Jimenez argv_copy[argc + 2] = NULL;
2370d575305SAntonio Huete Jimenez r = execvp(interpreter, argv_copy);
2380d575305SAntonio Huete Jimenez } else {
239a563ca70SAlex Hornung /* Try to exec() */
240a563ca70SAlex Hornung r = execvp(binary, __DECONST(char **, argv));
2410d575305SAntonio Huete Jimenez }
242a563ca70SAlex Hornung if (r == -1) {
243a563ca70SAlex Hornung /*
244a563ca70SAlex Hornung * If we couldn't exec(), notify parent that we didn't
245a563ca70SAlex Hornung * run.
246a563ca70SAlex Hornung */
247a563ca70SAlex Hornung fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno));
248a563ca70SAlex Hornung exit(EXIT_NOTRUN);
249a563ca70SAlex Hornung }
250a563ca70SAlex Hornung }
251a563ca70SAlex Hornung
252a563ca70SAlex Hornung /* Read stdout and stderr redirected file contents into memory */
253a563ca70SAlex Hornung sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END);
254a563ca70SAlex Hornung lseek(fd_stdout, 0, SEEK_SET);
255a563ca70SAlex Hornung
256a563ca70SAlex Hornung tr->stdout_buf = malloc(sz_stdout + 1);
257a563ca70SAlex Hornung if (tr->stdout_buf == NULL)
258a563ca70SAlex Hornung err(1, "could not malloc fd buf memory");
259a563ca70SAlex Hornung
260a563ca70SAlex Hornung read(fd_stdout, tr->stdout_buf, sz_stdout);
261a563ca70SAlex Hornung tr->stdout_buf[sz_stdout] = '\0';
262a563ca70SAlex Hornung
263a563ca70SAlex Hornung close(fd_stdout);
264a563ca70SAlex Hornung unlink(stdout_file);
265a563ca70SAlex Hornung
266a563ca70SAlex Hornung if (!unify_output) {
267a563ca70SAlex Hornung sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END);
268a563ca70SAlex Hornung lseek(fd_stderr, 0, SEEK_SET);
269a563ca70SAlex Hornung
270a563ca70SAlex Hornung tr->stderr_buf = malloc(sz_stderr + 1);
271a563ca70SAlex Hornung if (tr->stderr_buf == NULL)
272a563ca70SAlex Hornung err(1, "could not malloc fd buf memory");
273a563ca70SAlex Hornung
274a563ca70SAlex Hornung read(fd_stderr, tr->stderr_buf, sz_stderr);
275a563ca70SAlex Hornung tr->stderr_buf[sz_stderr] = '\0';
276a563ca70SAlex Hornung
277a563ca70SAlex Hornung close(fd_stderr);
278a563ca70SAlex Hornung unlink(stderr_file);
279a563ca70SAlex Hornung }
280a563ca70SAlex Hornung
281a563ca70SAlex Hornung
282a563ca70SAlex Hornung return 0;
283a563ca70SAlex Hornung /* NOTREACHED */
284a563ca70SAlex Hornung
285a563ca70SAlex Hornung err_out:
286a563ca70SAlex Hornung if (pid != -1)
287a563ca70SAlex Hornung clean_child(pid);
288a563ca70SAlex Hornung
28965e1e942SSascha Wildner if (fd_stdout >= 0) {
290a563ca70SAlex Hornung close(fd_stdout);
291a563ca70SAlex Hornung unlink(stdout_file);
29265e1e942SSascha Wildner }
293a563ca70SAlex Hornung
29465e1e942SSascha Wildner if (fd_stderr >= 0) {
295a563ca70SAlex Hornung close(fd_stderr);
296a563ca70SAlex Hornung unlink(stderr_file);
29765e1e942SSascha Wildner }
298a563ca70SAlex Hornung
299a563ca70SAlex Hornung return -1;
300a563ca70SAlex Hornung }
301a563ca70SAlex Hornung
302a563ca70SAlex Hornung int
run_simple_cmd(const char * binary,const char * arg,char * errbuf,size_t errbuf_sz,struct testcase_result * tr)303a563ca70SAlex Hornung run_simple_cmd(const char *binary, const char *arg, char *errbuf,
304a563ca70SAlex Hornung size_t errbuf_sz, struct testcase_result *tr)
305a563ca70SAlex Hornung {
306a563ca70SAlex Hornung const char *argv[3];
307a563ca70SAlex Hornung char *s;
308a563ca70SAlex Hornung
309a563ca70SAlex Hornung s = strrchr(binary, '/');
310a563ca70SAlex Hornung
311a563ca70SAlex Hornung argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
312a563ca70SAlex Hornung argv[1] = __DECONST(char *, arg);
313a563ca70SAlex Hornung argv[2] = NULL;
314a563ca70SAlex Hornung
315*039caa27SAntonio Huete Jimenez return run_userland(binary, /* executable */
316*039caa27SAntonio Huete Jimenez 1, /* argc */
317*039caa27SAntonio Huete Jimenez argv, /* argv */
318*039caa27SAntonio Huete Jimenez NULL, /* interpreter */
319*039caa27SAntonio Huete Jimenez 0, /* needs_setuid */
320*039caa27SAntonio Huete Jimenez 0, /* runas_uid */
321*039caa27SAntonio Huete Jimenez NULL, /* timeout */
322*039caa27SAntonio Huete Jimenez 0, /* rc */
323*039caa27SAntonio Huete Jimenez 1, /* unify_output */
324*039caa27SAntonio Huete Jimenez errbuf, /* errbuf */
325*039caa27SAntonio Huete Jimenez errbuf_sz, /* errbuf_size */
326*039caa27SAntonio Huete Jimenez tr); /* testcase_result */
327a563ca70SAlex Hornung }
328