b9435340ba8d312458f36e8e7871298ec5d95824
[dragonfly.git] / usr.bin / dfregress / testcase.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 <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 __unused 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 }