Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / usr.bin / dfregress / userland.c
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         if (fd_stderr >= 0)
275                 close(fd_stderr);
276                 unlink(stderr_file);
277
278         return -1;
279 }
280
281 int
282 run_simple_cmd(const char *binary, const char *arg, char *errbuf,
283     size_t errbuf_sz, struct testcase_result *tr)
284 {
285         const char *argv[3];
286         char *s;
287
288         s = strrchr(binary, '/');
289
290         argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
291         argv[1] = __DECONST(char *, arg);
292         argv[2] = NULL;
293
294         return run_userland(binary, argv, 0, 0, NULL, 1, errbuf, errbuf_sz, tr);
295 }