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