xref: /dragonfly/usr.bin/dfregress/testcase.c (revision 89a89091)
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 <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stdint.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <pwd.h>
43 
44 #include <err.h>
45 
46 #include <libprop/proplib.h>
47 
48 #include "parser.h"
49 #include "testcase.h"
50 #include "runlist.h"
51 #include "config.h"
52 #include <dfregress.h>
53 
54 prop_dictionary_t
55 testcase_from_struct(struct testcase *testcase)
56 {
57 	int i, r;
58 	prop_dictionary_t dict, testcase_dict;
59 	prop_array_t a;
60 	char *s;
61 
62 	testcase_dict = prop_dictionary_create();
63 	if (testcase_dict == NULL)
64 		err(1, "could not create testcase dict");
65 	r = prop_dictionary_set_cstring(testcase_dict, "name", testcase->name);
66 	if (r == 0)
67 		err(1, "prop_dictionary operation failed");
68 	r = prop_dictionary_set_cstring(testcase_dict, "type", testcase->type_str);
69 	if (r == 0)
70 		err(1, "prop_dictionary operation failed");
71 
72 	a = prop_array_create_with_capacity(testcase->argc+1);
73 	if (a == NULL)
74 		err(1, "prop_array_create for argv failed");
75 
76 	s = strrchr(testcase->name, '/');
77 	r = prop_array_set_cstring(a, 0, (s == NULL) ? testcase->name : s+1);
78 	if (r == 0)
79 		err(1, "prop_array_set_cstring operation failed");
80 
81 	for (i = 1; i <= testcase->argc; i++) {
82 		r = prop_array_set_cstring(a, i, testcase->argv[i-1]);
83 		if (r == 0)
84 			err(1, "prop_array_set_cstring operation failed");
85 	}
86 
87 	r = prop_dictionary_set(testcase_dict, "args", a);
88 	if (r == 0)
89 		err(1, "prop_dictionary_set \"args\" failed");
90 
91 	dict = prop_dictionary_create();
92 	if (dict == NULL)
93 		err(1, "could not create dict");
94 
95 	r = prop_dictionary_set_int32(dict, "timeout_in_secs",
96 	    (int32_t)testcase->opts.timeout_in_secs);
97 	if (r == 0)
98 		err(1, "prop_dictionary operation failed");
99 
100 	r = prop_dictionary_set_uint32(dict, "flags", testcase->opts.flags);
101 	if (r == 0)
102 		err(1, "prop_dictionary operation failed");
103 
104 	if (testcase->opts.pre_cmd != NULL) {
105 		r = prop_dictionary_set_cstring(dict, "pre_cmd",
106 		    testcase->opts.pre_cmd);
107 		if (r == 0)
108 			err(1, "prop_dictionary operation failed");
109 	}
110 
111 	if (testcase->opts.post_cmd != NULL) {
112 		r = prop_dictionary_set_cstring(dict, "post_cmd",
113 		    testcase->opts.post_cmd);
114 		if (r == 0)
115 			err(1, "prop_dictionary operation failed");
116 	}
117 
118 	r = prop_dictionary_set_uint32(dict, "runas_uid",
119 	    (uint32_t)testcase->opts.runas_uid);
120 	if (r == 0)
121 		err(1, "prop_dictionary operation failed");
122 
123 	r = prop_dictionary_set_cstring(dict, "make_cmd",
124 	    (testcase->opts.make_cmd != NULL) ? testcase->opts.make_cmd : "make");
125 	if (r == 0)
126 		err(1, "prop_dictionary operation failed");
127 
128 	r = prop_dictionary_set(testcase_dict, "opts", dict);
129 	if (r == 0)
130 		err(1, "prop_dictionary operation failed");
131 
132 	return testcase_dict;
133 }
134 
135 struct timeval *
136 testcase_get_timeout(prop_dictionary_t testcase)
137 {
138 	static struct timeval tv;
139 	int32_t val;
140 	int r;
141 
142 	r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
143 	    "timeout_in_secs", &val);
144 	if (r == 0)
145 		err(1, "prop_dictionary operation failed");
146 
147 	tv.tv_usec = 0;
148 	tv.tv_sec = (long)val;
149 
150 	return &tv;
151 }
152 
153 int
154 testcase_get_type(prop_dictionary_t testcase)
155 {
156 	const char *type;
157 	int r;
158 
159 	r = prop_dictionary_get_cstring_nocopy(testcase, "type", &type);
160 	if (r == 0)
161 		err(1, "prop_dictionary operation failed");
162 
163 	if (strcmp(type, "userland") == 0)
164 		return TESTCASE_TYPE_USERLAND;
165 	else if (strcmp(type, "kernel") == 0)
166 		return TESTCASE_TYPE_KERNEL;
167 	else if (strcmp(type, "buildonly") == 0)
168 		return TESTCASE_TYPE_BUILDONLY;
169 
170 	return 0;
171 }
172 
173 const char *
174 testcase_get_type_desc(prop_dictionary_t testcase)
175 {
176 	const char *str;
177 	int r;
178 
179 	r = prop_dictionary_get_cstring_nocopy(testcase, "type", &str);
180 	if (r == 0)
181 		err(1, "prop_dictionary operation failed");
182 
183 	return str;
184 }
185 
186 const char *
187 testcase_get_name(prop_dictionary_t testcase)
188 {
189 	const char *str;
190 	int r;
191 
192 	r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
193 	if (r == 0)
194 		err(1, "prop_dictionary operation failed");
195 
196 	return str;
197 }
198 
199 const char **
200 testcase_get_args(prop_dictionary_t testcase)
201 {
202 	/* Sane limit of 63 arguments... who wants more than that? */
203 	static const char *argv[64];
204 	unsigned int i, count;
205 	prop_array_t a;
206 	int r;
207 
208 	a = prop_dictionary_get(testcase, "args");
209 	if (a == NULL)
210 		err(1, "testcase_get_args NULL array");
211 
212 	count = prop_array_count(a);
213 
214 	for (i = 0; i < count; i++) {
215 		r = prop_array_get_cstring_nocopy(a, i, &argv[i]);
216 		if (r == 0)
217 			err(1, "error building argv");
218 	}
219 
220 	argv[i] = NULL;
221 
222 	return argv;
223 }
224 
225 uint32_t
226 testcase_get_flags(prop_dictionary_t testcase)
227 {
228 	uint32_t flags;
229 	int r;
230 
231 	r = prop_dictionary_get_uint32(prop_dictionary_get(testcase, "opts"),
232 	    "flags", &flags);
233 	if (r == 0)
234 		err(1, "prop_dictionary operation failed");
235 
236 	return flags;
237 }
238 
239 int
240 testcase_get_precmd_type(prop_dictionary_t testcase)
241 {
242 	uint32_t flags = testcase_get_flags(testcase);
243 
244 	return (flags & (TESTCASE_INT_PRE | TESTCASE_CUSTOM_PRE));
245 }
246 
247 int
248 testcase_get_postcmd_type(prop_dictionary_t testcase)
249 {
250 	uint32_t flags = testcase_get_flags(testcase);
251 
252 	return (flags & (TESTCASE_INT_POST | TESTCASE_CUSTOM_POST));
253 }
254 
255 int
256 testcase_needs_setuid(prop_dictionary_t testcase)
257 {
258 	uint32_t flags = testcase_get_flags(testcase);
259 
260 	return (flags & TESTCASE_RUN_AS);
261 }
262 
263 uid_t
264 testcase_get_runas_uid(prop_dictionary_t testcase)
265 {
266 	uint32_t uid = 0;
267 	int r;
268 
269 	r = prop_dictionary_get_uint32(
270 	    prop_dictionary_get(testcase, "opts"), "runas_uid", &uid);
271 
272 	return (uid_t)uid;
273 }
274 
275 const char *
276 testcase_get_custom_precmd(prop_dictionary_t testcase)
277 {
278 	const char *str;
279 	int r;
280 
281 	r = prop_dictionary_get_cstring_nocopy(
282 	    prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
283 	if (r == 0)
284 		err(1, "prop_dictionary operation failed");
285 
286 	return str;
287 }
288 
289 const char *
290 testcase_get_custom_postcmd(prop_dictionary_t testcase)
291 {
292 	const char *str;
293 	int r;
294 
295 	r = prop_dictionary_get_cstring_nocopy(
296 	    prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
297 	if (r == 0)
298 		err(1, "prop_dictionary operation failed");
299 
300 	return str;
301 }
302 
303 const char *
304 testcase_get_make_cmd(prop_dictionary_t testcase)
305 {
306 	const char *str;
307 	int r;
308 
309 	r = prop_dictionary_get_cstring_nocopy(
310 	    prop_dictionary_get(testcase, "opts"), "make_cmd", &str);
311 	if (r == 0)
312 		err(1, "prop_dictionary operation failed");
313 
314 	return str;
315 }
316 
317 prop_dictionary_t
318 testcase_get_result_dict(prop_dictionary_t testcase)
319 {
320 	prop_dictionary_t result_dict;
321 	int r;
322 
323 	result_dict = prop_dictionary_get(testcase, "result");
324 	if (result_dict == NULL) {
325 		result_dict = prop_dictionary_create();
326 		if (result_dict == NULL)
327 			err(1, "could not allocate new result dict");
328 
329 		r = prop_dictionary_set(testcase, "result", result_dict);
330 		if (r == 0)
331 			err(1, "prop_dictionary operation failed");
332 	}
333 
334 	return result_dict;
335 }
336 
337 int
338 testcase_set_build_buf(prop_dictionary_t testcase, const char *buf)
339 {
340 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
341 
342 	return !prop_dictionary_set_cstring(dict, "build_buf", buf);
343 }
344 
345 int
346 testcase_set_cleanup_buf(prop_dictionary_t testcase, const char *buf)
347 {
348 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
349 
350 	return !prop_dictionary_set_cstring(dict, "cleanup_buf", buf);
351 }
352 
353 int
354 testcase_set_sys_buf(prop_dictionary_t testcase, const char *buf)
355 {
356 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
357 
358 	return !prop_dictionary_set_cstring(dict, "sys_buf", buf);
359 }
360 
361 int
362 testcase_set_precmd_buf(prop_dictionary_t testcase, const char *buf)
363 {
364 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
365 
366 	return !prop_dictionary_set_cstring(dict, "precmd_buf", buf);
367 }
368 
369 int
370 testcase_set_postcmd_buf(prop_dictionary_t testcase, const char *buf)
371 {
372 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
373 
374 	return !prop_dictionary_set_cstring(dict, "postcmd_buf", buf);
375 }
376 
377 int
378 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
379 {
380 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
381 
382 	return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
383 }
384 
385 int
386 testcase_set_stderr_buf(prop_dictionary_t testcase, const char *buf)
387 {
388 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
389 
390 	return !prop_dictionary_set_cstring(dict, "stderr_buf", buf);
391 }
392 
393 int
394 testcase_set_result(prop_dictionary_t testcase, int result)
395 {
396 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
397 
398 	return !prop_dictionary_set_int32(dict, "result", result);
399 }
400 
401 int
402 testcase_set_exit_value(prop_dictionary_t testcase, int exitval)
403 {
404 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
405 
406 	return !prop_dictionary_set_int32(dict, "exit_value", exitval);
407 }
408 
409 int
410 testcase_set_signal(prop_dictionary_t testcase, int sig)
411 {
412 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
413 
414 	return !prop_dictionary_set_int32(dict, "signal", sig);
415 }
416 
417 const char *
418 testcase_get_build_buf(prop_dictionary_t testcase)
419 {
420 	const char *str = "";
421 
422 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
423 	prop_dictionary_get_cstring_nocopy(dict, "build_buf", &str);
424 
425 	return str;
426 }
427 
428 const char *
429 testcase_get_cleanup_buf(prop_dictionary_t testcase)
430 {
431 	const char *str = "";
432 
433 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
434 	prop_dictionary_get_cstring_nocopy(dict, "cleanup_buf", &str);
435 
436 	return str;
437 }
438 
439 const char *
440 testcase_get_sys_buf(prop_dictionary_t testcase)
441 {
442 	const char *str = "";
443 
444 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
445 	prop_dictionary_get_cstring_nocopy(dict, "sys_buf", &str);
446 
447 	return str;
448 }
449 
450 const char *
451 testcase_get_precmd_buf(prop_dictionary_t testcase)
452 {
453 	const char *str = "";
454 
455 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
456 	prop_dictionary_get_cstring_nocopy(dict, "precmd_buf", &str);
457 
458 	return str;
459 }
460 
461 const char *
462 testcase_get_postcmd_buf(prop_dictionary_t testcase)
463 {
464 	const char *str = "";
465 
466 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
467 	prop_dictionary_get_cstring_nocopy(dict, "postcmd_buf", &str);
468 
469 	return str;
470 }
471 
472 const char *
473 testcase_get_stdout_buf(prop_dictionary_t testcase)
474 {
475 	const char *str = "";
476 
477 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
478 	prop_dictionary_get_cstring_nocopy(dict, "stdout_buf", &str);
479 
480 	return str;
481 }
482 
483 const char *
484 testcase_get_stderr_buf(prop_dictionary_t testcase)
485 {
486 	const char *str = "";
487 
488 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
489 	prop_dictionary_get_cstring_nocopy(dict, "stderr_buf", &str);
490 
491 	return str;
492 }
493 
494 int
495 testcase_get_result(prop_dictionary_t testcase)
496 {
497 	int32_t result = RESULT_NOTRUN;
498 
499 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
500 	prop_dictionary_get_int32(dict, "result", &result);
501 
502 	return (int)result;
503 }
504 
505 const char *
506 testcase_get_result_desc(prop_dictionary_t testcase)
507 {
508 	int result = testcase_get_result(testcase);
509 
510 	switch(result) {
511 	case RESULT_TIMEOUT:	return "TIMEOUT";
512 	case RESULT_SIGNALLED:	return "SIGNALLED";
513 	case RESULT_NOTRUN:	return "NOT RUN";
514 	case RESULT_FAIL:	return "FAIL";
515 	case RESULT_PASS:	return "PASS";
516 	case RESULT_PREFAIL:	return "PREFAIL";
517 	case RESULT_POSTFAIL:	return "POSTFAIL";
518 	case RESULT_BUILDFAIL:	return "BUILDFAIL";
519 	default:		return "UNKNOWN";
520 	}
521 }
522 
523 int
524 testcase_get_exit_value(prop_dictionary_t testcase)
525 {
526 	int32_t exitval;
527 	int r;
528 
529 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
530 	r = prop_dictionary_get_int32(dict, "exit_value", &exitval);
531 	if (r == 0)
532 		err(1, "prop_dictionary operation failed");
533 
534 	return (int)exitval;
535 }
536 
537 int
538 testcase_get_signal(prop_dictionary_t testcase)
539 {
540 	int32_t sig;
541 	int r;
542 
543 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
544 	r = prop_dictionary_get_int32(dict, "signal", &sig);
545 	if (r == 0)
546 		err(1, "prop_dictionary operation failed");
547 
548 	return (int)sig;
549 }
550 
551 int
552 parse_testcase_option(struct testcase_options *opts, char *option)
553 {
554 	struct passwd *pwd;
555 	char	*parameter, *endptr;
556 	long	lval;
557 	int	noparam = 0;
558 
559 	parameter = strchr(option, '=');
560 	noparam = (parameter == NULL);
561 	if (!noparam)
562 	{
563 		*parameter = '\0';
564 		++parameter;
565 	}
566 
567 	if (strcmp(option, "timeout") == 0) {
568 		if (noparam)
569 			syntax_error("The option 'timeout' needs a parameter");
570 			/* NOTREACHED */
571 
572 		lval = strtol(parameter, &endptr, 10);
573 		if (*endptr != '\0')
574 			syntax_error("The option 'timeout' expects an integer "
575 			    "parameter, not '%s'", parameter);
576 			/* NOTREACHED */
577 
578 		opts->timeout_in_secs = (long int)lval;
579 	} else if (strcmp(option, "intpre") == 0) {
580 		opts->flags |= TESTCASE_INT_PRE;
581 	} else if (strcmp(option, "intpost") == 0) {
582 		opts->flags |= TESTCASE_INT_POST;
583 	} else if (strcmp(option, "pre") == 0) {
584 		if (noparam)
585 			syntax_error("The option 'pre' needs a parameter");
586 			/* NOTREACHED */
587 
588 		opts->flags |= TESTCASE_CUSTOM_PRE;
589 		opts->pre_cmd = strdup(parameter);
590 	} else if (strcmp(option, "post") == 0) {
591 		if (noparam)
592 			syntax_error("The option 'post' needs a parameter");
593 			/* NOTREACHED */
594 
595 		opts->flags |= TESTCASE_CUSTOM_POST;
596 		opts->post_cmd = strdup(parameter);
597 	} else if (strcmp(option, "runas") == 0) {
598 		if (noparam)
599 			syntax_error("The option 'runas' needs a parameter");
600 			/* NOTREACHED */
601 
602 		if ((pwd = getpwnam(parameter))) {
603 			opts->runas_uid = pwd->pw_uid;
604 			opts->flags |= TESTCASE_RUN_AS;
605 		} else {
606 			syntax_error("invalid user name for 'runas': %s",
607 			    parameter);
608 		}
609 	} else if (strcmp(option, "nobuild") == 0) {
610 		opts->flags |= TESTCASE_NOBUILD;
611 	} else if (strcmp(option, "make") == 0) {
612 		if (noparam)
613 			syntax_error("The option 'make' needs a parameter");
614 			/* NOTREACHED */
615 
616 		opts->make_cmd = strdup(parameter);
617 	} else if (strcmp(option, "defaults") == 0) {
618 		/* Valid option, does nothing */
619 	} else {
620 		syntax_error("Unknown option: %s", option);
621 		/* NOTREACHED */
622 	}
623 
624 	return 0;
625 }
626 
627 void
628 testcase_entry_parser(void *arg, char **tokens)
629 {
630 	prop_array_t runlist;
631 	prop_dictionary_t testcase_dict;
632 	struct testcase *testcase;
633 	char *options[256];
634 	int i, r, nopts;
635 
636 	runlist = (prop_array_t)arg;
637 
638 	testcase = malloc(sizeof(struct testcase));
639 	if (testcase == NULL)
640 		err(1, "could not malloc testcase memory");
641 
642 	bzero(testcase, sizeof(struct testcase));
643 
644 	entry_check_num_args(tokens, 3);
645 
646 	testcase->argv = &tokens[3];
647 	for (testcase->argc = 0; testcase->argv[testcase->argc] != NULL;
648 	     testcase->argc++)
649 		;
650 
651 	nopts = parse_options(tokens[2], options);
652 
653 	testcase->name = tokens[0];
654 
655 	if (strcmp(tokens[1], "userland") == 0) {
656 		testcase->type = TESTCASE_TYPE_USERLAND;
657 	} else if (strcmp(tokens[1], "kernel") == 0) {
658 		testcase->type = TESTCASE_TYPE_KERNEL;
659 	} else if (strcmp(tokens[1], "buildonly") == 0) {
660 		testcase->type = TESTCASE_TYPE_BUILDONLY;
661 	} else {
662 		syntax_error("Unknown type: %s", tokens[1]);
663 		/* NOTREACHED */
664 	}
665 
666 	testcase->type_str = tokens[1];
667 
668 	config_get_defaults(&testcase->opts);
669 
670 	for (i = 0; i < nopts; i++)
671 		parse_testcase_option(&testcase->opts, options[i]);
672 
673 	if ((testcase->type != TESTCASE_TYPE_USERLAND) &&
674 	    (testcase->opts.flags & (TESTCASE_INT_PRE | TESTCASE_INT_POST)))
675 		syntax_error("'intpre' and 'intpost' options are only valid "
676 		    "with testcase type 'userland'");
677 
678 	if ((testcase->type == TESTCASE_TYPE_BUILDONLY) &&
679 	    (testcase->opts.flags & TESTCASE_NOBUILD))
680 		syntax_error("'nobuild' option is incompatible with type "
681 		    "'buildonly'");
682 
683 	testcase_dict = testcase_from_struct(testcase);
684 	if (testcase->opts.pre_cmd != NULL)
685 		free(testcase->opts.pre_cmd);
686 	if (testcase->opts.post_cmd != NULL)
687 		free(testcase->opts.post_cmd);
688 	if (testcase->opts.make_cmd != NULL)
689 		free(testcase->opts.make_cmd);
690 	free(testcase);
691 
692 	r = prop_array_add(runlist, testcase_dict);
693 	if (r == 0)
694 		err(1, "prop_array_add failed");
695 }
696