xref: /dragonfly/usr.bin/dfregress/testcase.c (revision abf903a5)
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 	if (r == 0)
272 		err(1, "prop_dictionary operation failed");
273 
274 	return (uid_t)uid;
275 }
276 
277 const char *
278 testcase_get_custom_precmd(prop_dictionary_t testcase)
279 {
280 	const char *str;
281 	int r;
282 
283 	r = prop_dictionary_get_cstring_nocopy(
284 	    prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
285 	if (r == 0)
286 		err(1, "prop_dictionary operation failed");
287 
288 	return str;
289 }
290 
291 const char *
292 testcase_get_custom_postcmd(prop_dictionary_t testcase)
293 {
294 	const char *str;
295 	int r;
296 
297 	r = prop_dictionary_get_cstring_nocopy(
298 	    prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
299 	if (r == 0)
300 		err(1, "prop_dictionary operation failed");
301 
302 	return str;
303 }
304 
305 const char *
306 testcase_get_make_cmd(prop_dictionary_t testcase)
307 {
308 	const char *str;
309 	int r;
310 
311 	r = prop_dictionary_get_cstring_nocopy(
312 	    prop_dictionary_get(testcase, "opts"), "make_cmd", &str);
313 	if (r == 0)
314 		err(1, "prop_dictionary operation failed");
315 
316 	return str;
317 }
318 
319 prop_dictionary_t
320 testcase_get_result_dict(prop_dictionary_t testcase)
321 {
322 	prop_dictionary_t result_dict;
323 	int r;
324 
325 	result_dict = prop_dictionary_get(testcase, "result");
326 	if (result_dict == NULL) {
327 		result_dict = prop_dictionary_create();
328 		if (result_dict == NULL)
329 			err(1, "could not allocate new result dict");
330 
331 		r = prop_dictionary_set(testcase, "result", result_dict);
332 		if (r == 0)
333 			err(1, "prop_dictionary operation failed");
334 	}
335 
336 	return result_dict;
337 }
338 
339 int
340 testcase_set_build_buf(prop_dictionary_t testcase, const char *buf)
341 {
342 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
343 
344 	return !prop_dictionary_set_cstring(dict, "build_buf", buf);
345 }
346 
347 int
348 testcase_set_cleanup_buf(prop_dictionary_t testcase, const char *buf)
349 {
350 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
351 
352 	return !prop_dictionary_set_cstring(dict, "cleanup_buf", buf);
353 }
354 
355 int
356 testcase_set_sys_buf(prop_dictionary_t testcase, const char *buf)
357 {
358 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
359 
360 	return !prop_dictionary_set_cstring(dict, "sys_buf", buf);
361 }
362 
363 int
364 testcase_set_precmd_buf(prop_dictionary_t testcase, const char *buf)
365 {
366 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
367 
368 	return !prop_dictionary_set_cstring(dict, "precmd_buf", buf);
369 }
370 
371 int
372 testcase_set_postcmd_buf(prop_dictionary_t testcase, const char *buf)
373 {
374 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
375 
376 	return !prop_dictionary_set_cstring(dict, "postcmd_buf", buf);
377 }
378 
379 int
380 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
381 {
382 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
383 
384 	return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
385 }
386 
387 int
388 testcase_set_stderr_buf(prop_dictionary_t testcase, const char *buf)
389 {
390 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
391 
392 	return !prop_dictionary_set_cstring(dict, "stderr_buf", buf);
393 }
394 
395 int
396 testcase_set_result(prop_dictionary_t testcase, int result)
397 {
398 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
399 
400 	return !prop_dictionary_set_int32(dict, "result", result);
401 }
402 
403 int
404 testcase_set_exit_value(prop_dictionary_t testcase, int exitval)
405 {
406 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
407 
408 	return !prop_dictionary_set_int32(dict, "exit_value", exitval);
409 }
410 
411 int
412 testcase_set_signal(prop_dictionary_t testcase, int sig)
413 {
414 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
415 
416 	return !prop_dictionary_set_int32(dict, "signal", sig);
417 }
418 
419 const char *
420 testcase_get_build_buf(prop_dictionary_t testcase)
421 {
422 	const char *str = "";
423 
424 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
425 	prop_dictionary_get_cstring_nocopy(dict, "build_buf", &str);
426 
427 	return str;
428 }
429 
430 const char *
431 testcase_get_cleanup_buf(prop_dictionary_t testcase)
432 {
433 	const char *str = "";
434 
435 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
436 	prop_dictionary_get_cstring_nocopy(dict, "cleanup_buf", &str);
437 
438 	return str;
439 }
440 
441 const char *
442 testcase_get_sys_buf(prop_dictionary_t testcase)
443 {
444 	const char *str = "";
445 
446 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
447 	prop_dictionary_get_cstring_nocopy(dict, "sys_buf", &str);
448 
449 	return str;
450 }
451 
452 const char *
453 testcase_get_precmd_buf(prop_dictionary_t testcase)
454 {
455 	const char *str = "";
456 
457 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
458 	prop_dictionary_get_cstring_nocopy(dict, "precmd_buf", &str);
459 
460 	return str;
461 }
462 
463 const char *
464 testcase_get_postcmd_buf(prop_dictionary_t testcase)
465 {
466 	const char *str = "";
467 
468 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
469 	prop_dictionary_get_cstring_nocopy(dict, "postcmd_buf", &str);
470 
471 	return str;
472 }
473 
474 const char *
475 testcase_get_stdout_buf(prop_dictionary_t testcase)
476 {
477 	const char *str = "";
478 
479 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
480 	prop_dictionary_get_cstring_nocopy(dict, "stdout_buf", &str);
481 
482 	return str;
483 }
484 
485 const char *
486 testcase_get_stderr_buf(prop_dictionary_t testcase)
487 {
488 	const char *str = "";
489 
490 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
491 	prop_dictionary_get_cstring_nocopy(dict, "stderr_buf", &str);
492 
493 	return str;
494 }
495 
496 int
497 testcase_get_result(prop_dictionary_t testcase)
498 {
499 	int32_t result = RESULT_NOTRUN;
500 
501 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
502 	prop_dictionary_get_int32(dict, "result", &result);
503 
504 	return (int)result;
505 }
506 
507 const char *
508 testcase_get_result_desc(prop_dictionary_t testcase)
509 {
510 	int result = testcase_get_result(testcase);
511 
512 	switch(result) {
513 	case RESULT_TIMEOUT:	return "TIMEOUT";
514 	case RESULT_SIGNALLED:	return "SIGNALLED";
515 	case RESULT_NOTRUN:	return "NOT RUN";
516 	case RESULT_FAIL:	return "FAIL";
517 	case RESULT_PASS:	return "PASS";
518 	case RESULT_PREFAIL:	return "PREFAIL";
519 	case RESULT_POSTFAIL:	return "POSTFAIL";
520 	case RESULT_BUILDFAIL:	return "BUILDFAIL";
521 	default:		return "UNKNOWN";
522 	}
523 }
524 
525 int
526 testcase_get_exit_value(prop_dictionary_t testcase)
527 {
528 	int32_t exitval;
529 	int r;
530 
531 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
532 	r = prop_dictionary_get_int32(dict, "exit_value", &exitval);
533 	if (r == 0)
534 		err(1, "prop_dictionary operation failed");
535 
536 	return (int)exitval;
537 }
538 
539 int
540 testcase_get_signal(prop_dictionary_t testcase)
541 {
542 	int32_t sig;
543 	int r;
544 
545 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
546 	r = prop_dictionary_get_int32(dict, "signal", &sig);
547 	if (r == 0)
548 		err(1, "prop_dictionary operation failed");
549 
550 	return (int)sig;
551 }
552 
553 int
554 parse_testcase_option(struct testcase_options *opts, char *option)
555 {
556 	struct passwd *pwd;
557 	char	*parameter, *endptr;
558 	long	lval;
559 	int	noparam = 0;
560 
561 	parameter = strchr(option, '=');
562 	noparam = (parameter == NULL);
563 	if (!noparam)
564 	{
565 		*parameter = '\0';
566 		++parameter;
567 	}
568 
569 	if (strcmp(option, "timeout") == 0) {
570 		if (noparam)
571 			syntax_error("The option 'timeout' needs a parameter");
572 			/* NOTREACHED */
573 
574 		lval = strtol(parameter, &endptr, 10);
575 		if (*endptr != '\0')
576 			syntax_error("The option 'timeout' expects an integer "
577 			    "parameter, not '%s'", parameter);
578 			/* NOTREACHED */
579 
580 		opts->timeout_in_secs = (long int)lval;
581 	} else if (strcmp(option, "intpre") == 0) {
582 		opts->flags |= TESTCASE_INT_PRE;
583 	} else if (strcmp(option, "intpost") == 0) {
584 		opts->flags |= TESTCASE_INT_POST;
585 	} else if (strcmp(option, "pre") == 0) {
586 		if (noparam)
587 			syntax_error("The option 'pre' needs a parameter");
588 			/* NOTREACHED */
589 
590 		opts->flags |= TESTCASE_CUSTOM_PRE;
591 		opts->pre_cmd = strdup(parameter);
592 	} else if (strcmp(option, "post") == 0) {
593 		if (noparam)
594 			syntax_error("The option 'post' needs a parameter");
595 			/* NOTREACHED */
596 
597 		opts->flags |= TESTCASE_CUSTOM_POST;
598 		opts->post_cmd = strdup(parameter);
599 	} else if (strcmp(option, "runas") == 0) {
600 		if (noparam)
601 			syntax_error("The option 'runas' needs a parameter");
602 			/* NOTREACHED */
603 
604 		if ((pwd = getpwnam(parameter))) {
605 			opts->runas_uid = pwd->pw_uid;
606 			opts->flags |= TESTCASE_RUN_AS;
607 		} else {
608 			syntax_error("invalid user name for 'runas': %s",
609 			    parameter);
610 		}
611 	} else if (strcmp(option, "nobuild") == 0) {
612 		opts->flags |= TESTCASE_NOBUILD;
613 	} else if (strcmp(option, "make") == 0) {
614 		if (noparam)
615 			syntax_error("The option 'make' needs a parameter");
616 			/* NOTREACHED */
617 
618 		opts->make_cmd = strdup(parameter);
619 	} else if (strcmp(option, "defaults") == 0) {
620 		/* Valid option, does nothing */
621 	} else {
622 		syntax_error("Unknown option: %s", option);
623 		/* NOTREACHED */
624 	}
625 
626 	return 0;
627 }
628 
629 void
630 testcase_entry_parser(void *arg, char **tokens)
631 {
632 	prop_array_t runlist;
633 	prop_dictionary_t testcase_dict;
634 	struct testcase *testcase;
635 	char *options[256];
636 	int i, r, nopts;
637 
638 	runlist = (prop_array_t)arg;
639 
640 	testcase = malloc(sizeof(struct testcase));
641 	if (testcase == NULL)
642 		err(1, "could not malloc testcase memory");
643 
644 	bzero(testcase, sizeof(struct testcase));
645 
646 	entry_check_num_args(tokens, 3);
647 
648 	testcase->argv = &tokens[3];
649 	for (testcase->argc = 0; testcase->argv[testcase->argc] != NULL;
650 	     testcase->argc++)
651 		;
652 
653 	nopts = parse_options(tokens[2], options);
654 
655 	testcase->name = tokens[0];
656 
657 	if (strcmp(tokens[1], "userland") == 0) {
658 		testcase->type = TESTCASE_TYPE_USERLAND;
659 	} else if (strcmp(tokens[1], "kernel") == 0) {
660 		testcase->type = TESTCASE_TYPE_KERNEL;
661 	} else if (strcmp(tokens[1], "buildonly") == 0) {
662 		testcase->type = TESTCASE_TYPE_BUILDONLY;
663 	} else {
664 		syntax_error("Unknown type: %s", tokens[1]);
665 		/* NOTREACHED */
666 	}
667 
668 	testcase->type_str = tokens[1];
669 
670 	config_get_defaults(&testcase->opts);
671 
672 	for (i = 0; i < nopts; i++)
673 		parse_testcase_option(&testcase->opts, options[i]);
674 
675 	if ((testcase->type != TESTCASE_TYPE_USERLAND) &&
676 	    (testcase->opts.flags & (TESTCASE_INT_PRE | TESTCASE_INT_POST)))
677 		syntax_error("'intpre' and 'intpost' options are only valid "
678 		    "with testcase type 'userland'");
679 
680 	if ((testcase->type == TESTCASE_TYPE_BUILDONLY) &&
681 	    (testcase->opts.flags & TESTCASE_NOBUILD))
682 		syntax_error("'nobuild' option is incompatible with type "
683 		    "'buildonly'");
684 
685 	testcase_dict = testcase_from_struct(testcase);
686 	if (testcase->opts.pre_cmd != NULL)
687 		free(testcase->opts.pre_cmd);
688 	if (testcase->opts.post_cmd != NULL)
689 		free(testcase->opts.post_cmd);
690 	if (testcase->opts.make_cmd != NULL)
691 		free(testcase->opts.make_cmd);
692 	free(testcase);
693 
694 	r = prop_array_add(runlist, testcase_dict);
695 	if (r == 0)
696 		err(1, "prop_array_add failed");
697 }
698