1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 1995 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * The purpose of this lex specification is to estimate the 30 * correctness of the various scripts that accompany packages. It 31 * is not flawless, but it is a better review than that of prior 32 * package validators. It looks for indications of interaction, 33 * root calls and attempts to modify locked files. 34 */ 35 %e 1500 36 %p 3500 37 %s WHROOT 38 %{ 39 #undef input 40 #undef unput 41 FILE *scr_fp; 42 #define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(scr_fp))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar) 43 #define unput(p) ungetc(p, scr_fp) 44 45 #define INTERACT_D 0x00000001 /* definitely */ 46 #define ROOT_D 0x00000002 47 #define LOCKED_D 0x00000004 48 #define INTERACT_M 0x00010000 /* might be true, or we ... */ 49 #define ROOT_M 0x00020000 /* ... might be reading it wrong. */ 50 #define LOCKED_M 0x00040000 51 #define WPARM1_M 0x00080000 /* attempt to write to $1 */ 52 #define USEPARM1_M 0x00100000 /* other attempt to use $1 */ 53 #define ODDPARM_M 0x00200000 /* use of some other parameter */ 54 #define PKGDB_M 0x00400000 /* read access to DB */ 55 #define INITVAL 0x40000000 56 57 /* Abbreviations */ 58 #define INTERACT (INTERACT_D | INTERACT_M) 59 #define ROOT (ROOT_D | ROOT_M) 60 #define LOCKED (LOCKED_D | LOCKED_M) 61 #define HASPARM (WPARM1_M | USEPARM1_M | ODDPARM_M) 62 63 /* Things the preinstall and preremove scripts can't do. */ 64 #define PRE_MASK (INTERACT | LOCKED | PKGDB_M | HASPARM) 65 /* 66 * Things the class action script can't do. Don't get the impression that 67 * this means the class action script can be interactive; but, it can 68 * legitimately read stdin (which is what INTERACT tests for). 69 */ 70 #define CAS_MASK (LOCKED | PKGDB_M | WPARM1_M | ODDPARM_M) 71 /* Things the postinstall and postremove scripts can't do. */ 72 #define POST_MASK (INTERACT | HASPARM) 73 /* Things the request script can't do. */ 74 #define REQ_MASK (ROOT | ODDPARM_M) 75 /* Things the checkinstall script can't do. */ 76 #define CHK_MASK (INTERACT | ROOT | ODDPARM_M) 77 78 /* Nothing definite - not worth returning an error */ 79 #define MAYBE_ONLY ~(INTERACT_D | ROOT_D | LOCKED_D) 80 81 #define WRN_INST_F "WARNING: script <%s> uses installf but no " \ 82 "installf -f was detected." 83 #define WRN_REM_F "WARNING: script <%s> uses removef but no " \ 84 "removef -f was detected." 85 #define WRN_INTERACT "WARNING: script <%s> may require " \ 86 "user interaction at line <%d>." 87 #define WRN_LOCKED "WARNING: script <%s> may seek access to the " \ 88 "transitional package database at line <%d>. " \ 89 "This is safest in the postinstall or " \ 90 "postremove script." 91 #define WRN_ROOT "WARNING: script <%s> may not have permission " \ 92 "to execute line <%d>." 93 #define WRN_FORM_ARG "WARNING: not sure where script <%s> gets the "\ 94 "parameter at line <%d>." 95 #define WRN_FORM_USE "WARNING: script <%s> questionable usage of "\ 96 "parameter at line <%d>." 97 #define WRN_TRANSDB "WARNING: script <%s> questionable read " \ 98 "of package database at line <%d>. An " \ 99 "intermediate buffer may be appropriate." 100 #define WRN_SPACEACC "WARNING: script <%s> updates the package database " \ 101 "but provides no space file to account for " \ 102 "the additional package object." 103 #define ERR_INTERACT "ERROR: script <%s> requires user " \ 104 "interaction at line <%d>." 105 #define ERR_LOCKED "ERROR: script <%s> attempts to modify locked " \ 106 "package database at line <%d>." 107 #define ERR_ROOT "ERROR: script <%s> requires root permission at " \ 108 "line <%d>." 109 #define ERR_FOPEN "ERROR: Cannot evaluate script <%s>, errno=%d." 110 #define ERR_ARGS "ERROR: scripteval() - no script provided for " \ 111 "evaluation." 112 extern int errno; 113 114 static int line_no; /* current line number */ 115 int pipe_release = 0; /* loop level for release of pipe */ 116 int loop_depth = 0; /* current number of nested loops */ 117 int case_depth = 0; /* same for case ... */ 118 int if_depth = 0; /* ... and if statements */ 119 int cur_level = 0; /* current number of nested anything */ 120 int braces = 0; /* depth into a function */ 121 122 int lock_level = 0; 123 int root_level = 0; 124 125 struct statstrct { 126 unsigned int in_function:1; 127 unsigned int in_pipe:1; 128 unsigned int in_loop:1; 129 unsigned int in_case:1; 130 unsigned int in_if:1; 131 unsigned int in_awk:1; 132 unsigned int allow_int:1; /* Allow an interactive function. */ 133 unsigned int pkg_rtn_done:1; 134 unsigned int pkgchk_f:1; 135 unsigned int instf:1; 136 unsigned int instf_f:1; 137 unsigned int remf:1; 138 unsigned int remf_f:1; 139 unsigned int nospacefile:1; 140 unsigned int needspacefile:1; 141 } status = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 142 143 %} 144 %% 145 %{ 146 /* 147 * Validate a few OK patterns that look like bad patterns. These include: 148 * 1. comments 149 * 2. quoted strings 150 * 3. writes to $1 (request script) 151 * 4. reads from $1 (CAS) 152 * 5. writes to /dev/null 153 */ 154 %} 155 #.*$ return INITVAL; 156 157 \` unput(' '); /* No executable matching */ 158 159 %{ 160 /* Anybody can write to /dev/null and anybody can write to /tmp. */ 161 %} 162 \>[ \t]*"/dev/null" return INITVAL; 163 \>[ \t]*"/tmp" return INITVAL; 164 165 %{ 166 /* If it's escaped, the next entry may as well be a space. */ 167 %} 168 \\ { 169 char ch; 170 171 if ((ch = input()) == '\n') 172 line_no++; 173 174 unput(' '); 175 } 176 177 %{ 178 /* In the quotes is OK. */ 179 %} 180 \" { 181 char ch; 182 while ((ch = input()) != '\"') { 183 if (ch == '\\') { 184 input(); /* Read this into the bit bucket. */ 185 continue; 186 } 187 if (ch == '\n') 188 line_no++; 189 else if (ch == '\0') 190 return (0); /* EOF */ 191 } 192 } 193 194 %{ 195 /* In the single quotes is OK if they aren't associated with an awk script. */ 196 %} 197 \' { 198 char ch; 199 200 if (status.in_awk != 0) 201 REJECT;; 202 203 while ((ch = input()) != '\'') { 204 if (ch == '\\') { 205 input(); /* Read this into the bit bucket. */ 206 continue; 207 } 208 if (ch == '\n') 209 line_no++; 210 else if (ch == '\0') 211 return (0); /* EOF */ 212 } 213 } 214 215 %{ 216 /* 217 * Check for use of parameters passed to the script. 218 * 1. writes to $1 as though it were a file 219 * 2. use of $1 in any capacity 220 * 3. use of other parameters 221 * Within a function or an awk script, these parameters aren't 222 * the one's of interest. 223 */ 224 %} 225 \>[\t ]*\$1/[\t\n ] { 226 if (status.in_function == 0 && status.in_awk == 0) 227 return (WPARM1_M); 228 } 229 230 ^$1/[\t\n ] | 231 [\t ]$1/[\t\n ] { 232 if (status.in_function == 0 && status.in_awk == 0) 233 return (USEPARM1_M); 234 } 235 236 \$[2-9] | 237 \$[0-9][0-9]+ { 238 if (status.in_function == 0 && status.in_awk == 0) 239 return (ODDPARM_M); 240 } 241 242 %{ 243 /* 244 * Detect shell function. 245 */ 246 %} 247 "()"[ \t]*\n[ \t]*/\{ { status.in_function = 1; line_no++; } 248 "()"[ ]*/\{ status.in_function = 1; 249 250 "{" { 251 if (status.in_function == 1) 252 braces++; 253 } 254 255 "}" { 256 if (status.in_function == 1) { 257 braces--; 258 if (braces == 0) 259 status.in_function = 0; 260 } 261 } 262 263 %{ 264 /* 265 * Detect for or while loop. 266 */ 267 %} 268 ^for/[\t\n ] | 269 [\t ]for/[\t\n ] | 270 ^while/[\t\n ] | 271 [\t ]while/[\t\n ] { 272 status.in_loop = 1; 273 loop_depth++; 274 cur_level++; 275 REJECT; /* What's in the argument is important too. */ 276 } 277 278 ^done/[\t\n ] | 279 [\t ]done/[\t\n ] { 280 if (status.in_loop == 1) 281 loop_depth--; 282 cur_level--; 283 if (loop_depth == 0) 284 status.in_loop = 0; 285 } 286 287 %{ 288 /* 289 * Detect case. 290 */ 291 %} 292 ^case/[\t\n ] | 293 [\t ]case/[\t\n ] { 294 status.in_case = 1; 295 case_depth++; 296 cur_level++; 297 REJECT; /* What's in the argument is important too. */ 298 } 299 300 ^esac/[\t\n ] | 301 [\t ]esac/[\t\n ] { 302 if (status.in_case == 1) 303 case_depth--; 304 cur_level--; 305 if (case_depth == 0) 306 status.in_case = 0; 307 } 308 309 %{ 310 /* 311 * Detect if. 312 */ 313 %} 314 ^if" "*"[" | 315 [\t ]if" "*"[" { 316 status.in_if = 1; 317 if_depth++; 318 cur_level++; 319 REJECT; /* What's in the argument is important too. */ 320 } 321 322 ^fi/[\t\n ] | 323 [\t ]fi/[\t\n ] { 324 if (status.in_if == 1) 325 if_depth--; 326 cur_level--; 327 if (if_depth == 0) 328 status.in_if = 0; 329 } 330 331 %{ 332 /* 333 * Detect awk or nawk function. If the function is enclosed in "`"s 334 * the entire line will be grabbed., so we check for that possibility. 335 */ 336 %} 337 ^n?awk[^\n^']*\' | 338 [\t \\\(\/]n?awk[^\n^']*\' status.in_awk = 1; 339 340 341 \' { 342 if (status.in_awk == 1) 343 status.in_awk = 0; 344 } 345 346 %{ 347 /* Detect pipe target. */ 348 %} 349 [\$A-Za-z] { 350 if (status.in_pipe == 1 && pipe_release == cur_level) 351 { 352 status.in_pipe = 0; /* target located */ 353 pipe_release = 0; 354 status.allow_int = 1; /* this isn't really interactive. */ 355 REJECT; /* put it back */ 356 } 357 } 358 359 %{ 360 /* If it's a pipe, note that and continue. */ 361 %} 362 "||" | 363 "|" { 364 if (status.in_pipe == 0) { 365 status.in_pipe = 1; 366 pipe_release = cur_level; 367 } 368 } 369 370 %{ 371 /* 372 * Test input for admin-type telltale interactive functions. Definite's 373 * first, maybe's next. 374 */ 375 %} 376 ^ckdate/[\t\n ] | 377 [\t \/]ckdate/[\t\n ] | 378 ^ckint/[\t\n ] | 379 [\t \/]ckint/[\t\n ] | 380 ^ckrange/[\t\n ] | 381 [\t \/]ckrange/[\t\n ] | 382 ^cktime/[\t\n ] | 383 [\t \/]cktime/[\t\n ] | 384 ^ckyorn/[\t\n ] | 385 [\t \/]ckyorn/[\t\n ] | 386 ^ckgid/[\t\n ] | 387 [\t \/]ckgid/[\t\n ] | 388 ^ckpath/[\t\n ] | 389 [\t \/]ckpath/[\t\n ] | 390 ^ckstr/[\t\n ] | 391 [\t \/]ckstr/[\t\n ] | 392 ^ckuid/[\t\n ] | 393 [\t \/]ckuid/[\t\n ] { 394 if (status.in_pipe == 1 || status.allow_int == 1) 395 return (INITVAL); 396 else 397 return (INTERACT_M); /* maybe should be _D */ 398 } 399 400 ^read/[\t\n ] | 401 [\t ]read/[\t\n ] | 402 "=[ ]+&<"[\t ] { 403 if (status.in_pipe == 1 || status.allow_int == 1) 404 return (INITVAL); 405 else 406 return (INTERACT_M); 407 } 408 409 %{ 410 /* Scan for root authority commands. Definite's first, maybe's next. */ 411 %} 412 ^mkdir/[\t\n ] | 413 [\t \/]mkdir/[\t\n ] | 414 ^mv/[\t\n ] | 415 [\t \/]mv/[\t\n ] | 416 ^cpio/[\t\n ] | 417 [\t \/]cpio/[\t\n ] | 418 ^tar/[\t\n ] | 419 [\t \/]tar/[\t\n ] | 420 ^(un)?compress/[\t\n ] | 421 [\t \/](un)?compress/[\t\n ] | 422 ^rmdir/[\t\n ] | 423 [\t \/]rmdir/[\t\n ] return (ROOT_D); 424 425 ^r?cp(dir)?/[\t\n ] | 426 [\t \/]r?cp(dir)?/[\t\n ] | 427 ^rm/[\t\n ] | 428 [\t \/]rm/[\t\n ] | 429 \>[ \t]*[\$\/a-zA-Z0-9] return (ROOT_M); 430 431 %{ 432 /* These root commands may also be locked. */ 433 434 /* Here we analyze any pkgchk calls. If it's "pkgchk ... -f ..." then that calls for root authority. We then check for a "-R" argument. */ 435 %} 436 ^pkgchk[^\n^|^>^;]*"-f" | 437 [\t \/]pkgchk[^\n^|^>^;]*"-f" { 438 status.pkgchk_f = 1; 439 REJECT; /* We need the intermediate args. */ 440 } 441 442 %{ 443 /* If it's "pkgchk ... -R ..." then the local package database is not being tested and no database warning is necessary. */ 444 %} 445 ^pkgchk[^\n^|^>^;]*"-R"[ \t][\/\$]/[^ ^\t^\n] | 446 [\t \/]pkgchk[^\n^|^>^;]*"-R"[ \t][\/\$]/[^ ^\t^\n] { 447 if (status.pkgchk_f) 448 return (ROOT_D); 449 else 450 return (INITVAL); 451 } 452 453 %{ 454 /* If it's just "pkgchk ..." then we need to mention something about access to the package database. With Solaris 2.5, an improved locking mechanism is in place, so this message may be something we can drop later. */ 455 %} 456 ^pkgchk/[\t\n ] | 457 [\t \/]pkgchk/[\t\n ] { 458 if (status.pkgchk_f) { 459 status.pkgchk_f = 0; 460 return (ROOT_D | PKGDB_M); 461 } else 462 return (PKGDB_M); 463 } 464 465 %{ 466 /* The installf and removef utilities require root authority, they modify the package database and they must be invoked at least once with a "-f" argument. */ 467 468 /* First test for a "-f" argument. */ 469 %} 470 ^installf[^\n^|^>^;]*"-f" | 471 [\t \/]installf[^\n^|^>^;]*"-f" { 472 status.instf_f = 1; 473 474 REJECT; /* The whole line needs to be re-reviewed. */ 475 } 476 477 ^removef[^\n^|^>^;]*"-f" | 478 [\t \/]removef[^\n^|^>^;]*"-f" { 479 status.remf_f = 1; 480 481 REJECT; /* The whole line needs to be re-reviewed. */ 482 } 483 484 ^installf/[\t\n ] | 485 [\t \/]installf/[\t\n ] { 486 status.instf = 1; 487 status.needspacefile = 1; 488 489 root_level = ROOT_D; 490 lock_level = LOCKED_M; 491 492 BEGIN WHROOT; 493 } 494 495 ^removef/[\t\n ] | 496 [\t \/]removef/[\t\n ] { 497 status.remf = 1; 498 499 root_level = ROOT_D; 500 lock_level = LOCKED_M; 501 BEGIN WHROOT; 502 } 503 504 %{ 505 /* There's no question that use of a pkgadd or pkgrm in a script is bound to cause problems unless it is to a different root. */ 506 %} 507 ^pkgadd/[\t\n ] | 508 [\t \/]pkgadd/[\t\n ] | 509 ^pkgrm/[\t\n ] | 510 [\t \/]pkgrm/[\t\n ] { 511 root_level = ROOT_D; 512 lock_level = LOCKED_D; 513 BEGIN WHROOT; 514 } 515 516 %{ 517 /* The only way to get here is if we are in the middle of a pkg command. */ 518 %} 519 <WHROOT>. { 520 if (status.pkg_rtn_done) { 521 status.pkg_rtn_done = 0; 522 BEGIN 0; 523 } else 524 REJECT; 525 } 526 <WHROOT>[ \t]+"-R"[ \t][\/\$]/[^ ^\t^\n] { 527 status.pkg_rtn_done = 1; 528 return (root_level); /* "-R" means locking is unlikely. */ 529 } 530 <WHROOT>[\n] { 531 if (status.pkg_rtn_done) { 532 status.pkg_rtn_done = 0; 533 line_no++; 534 BEGIN 0; 535 } else { 536 status.pkg_rtn_done = 1; 537 unput('\n'); 538 return (root_level | lock_level); /* No "-R". */ 539 } 540 } 541 <WHROOT>[;|>] { 542 status.pkg_rtn_done = 1; 543 return (root_level | lock_level); /* End of command without a "-R". */ 544 } 545 546 \n { line_no++; status.allow_int = 0; } 547 . status.allow_int = 0; 548 %% 549 #include <stdio.h> 550 #include <limits.h> 551 #include <dirent.h> 552 #include <unistd.h> 553 #include <libintl.h> 554 555 #ifdef DEBUG 556 /* 557 * Since this is a lex specification twice removed from the binary, 558 * I strongly recommend leaving the DEBUG portions in place. When new 559 * keywords are added, this will be very important. After modifying 560 * the specification, create an executable to test in this way. 561 * 562 * lex scriptvfy.l 563 * cc -o scriptvfy -g lex.yy.c $ROOT/usr/lib/libpkg.a \ 564 * -DDEBUG [-DVERBOSE] -ll -lintl 565 * scriptvfy test_directory 566 */ 567 568 main(int argc, char *argv[]) 569 { 570 int val; 571 572 line_no = 1; 573 574 if (argc == 1) { 575 printf("No directory provided.\n"); 576 exit(1); 577 } 578 579 val = checkscripts(argv[1], 0); 580 581 printf("return code is %d\n", val); 582 } 583 #endif 584 585 /* 586 * This function evaluates the provided script and returns a bit string 587 * describing what patterns were located. 588 */ 589 static int 590 scripteval(char *script_name, char *script_path, int mask, int silent) 591 { 592 int val = 0; 593 int error = 0; 594 line_no = 1; 595 596 if ((script_path == NULL) || (*script_path == NULL) || 597 (script_name == NULL)) { 598 logerr(gettext(ERR_ARGS)); 599 return (0); 600 } 601 602 #ifdef VERBOSE 603 printf("Evaluating %s\n", script_path); 604 #endif 605 606 if ((scr_fp = fopen(script_path, "r")) == NULL) { 607 logerr(gettext(ERR_FOPEN), script_path, errno); 608 return (0); 609 } 610 611 #ifdef VERBOSE 612 printf("Openned script\n"); 613 #endif 614 615 while (val = yylex()) { 616 #ifdef VERBOSE 617 printf(" Match is %s, returned 0x%x at line %d\n", 618 yytext, val, line_no); 619 printf(" in_function = %d, in_awk = %d, in_loop = %d, " \ 620 "in_case = %d, in_if = %d, in_pipe = %d\n", 621 status.in_function, status.in_awk, status.in_loop, 622 status.in_case, status.in_if, status.in_pipe); 623 printf(" loop_depth = %d, case_depth = %d, " \ 624 "if_depth = %d, pipe_release = %d, cur_level = %d\n", 625 loop_depth, case_depth, if_depth, pipe_release, cur_level); 626 #endif 627 628 val &= mask; 629 if (val) { 630 error |= ((val & MAYBE_ONLY) ? 1 : 2); 631 632 /* 633 * So at this point, val contains all status bits 634 * appropriate to this script. 635 */ 636 if (!silent) { 637 char *msg_ptr; 638 if (val & INTERACT_D) 639 msg_ptr = gettext(ERR_INTERACT); 640 else if (val & ROOT_D) 641 msg_ptr = gettext(ERR_ROOT); 642 else if (val & LOCKED_D) 643 msg_ptr = gettext(ERR_LOCKED); 644 else if (val & INTERACT_M) 645 msg_ptr = gettext(WRN_INTERACT); 646 else if (val & ROOT_M) 647 msg_ptr = gettext(WRN_ROOT); 648 else if (val & LOCKED_M) 649 msg_ptr = gettext(WRN_LOCKED); 650 else if (val & WPARM1_M) 651 msg_ptr = gettext(WRN_FORM_USE); 652 else if (val & USEPARM1_M) 653 msg_ptr = gettext(WRN_FORM_USE); 654 else if (val & ODDPARM_M) 655 msg_ptr = gettext(WRN_FORM_ARG); 656 else if (val & PKGDB_M) 657 msg_ptr = gettext(WRN_TRANSDB); 658 else 659 msg_ptr = gettext("unknown error"); 660 661 logerr(msg_ptr, script_name, line_no); 662 } 663 } 664 } 665 666 /* Warn if required about missing "-f" calls. */ 667 if (status.instf && !(status.instf_f)) 668 logerr(gettext(WRN_INST_F), script_name); 669 670 if (status.remf && !(status.remf_f)) 671 logerr(gettext(WRN_REM_F), script_name); 672 673 status.instf = status.instf_f = status.remf = status.remf_f = 0; 674 675 /* Warn if installf was used but no space file is in place. */ 676 if (status.nospacefile && status.needspacefile) { 677 logerr(gettext(WRN_SPACEACC), script_name); 678 status.needspacefile = 0; 679 } 680 681 status.in_pipe = 0; /* Pipes may dangle. */ 682 fclose(scr_fp); 683 684 if (error == 3) 685 error = 2; 686 687 return (error); 688 } 689 690 /* Test a preinstall or preremove script for validity. */ 691 int 692 pre_valid(char *script_name, char *script_path, int silent) 693 { 694 return (scripteval(script_name, script_path, PRE_MASK, silent)); 695 } 696 697 /* Test a class action script for validity. */ 698 int 699 cas_valid(char *script_name, char *script_path, int silent) 700 { 701 return (scripteval(script_name, script_path, CAS_MASK, silent)); 702 } 703 704 /* Test a postinstall or postremove script for validity. */ 705 int 706 post_valid(char *script_name, char *script_path, int silent) 707 { 708 return (scripteval(script_name, script_path, POST_MASK, silent)); 709 } 710 711 /* Test a class action script for validity. */ 712 int 713 req_valid(char *script_name, char *script_path, int silent) 714 { 715 return (scripteval(script_name, script_path, REQ_MASK, silent)); 716 } 717 718 719 /* Test a class action script for validity. */ 720 int 721 chk_valid(char *script_name, char *script_path, int silent) 722 { 723 return (scripteval(script_name, script_path, CHK_MASK, silent)); 724 } 725 726 /* This tests all of the scripts in the provided directory. */ 727 int 728 checkscripts(char *inst_dir, int silent) 729 { 730 DIR *dirfp; 731 struct dirent *dp; 732 char path[PATH_MAX]; 733 int retval = 0; 734 735 /* For future reference, determine if a space file is present. */ 736 sprintf(path, "%s/%s", inst_dir, "space"); 737 if (access(path, F_OK) != 0) 738 status.nospacefile = 1; 739 740 if ((dirfp = opendir(inst_dir)) == NULL) 741 return (0); 742 743 while ((dp = readdir(dirfp)) != NULL) { 744 #ifdef VERBOSE 745 printf("Looking at file %s\n", dp->d_name); 746 #endif 747 if (dp->d_name[0] == '.') 748 continue; 749 750 if ((strcmp(dp->d_name, "preinstall") == 0) || 751 (strcmp(dp->d_name, "preremove") == 0)) { 752 sprintf(path, "%s/%s", inst_dir, dp->d_name); 753 retval |= pre_valid(dp->d_name, path, silent); 754 continue; 755 } 756 757 if ((strncmp(dp->d_name, "i.", 2) == 0) || 758 (strncmp(dp->d_name, "r.", 2) == 0)) { 759 sprintf(path, "%s/%s", inst_dir, dp->d_name); 760 retval |= cas_valid(dp->d_name, path, silent); 761 continue; 762 } 763 764 if ((strcmp(dp->d_name, "postinstall") == 0) || 765 (strcmp(dp->d_name, "postremove") == 0)) { 766 sprintf(path, "%s/%s", inst_dir, dp->d_name); 767 retval |= post_valid(dp->d_name, path, silent); 768 continue; 769 } 770 771 if (strcmp(dp->d_name, "request") == 0) { 772 sprintf(path, "%s/%s", inst_dir, dp->d_name); 773 retval |= req_valid(dp->d_name, path, silent); 774 continue; 775 } 776 if (strcmp(dp->d_name, "checkinstall") == 0) { 777 sprintf(path, "%s/%s", inst_dir, dp->d_name); 778 retval |= chk_valid(dp->d_name, path, silent); 779 continue; 780 } 781 } 782 783 (void) closedir(dirfp); 784 785 return (retval); 786 } 787