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