xref: /dragonfly/usr.bin/dfregress/userland.c (revision 0ca59c34)
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, const char **argv, int need_setuid, uid_t uid,
68     struct timeval *timeout, int unify_output, char *errbuf, size_t errbuf_sz,
69     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 *str;
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 	str = mktemp(stdout_file);
88 	if (str == NULL) {
89 		if (errbuf)
90 			snprintf(errbuf, errbuf_sz, "Could not mktemp(): "
91 			    "%s\n", strerror(errno));
92 		return -1;
93 	}
94 
95 	if (!unify_output) {
96 		str = mktemp(stderr_file);
97 		if (str == NULL) {
98 			if (errbuf)
99 				snprintf(errbuf, errbuf_sz, "Could not mktemp(): "
100 				"%s\n", strerror(errno));
101 			return -1;
102 		}
103 	}
104 
105 	fd_stdout = open(stdout_file, O_RDWR | O_CREAT | O_FSYNC);
106 	if (fd_stdout < 0) {
107 		if (errbuf)
108 			snprintf(errbuf, errbuf_sz, "Could not open() temp file "
109 			    "for stdout: %s\n", strerror(errno));
110 		goto err_out;
111 	}
112 
113 	if (!unify_output) {
114 		fd_stderr = open(stderr_file, O_RDWR | O_CREAT | O_FSYNC);
115 		if (fd_stderr < 0) {
116 			if (errbuf)
117 				snprintf(errbuf, errbuf_sz, "Could not open() "
118 				"temp file for stderr: %s\n", strerror(errno));
119 			goto err_out;
120 		}
121 	}
122 
123 
124 	if ((pid = fork()) == -1) {
125 		if (errbuf)
126 			snprintf(errbuf, errbuf_sz, "Could not fork to run "
127 			    "binary %s: %s\n", binary, strerror(errno));
128 
129 		goto err_out;
130 	} else if (pid > 0) {
131 		/* parent */
132 
133 		if (timeout != NULL) {
134 			/* Ignore SIGALRM */
135 			bzero(&sa, sizeof(sa));
136 			sa.sa_handler = sig_handle;
137 			sigaction(SIGALRM, &sa, NULL);
138 
139 			/* Set up timeout */
140 			itim.it_interval.tv_sec = 0;
141 			itim.it_interval.tv_usec = 0;
142 			itim.it_value = *timeout;
143 			r = setitimer(ITIMER_REAL, &itim, NULL);
144 			if (r == -1) {
145 				if (errbuf)
146 					snprintf(errbuf, errbuf_sz, "Could not "
147 					    "set up timer: %s", strerror(errno));
148 
149 				/* Clean up child process! */
150 				goto err_out;
151 			}
152 		}
153 
154 		r_pid = wait4(pid, &status, 0, &tr->rusage);
155 		if (r_pid == -1) {
156 			if (errno == EINTR) {
157 				/* Alarm timed out */
158 				tr->result = RESULT_TIMEOUT;
159 
160 				/* Clean up child process! */
161 				clean_child(pid);
162 			} else if (errno == ECHILD) {
163 				/* Child already exited somehow */
164 				tr->result = RESULT_UNKNOWN;
165 			} else {
166 				/* EFAULT */
167 				if (errbuf)
168 					snprintf(errbuf, errbuf_sz, "Could not "
169 					    "wait4(): %s", strerror(errno));
170 
171 				goto err_out;
172 			}
173 		} else {
174 			if (WIFEXITED(status)) {
175 				tr->result = (WEXITSTATUS(status) == 0) ?
176 				    RESULT_PASS :
177 				    (WEXITSTATUS(status) == EXIT_NOTRUN) ?
178 				    RESULT_NOTRUN : RESULT_FAIL;
179 
180 				tr->exit_value = WEXITSTATUS(status);
181 			} else if (WIFSIGNALED(status)) {
182 				tr->result = RESULT_SIGNALLED;
183 				tr->signal = WTERMSIG(status);
184 				tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0;
185 			} else {
186 				tr->result = RESULT_UNKNOWN;
187 			}
188 		}
189 
190 		if (timeout != NULL) {
191 			/* Disable timer */
192 			itim.it_value.tv_sec = 0;
193 			itim.it_value.tv_usec = 0;
194 			setitimer(ITIMER_REAL, &itim, NULL);
195 		}
196 	} else {
197 		/* pid == 0, so we are the child */
198 
199 		/* Redirect stdout and stderr */
200 		if (fd_stdout >= 0) {
201 			dup2(fd_stdout, 1);
202 			setvbuf(stdout, NULL, _IONBF, 0);
203 		}
204 
205 		if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) {
206 			dup2((unify_output) ? fd_stdout : fd_stderr, 2);
207 			setvbuf((unify_output) ? stdout : stderr,
208 			    NULL, _IONBF, 0);
209 		}
210 
211 		/* Set uid if necessary */
212 		if (need_setuid) {
213 			r = setuid(uid);
214 			if (r == -1) {
215 				fprintf(stderr, "ERR: NOT RUN (setuid): %s",
216 				    strerror(errno));
217 				exit(EXIT_NOTRUN);
218 			}
219 		}
220 
221 		/* Try to exec() */
222 		r = execvp(binary, __DECONST(char **, argv));
223 		if (r == -1) {
224 			/*
225 			 * If we couldn't exec(), notify parent that we didn't
226 			 * run.
227 			 */
228 			fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno));
229 			exit(EXIT_NOTRUN);
230 		}
231 	}
232 
233 	/* Read stdout and stderr redirected file contents into memory */
234 	sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END);
235 	lseek(fd_stdout, 0, SEEK_SET);
236 
237 	tr->stdout_buf = malloc(sz_stdout + 1);
238 	if (tr->stdout_buf == NULL)
239 		err(1, "could not malloc fd buf memory");
240 
241 	read(fd_stdout, tr->stdout_buf, sz_stdout);
242 	tr->stdout_buf[sz_stdout] = '\0';
243 
244 	close(fd_stdout);
245 	unlink(stdout_file);
246 
247 	if (!unify_output) {
248 		sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END);
249 		lseek(fd_stderr, 0, SEEK_SET);
250 
251 		tr->stderr_buf = malloc(sz_stderr + 1);
252 		if (tr->stderr_buf == NULL)
253 			err(1, "could not malloc fd buf memory");
254 
255 		read(fd_stderr, tr->stderr_buf, sz_stderr);
256 		tr->stderr_buf[sz_stderr] = '\0';
257 
258 		close(fd_stderr);
259 		unlink(stderr_file);
260 	}
261 
262 
263 	return 0;
264 	/* NOTREACHED */
265 
266 err_out:
267 	if (pid != -1)
268 		clean_child(pid);
269 
270 	if (fd_stdout >= 0) {
271 		close(fd_stdout);
272 		unlink(stdout_file);
273 	}
274 
275 	if (fd_stderr >= 0) {
276 		close(fd_stderr);
277 		unlink(stderr_file);
278 	}
279 
280 	return -1;
281 }
282 
283 int
284 run_simple_cmd(const char *binary, const char *arg, char *errbuf,
285     size_t errbuf_sz, struct testcase_result *tr)
286 {
287 	const char *argv[3];
288 	char *s;
289 
290 	s = strrchr(binary, '/');
291 
292 	argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
293 	argv[1] = __DECONST(char *, arg);
294 	argv[2] = NULL;
295 
296 	return run_userland(binary, argv, 0, 0, NULL, 1, errbuf, errbuf_sz, tr);
297 }
298