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