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