dfregress(8): Check prop_dictionary_get_uint32()'s return value.
[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 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 }