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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * The Secure SunOS audit reduction tool - auditreduce. 29 * Document SM0071 is the primary source of information on auditreduce. 30 * 31 * Composed of 4 source modules: 32 * main.c - main driver. 33 * option.c - command line option processing. 34 * process.c - record/file/process functions. 35 * time.c - date/time handling. 36 * 37 * Main(), write_header(), audit_stats(), and a_calloc() 38 * are the only functions visible outside this module. 39 */ 40 41 #include <siginfo.h> 42 #include <locale.h> 43 #include <libintl.h> 44 #include "auditr.h" 45 #include "auditrd.h" 46 47 #if !defined(TEXT_DOMAIN) 48 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 49 #endif 50 51 extern void derive_str(time_t, char *); 52 extern int process_options(int, char **); 53 extern int mproc(audit_pcb_t *); 54 extern void init_tokens(void); /* shared with praudit */ 55 56 static int a_pow(int, int); 57 static void calc_procs(void); 58 static void chld_handler(int); 59 static int close_outfile(void); 60 static void c_close(audit_pcb_t *, int); 61 static void delete_infiles(void); 62 static void gather_pcb(audit_pcb_t *, int, int); 63 static void init_options(void); 64 static int init_sig(void); 65 static void int_handler(int); 66 static int mfork(audit_pcb_t *, int, int, int); 67 static void mcount(int, int); 68 static int open_outfile(void); 69 static void p_close(audit_pcb_t *); 70 static int rename_outfile(void); 71 static void rm_mem(audit_pcb_t *); 72 static void rm_outfile(void); 73 static void trim_mem(audit_pcb_t *); 74 static int write_file_token(time_t); 75 static int write_trailer(void); 76 77 /* 78 * File globals. 79 */ 80 static int max_sproc; /* maximum number of subprocesses per process */ 81 static int total_procs; /* number of processes in the process tree */ 82 static int total_layers; /* number of layers in the process tree */ 83 84 /* 85 * .func main - main. 86 * .desc The beginning. Main() calls each of the initialization routines 87 * and then allocates the root pcb. Then it calls mfork() to get 88 * the work done. 89 * .call main(argc, argv). 90 * .arg argc - number of arguments. 91 * .arg argv - array of pointers to arguments. 92 * .ret 0 - via exit() - no errors detected. 93 * .ret 1 - via exit() - errors detected (messages printed). 94 */ 95 int 96 main(int argc, char **argv) 97 { 98 int ret; 99 audit_pcb_t *pcb; 100 101 /* Internationalization */ 102 (void) setlocale(LC_ALL, ""); 103 (void) textdomain(TEXT_DOMAIN); 104 105 root_pid = getpid(); /* know who is root process for error */ 106 init_options(); /* initialize options */ 107 init_tokens(); /* initialize token processing table */ 108 if (init_sig()) /* initialize signals */ 109 exit(1); 110 if (process_options(argc, argv)) 111 exit(1); /* process command line options */ 112 if (open_outfile()) /* setup root process output stream */ 113 exit(1); 114 calc_procs(); /* see how many subprocesses we need */ 115 /* 116 * Allocate the root pcb and set it up. 117 */ 118 pcb = (audit_pcb_t *)a_calloc(1, sizeof (audit_pcb_t)); 119 pcb->pcb_procno = root_pid; 120 pcb->pcb_flags |= PF_ROOT; 121 pcb->pcb_fpw = stdout; 122 pcb->pcb_time = -1; 123 /* 124 * Now start the whole thing rolling. 125 */ 126 if (mfork(pcb, pcbnum, 0, pcbnum - 1)) { 127 /* 128 * Error in processing somewhere. A message is already printed. 129 * Display usage statistics and remove the outfile. 130 */ 131 if (getpid() == root_pid) { 132 audit_stats(); 133 (void) close_outfile(); 134 rm_outfile(); 135 } 136 exit(1); 137 } 138 /* 139 * Clean up afterwards. 140 * Only do outfile cleanup if we are root process. 141 */ 142 if (getpid() == root_pid) { 143 if ((ret = write_trailer()) == 0) { /* write trailer to file */ 144 145 ret = close_outfile(); /* close the outfile */ 146 } 147 /* 148 * If there was an error in cleanup then remove outfile. 149 */ 150 if (ret) { 151 rm_outfile(); 152 exit(1); 153 } 154 /* 155 * And lastly delete the infiles if the user so wishes. 156 */ 157 if (f_delete) 158 delete_infiles(); 159 } 160 return (0); 161 /*NOTREACHED*/ 162 } 163 164 165 /* 166 * .func mfork - main fork routine. 167 * .desc Create a (sub-)tree of processses if needed, or just do the work 168 * if we have few enough groups to process. This is a recursive routine 169 * which stops recursing when the number of files to process is small 170 * enough. Each call to mfork() is responsible for a range of pcbs 171 * from audit_pcbs[]. This range is designated by the lo and hi 172 * arguments (inclusive). If the number of pcbs is small enough 173 * then we have hit a leaf of the tree and mproc() is called to 174 * do the processing. Otherwise we fork some processes and break 175 * the range of pcbs up amongst them. 176 * .call ret = mfork(pcb, nsp, lo, hi). 177 * .arg pcb - ptr to pcb that is root node of the to-be-created tree. 178 * .arg nsp - number of sub-processes this tree must process. 179 * .arg lo - lower-limit of process number range. Index into audit_pcbs. 180 * .arg hi - higher limit of pcb range. Index into audit_pcbs. 181 * .ret 0 - succesful completion. 182 * .ret -1 - error encountered in processing - message already printed. 183 */ 184 static int 185 mfork(audit_pcb_t *pcb, int nsp, int lo, int hi) 186 { 187 int range, procno, i, tofork, nnsp, nrem; 188 int fildes[2]; 189 audit_pcb_t *pcbn; 190 191 #if AUDIT_PROC_TRACE 192 (void) fprintf(stderr, "mfork: nsp %d %d->%d\n", nsp, lo, hi); 193 #endif 194 195 /* 196 * The range of pcb's to process is small enough now. Do the work. 197 */ 198 if (nsp <= max_sproc) { 199 pcb->pcb_flags |= PF_LEAF; /* leaf in process tree */ 200 pcb->pcb_below = audit_pcbs; /* proc pcbs from audit_pcbs */ 201 gather_pcb(pcb, lo, hi); 202 trim_mem(pcb); /* trim allocated memory */ 203 return (mproc(pcb)); /* do the work */ 204 } 205 /* 206 * Too many pcb's for one process - must fork. 207 * Try to balance the tree as it grows and make it short and fat. 208 * The thing to minimize is the number of times a record passes 209 * through a pipe. 210 */ 211 else { 212 /* 213 * Fork less than the maximum number of processes. 214 */ 215 if (nsp <= max_sproc * (max_sproc - 1)) { 216 tofork = nsp / max_sproc; 217 if (nsp % max_sproc) 218 tofork++; /* how many to fork */ 219 } 220 /* 221 * Fork the maximum number of processes. 222 */ 223 else { 224 tofork = max_sproc; /* how many to fork */ 225 } 226 /* 227 * Allocate the nodes below us in the process tree. 228 */ 229 pcb->pcb_below = (audit_pcb_t *) 230 a_calloc(tofork, sizeof (*pcb)); 231 nnsp = nsp / tofork; /* # of pcbs per forked process */ 232 nrem = nsp % tofork; /* remainder to spread around */ 233 /* 234 * Loop to fork all of the subs. Open a pipe for each. 235 * If there are any errors in pipes, forks, or getting streams 236 * for the pipes then quit altogether. 237 */ 238 for (i = 0; i < tofork; i++) { 239 pcbn = &pcb->pcb_below[i]; 240 pcbn->pcb_time = -1; 241 if (pipe(fildes)) { 242 perror(gettext( 243 "auditreduce: couldn't get a pipe")); 244 return (-1); 245 } 246 /* 247 * Convert descriptors to streams. 248 */ 249 if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) { 250 perror(gettext("auditreduce: couldn't get read stream for pipe")); 251 return (-1); 252 } 253 if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) { 254 perror(gettext("auditreduce: couldn't get write stream for pipe")); 255 return (-1); 256 } 257 if ((procno = fork()) == -1) { 258 perror(gettext("auditreduce: fork failed")); 259 return (-1); 260 } 261 /* 262 * Calculate the range of pcbs from audit_pcbs [] this 263 * branch of the tree will be responsible for. 264 */ 265 range = (nrem > 0) ? nnsp + 1 : nnsp; 266 /* 267 * Child route. 268 */ 269 if (procno == 0) { 270 pcbn->pcb_procno = getpid(); 271 c_close(pcb, i); /* close unused streams */ 272 /* 273 * Continue resolving this branch. 274 */ 275 return (mfork(pcbn, range, lo, lo + range - 1)); 276 } 277 /* Parent route. */ 278 else { 279 pcbn->pcb_procno = i; 280 /* allocate buffer to hold record */ 281 pcbn->pcb_rec = (char *)a_calloc(1, 282 AUDITBUFSIZE); 283 pcbn->pcb_size = AUDITBUFSIZE; 284 p_close(pcbn); /* close unused streams */ 285 286 nrem--; 287 lo += range; 288 } 289 } 290 /* 291 * Done forking all of the subs. 292 */ 293 gather_pcb(pcb, 0, tofork - 1); 294 trim_mem(pcb); /* free unused memory */ 295 return (mproc(pcb)); 296 } 297 } 298 299 300 /* 301 * .func trim_mem - trim memory usage. 302 * .desc Free un-needed allocated memory. 303 * .call trim_mem(pcb). 304 * .arg pcb - ptr to pcb for current process. 305 * .ret void. 306 */ 307 static void 308 trim_mem(audit_pcb_t *pcb) 309 { 310 int count; 311 size_t size; 312 313 /* 314 * For the root don't free anything. We need to save audit_pcbs[] 315 * in case we are deleting the infiles at the end. 316 */ 317 if (pcb->pcb_flags & PF_ROOT) 318 return; 319 /* 320 * For a leaf save its part of audit_pcbs[] and then remove it all. 321 */ 322 if (pcb->pcb_flags & PF_LEAF) { 323 count = pcb->pcb_count; 324 size = sizeof (audit_pcb_t); 325 /* allocate a new buffer to hold the pcbs */ 326 pcb->pcb_below = (audit_pcb_t *)a_calloc(count, size); 327 /* save this pcb's portion */ 328 (void) memcpy((void *) pcb->pcb_below, 329 (void *) &audit_pcbs[pcb->pcb_lo], count * size); 330 rm_mem(pcb); 331 gather_pcb(pcb, 0, count - 1); 332 } 333 /* 334 * If this is an intermediate node then just remove it all. 335 */ 336 else { 337 rm_mem(pcb); 338 } 339 } 340 341 342 /* 343 * .func rm_mem - remove memory. 344 * .desc Remove unused memory associated with audit_pcbs[]. For each 345 * pcb in audit_pcbs[] free the record buffer and all of 346 * the fcbs. Then free audit_pcbs[]. 347 * .call rm_mem(pcbr). 348 * .arg pcbr - ptr to pcb of current process. 349 * .ret void. 350 */ 351 static void 352 rm_mem(audit_pcb_t *pcbr) 353 { 354 int i; 355 audit_pcb_t *pcb; 356 audit_fcb_t *fcb, *fcbn; 357 358 for (i = 0; i < pcbsize; i++) { 359 /* 360 * Don't free the record buffer and fcbs for the pcbs this 361 * process is using. 362 */ 363 if (pcbr->pcb_flags & PF_LEAF) { 364 if (pcbr->pcb_lo <= i || i <= pcbr->pcb_hi) 365 continue; 366 } 367 pcb = &audit_pcbs[i]; 368 free(pcb->pcb_rec); 369 for (fcb = pcb->pcb_first; fcb != NULL; /* */) { 370 fcbn = fcb->fcb_next; 371 free((char *)fcb); 372 fcb = fcbn; 373 } 374 } 375 free((char *)audit_pcbs); 376 } 377 378 379 /* 380 * .func c_close - close unused streams. 381 * .desc This is called for each child process just after being born. 382 * The child closes the read stream for the pipe to its parent. 383 * It also closes the read streams for the other children that 384 * have been born before it. If any closes fail a warning message 385 * is printed, but processing continues. 386 * .call ret = c_close(pcb, i). 387 * .arg pcb - ptr to the child's parent pcb. 388 * .arg i - iteration # of child in forking loop. 389 * .ret void. 390 */ 391 static void 392 c_close(audit_pcb_t *pcb, int i) 393 { 394 int j; 395 audit_pcb_t *pcbt; 396 397 /* 398 * Do all pcbs in parent's group up to and including us 399 */ 400 for (j = 0; j <= i; j++) { 401 pcbt = &pcb->pcb_below[j]; 402 if (fclose(pcbt->pcb_fpr) == EOF) { 403 if (!f_quiet) 404 perror(gettext("auditreduce: initial close on pipe failed")); 405 } 406 /* 407 * Free the buffer allocated to hold incoming records. 408 */ 409 if (i != j) { 410 free(pcbt->pcb_rec); 411 } 412 } 413 } 414 415 416 /* 417 * .func p_close - close unused streams for parent. 418 * .desc Called by the parent right after forking a child. 419 * Closes the write stream on the pipe to the child since 420 * we will never use it. 421 * .call p_close(pcbn), 422 * .arg pcbn - ptr to pcb. 423 * .ret void. 424 */ 425 static void 426 p_close(audit_pcb_t *pcbn) 427 { 428 if (fclose(pcbn->pcb_fpw) == EOF) { 429 if (!f_quiet) 430 perror(gettext("auditreduce: close for write pipe failed")); 431 } 432 } 433 434 435 /* 436 * .func audit_stats - print statistics. 437 * .desc Print usage statistics for the user if the run fails. 438 * Tells them how many files they had and how many groups this 439 * totalled. Also tell them how many layers and processes the 440 * process tree had. 441 * .call audit_stats(). 442 * .arg none. 443 * .ret void. 444 */ 445 void 446 audit_stats(void) 447 { 448 struct rlimit rl; 449 450 if (getrlimit(RLIMIT_NOFILE, &rl) != -1) 451 (void) fprintf(stderr, 452 gettext("%s The system allows %d files per process.\n"), 453 ar, rl.rlim_cur); 454 (void) fprintf(stderr, gettext( 455 "%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"), 456 ar, filenum, pcbnum, total_procs, total_layers); 457 } 458 459 460 /* 461 * .func gather_pcb - gather pcbs. 462 * .desc Gather together the range of the sub-processes that we are 463 * responsible for. For a pcb that controls processes this is all 464 * of the sub-processes that it forks. For a pcb that controls 465 * files this is the the range of pcbs from audit_pcbs[]. 466 * .call gather_pcb(pcb, lo, hi). 467 * .arg pcb - ptr to pcb. 468 * .arg lo - lo index into pcb_below. 469 * .arg hi - hi index into pcb_below. 470 * .ret void. 471 */ 472 static void 473 gather_pcb(audit_pcb_t *pcb, int lo, int hi) 474 { 475 pcb->pcb_lo = lo; 476 pcb->pcb_hi = hi; 477 pcb->pcb_count = hi - lo + 1; 478 } 479 480 481 /* 482 * .func calc_procs - calculate process parameters. 483 * .desc Calculate the current run's paramters regarding how many 484 * processes will have to be forked (maybe none). 485 * 5 is subtracted from maxfiles_proc to allow for stdin, stdout, 486 * stderr, and the pipe to a parent process. The outfile 487 * in the root process is assigned to stdout. The unused half of each 488 * pipe is closed, to allow for more connections, but we still 489 * have to have the 5th spot because in order to get the pipe 490 * we need 2 descriptors up front. 491 * .call calc_procs(). 492 * .arg none. 493 * .ret void. 494 */ 495 static void 496 calc_procs(void) 497 { 498 int val; 499 int maxfiles_proc; 500 struct rlimit rl; 501 502 if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { 503 perror("auditreduce: getrlimit"); 504 exit(1); 505 } 506 507 maxfiles_proc = rl.rlim_cur; 508 509 max_sproc = maxfiles_proc - 5; /* max subprocesses per process */ 510 511 /* 512 * Calculate how many layers the process tree has. 513 */ 514 total_layers = 1; 515 for (/* */; /* */; /* */) { 516 val = a_pow(max_sproc, total_layers); 517 if (val > pcbnum) 518 break; 519 total_layers++; 520 } 521 /* 522 * Count how many processes are in the process tree. 523 */ 524 mcount(pcbnum, 0); 525 526 #if AUDIT_PROC_TRACE 527 (void) fprintf(stderr, 528 "pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n", 529 pcbnum, filenum, maxfiles_proc, max_sproc, 530 total_layers, total_procs); 531 #endif 532 } 533 534 535 static int 536 a_pow(int base, int exp) 537 { 538 int i; 539 int answer; 540 541 if (exp == 0) { 542 answer = 1; 543 } else { 544 answer = base; 545 for (i = 0; i < (exp - 1); i++) 546 answer *= base; 547 } 548 return (answer); 549 } 550 551 552 /* 553 * .func mcount - main count. 554 * .desc Go through the motions of building the process tree just 555 * to count how many processes there are. Don't really 556 * build anything. Answer is in global var total_procs. 557 * .call mcount(nsp, lo). 558 * .arg nsp - number of subs for this tree branch. 559 * .arg lo - lo side of range of subs. 560 * .ret void. 561 */ 562 static void 563 mcount(int nsp, int lo) 564 { 565 int range, i, tofork, nnsp, nrem; 566 567 total_procs++; /* count another process created */ 568 569 if (nsp > max_sproc) { 570 if (nsp <= max_sproc * (max_sproc - 1)) { 571 tofork = nsp / max_sproc; 572 if (nsp % max_sproc) 573 tofork++; 574 } else { 575 tofork = max_sproc; 576 } 577 nnsp = nsp / tofork; 578 nrem = nsp % tofork; 579 for (i = 0; i < tofork; i++) { 580 range = (nrem > 0) ? nnsp + 1 : nnsp; 581 mcount(range, lo); 582 nrem--; 583 lo += range; 584 } 585 } 586 } 587 588 589 /* 590 * .func delete_infiles - delete the input files. 591 * .desc If the user asked us to (via 'D' flag) then unlink the input files. 592 * .call ret = delete_infiles(). 593 * .arg none. 594 * .ret void. 595 */ 596 static void 597 delete_infiles(void) 598 { 599 int i; 600 audit_pcb_t *pcb; 601 audit_fcb_t *fcb; 602 603 for (i = 0; i < pcbsize; i++) { 604 pcb = &audit_pcbs[i]; 605 fcb = pcb->pcb_dfirst; 606 while (fcb != NULL) { 607 /* 608 * Only delete a file if it was succesfully processed. 609 * If there were any read errors or bad records 610 * then don't delete it. 611 * There may still be unprocessed records in it. 612 */ 613 if (fcb->fcb_flags & FF_DELETE) { 614 if (unlink(fcb->fcb_file)) { 615 if (f_verbose) { 616 (void) sprintf(errbuf, gettext( 617 "%s delete on %s failed"), 618 ar, fcb->fcb_file); 619 } 620 perror(errbuf); 621 } 622 } 623 fcb = fcb->fcb_next; 624 } 625 } 626 } 627 628 629 /* 630 * .func rm_outfile - remove the outfile. 631 * .desc Remove the file we are writing the records to. We do this if 632 * processing failed and we are quitting before finishing. 633 * Update - don't actually remove the outfile, but generate 634 * a warning about its possible heathen nature. 635 * .call ret = rm_outfile(). 636 * .arg none. 637 * .ret void. 638 */ 639 static void 640 rm_outfile(void) 641 { 642 #if 0 643 if (f_outfile) { 644 if (unlink(f_outtemp) == -1) { 645 (void) sprintf(errbuf, 646 gettext("%s delete on %s failed"), 647 ar, f_outtemp); 648 perror(errbuf); 649 } 650 } 651 #else 652 (void) fprintf(stderr, 653 gettext("%s Warning: Incomplete audit file may have been generated - %s\n"), 654 ar, 655 (f_outfile == NULL) ? gettext("standard output") : f_outfile); 656 #endif 657 } 658 659 660 /* 661 * .func close_outfile - close the outfile. 662 * .desc Close the file we are writing records to. 663 * .call ret = close_outfile(). 664 * .arg none. 665 * .ret 0 - close was succesful. 666 * .ret -1 - close failed. 667 */ 668 static int 669 close_outfile(void) 670 { 671 if (fclose(stdout) == EOF) { 672 (void) sprintf(errbuf, gettext("%s close on %s failed"), 673 ar, f_outfile ? f_outfile : "standard output"); 674 perror(errbuf); 675 return (-1); 676 } 677 (void) fsync(fileno(stdout)); 678 return (rename_outfile()); 679 } 680 681 682 /* 683 * .func write_header - write audit file header. 684 * .desc Write an audit file header to the output stream. The time in the 685 * header is the time of the first record written to the stream. This 686 * routine is called by the process handling the root node of the 687 * process tree just before it writes the first record to the output 688 * stream. 689 * .ret 0 - succesful write. 690 * .ret -1 - failed write - message printed. 691 */ 692 int 693 write_header(void) 694 { 695 return (write_file_token(f_start)); 696 } 697 698 699 static int 700 write_file_token(time_t when) 701 { 702 adr_t adr; /* adr ptr */ 703 struct timeval tv; /* time now */ 704 char for_adr[16]; /* plenty of room */ 705 #ifdef _LP64 706 char token_id = AUT_OTHER_FILE64; 707 #else 708 char token_id = AUT_OTHER_FILE32; 709 #endif 710 short i = 1; 711 char c = '\0'; 712 713 tv.tv_sec = when; 714 tv.tv_usec = 0; 715 adr_start(&adr, for_adr); 716 adr_char(&adr, &token_id, 1); 717 #ifdef _LP64 718 adr_int64(&adr, (int64_t *)&tv, 2); 719 #else 720 adr_int32(&adr, (int32_t *)&tv, 2); 721 #endif 722 adr_short(&adr, &i, 1); 723 adr_char(&adr, &c, 1); 724 725 if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) != 726 adr_count(&adr)) { 727 if (when == f_start) { 728 (void) sprintf(errbuf, 729 gettext("%s error writing header to %s. "), 730 ar, 731 f_outfile ? f_outfile : 732 gettext("standard output")); 733 } else { 734 (void) sprintf(errbuf, 735 gettext("%s error writing trailer to %s. "), 736 ar, 737 f_outfile ? f_outfile : 738 gettext("standard output")); 739 } 740 perror(errbuf); 741 return (-1); 742 } 743 return (0); 744 } 745 746 747 /* 748 * .func write_trailer - write audit file trailer. 749 * .desc Write an audit file trailer to the output stream. The finish 750 * time for the trailer is the time of the last record written 751 * to the stream. 752 * .ret 0 - succesful write. 753 * .ret -1 - failed write - message printed. 754 */ 755 static int 756 write_trailer(void) 757 { 758 return (write_file_token(f_end)); 759 } 760 761 762 /* 763 * .func rename_outfile - rename the outfile. 764 * .desc If the user used the -O flag they only gave us the suffix name 765 * for the outfile. We have to add the time stamps to put the filename 766 * in the proper audit file name format. The start time will be the time 767 * of the first record in the file and the end time will be the time of 768 * the last record in the file. 769 * .ret 0 - rename succesful. 770 * .ret -1 - rename failed - message printed. 771 */ 772 static int 773 rename_outfile(void) 774 { 775 char f_newfile[MAXFILELEN]; 776 char buf1[15], buf2[15]; 777 char *f_file, *f_nfile, *f_time, *f_name; 778 779 if (f_outfile != NULL) { 780 /* 781 * Get string representations of start and end times. 782 */ 783 derive_str(f_start, buf1); 784 derive_str(f_end, buf2); 785 786 f_nfile = f_time = f_newfile; /* working copy */ 787 f_file = f_name = f_outfile; /* their version */ 788 while (*f_file) { 789 if (*f_file == '/') { /* look for filename */ 790 f_time = f_nfile + 1; 791 f_name = f_file + 1; 792 } 793 *f_nfile++ = *f_file++; /* make copy of their version */ 794 } 795 *f_time = '\0'; 796 /* start time goes first */ 797 (void) strcat(f_newfile, buf1); 798 (void) strcat(f_newfile, "."); 799 /* then the finish time */ 800 (void) strcat(f_newfile, buf2); 801 (void) strcat(f_newfile, "."); 802 /* and the name they gave us */ 803 (void) strcat(f_newfile, f_name); 804 805 #if AUDIT_FILE 806 (void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n", 807 f_outfile, f_newfile); 808 #endif 809 810 #if AUDIT_RENAME 811 if (rename(f_outtemp, f_newfile) == -1) { 812 (void) fprintf(stderr, 813 "%s rename of %s to %s failed.\n", 814 ar, f_outtemp, f_newfile); 815 return (-1); 816 } 817 f_outfile = f_newfile; 818 #else 819 if (rename(f_outtemp, f_outfile) == -1) { 820 (void) fprintf(stderr, 821 gettext("%s rename of %s to %s failed.\n"), 822 ar, f_outtemp, f_outfile); 823 return (-1); 824 } 825 #endif 826 } 827 return (0); 828 } 829 830 831 /* 832 * .func open_outfile - open the outfile. 833 * .desc Open the outfile specified by the -O option. Assign it to the 834 * the standard output. Get a unique temporary name to use so we 835 * don't clobber an existing file. 836 * .ret 0 - no errors detected. 837 * .ret -1 - errors in processing (message already printed). 838 */ 839 static int 840 open_outfile(void) 841 { 842 int tmpfd = -1; 843 844 if (f_outfile != NULL) { 845 f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8); 846 (void) strcpy(f_outtemp, f_outfile); 847 (void) strcat(f_outtemp, "XXXXXX"); 848 if ((tmpfd = mkstemp(f_outtemp)) == -1) { 849 (void) sprintf(errbuf, 850 gettext("%s couldn't create temporary file"), ar); 851 perror(errbuf); 852 return (-1); 853 } 854 (void) fflush(stdout); 855 if (tmpfd != fileno(stdout)) { 856 if ((dup2(tmpfd, fileno(stdout))) == -1) { 857 (void) sprintf(errbuf, 858 gettext("%s can't assign %s to the " 859 "standard output"), ar, f_outfile); 860 perror(errbuf); 861 return (-1); 862 } 863 (void) close(tmpfd); 864 } 865 } 866 return (0); 867 } 868 869 870 /* 871 * .func init_options - initialize the options. 872 * .desc Give initial and/or default values to some options. 873 * .call init_options(); 874 * .arg none. 875 * .ret void. 876 */ 877 static void 878 init_options(void) 879 { 880 struct timeval tp; 881 struct timezone tpz; 882 883 /* 884 * Get current time for general use. 885 */ 886 if (gettimeofday(&tp, &tpz) == -1) 887 perror(gettext("auditreduce: initial getttimeofday failed")); 888 889 time_now = tp.tv_sec; /* save for general use */ 890 f_start = 0; /* first record time default */ 891 f_end = time_now; /* last record time default */ 892 m_after = 0; /* Jan 1, 1970 00:00:00 */ 893 894 /* 895 * Setup initial size of audit_pcbs[]. 896 */ 897 pcbsize = PCB_INITSIZE; /* initial size of file-holding pcb's */ 898 899 audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t)); 900 901 /* description of 'current' error */ 902 error_str = gettext("initial error"); 903 904 } 905 906 907 /* 908 * .func a_calloc - audit calloc. 909 * .desc Calloc with check for failure. This is called by all of the 910 * places that want memory. 911 * .call ptr = a_calloc(nelem, size). 912 * .arg nelem - number of elements to allocate. 913 * .arg size - size of each element. 914 * .ret ptr - ptr to allocated and zeroed memory. 915 * .ret never - if calloc fails then we never return. 916 */ 917 void * 918 a_calloc(int nelem, size_t size) 919 { 920 void *ptr; 921 922 if ((ptr = calloc((unsigned)nelem, size)) == NULL) { 923 perror(gettext("auditreduce: memory allocation failed")); 924 exit(1); 925 } 926 return (ptr); 927 } 928 929 930 /* 931 * .func init_sig - initial signal catching. 932 * 933 * .desc 934 * Setup the signal catcher to catch the SIGCHLD signal plus 935 * "environmental" signals -- keyboard plus other externally 936 * generated signals such as out of file space or cpu time. If a 937 * child exits with either a non-zero exit code or was killed by 938 * a signal to it then we will also exit with a non-zero exit 939 * code. In this way abnormal conditions can be passed up to the 940 * root process and the entire run be halted. Also catch the int 941 * and quit signals. Remove the output file since it is in an 942 * inconsistent state. 943 * .call ret = init_sig(). 944 * .arg none. 945 * .ret 0 - no errors detected. 946 * .ret -1 - signal failed (message printed). 947 */ 948 static int 949 init_sig(void) 950 { 951 if (signal(SIGCHLD, chld_handler) == SIG_ERR) { 952 perror(gettext("auditreduce: SIGCHLD signal failed")); 953 return (-1); 954 } 955 956 if (signal(SIGHUP, int_handler) == SIG_ERR) { 957 perror(gettext("auditreduce: SIGHUP signal failed")); 958 return (-1); 959 } 960 if (signal(SIGINT, int_handler) == SIG_ERR) { 961 perror(gettext("auditreduce: SIGINT signal failed")); 962 return (-1); 963 } 964 if (signal(SIGQUIT, int_handler) == SIG_ERR) { 965 perror(gettext("auditreduce: SIGQUIT signal failed")); 966 return (-1); 967 } 968 if (signal(SIGABRT, int_handler) == SIG_ERR) { 969 perror(gettext("auditreduce: SIGABRT signal failed")); 970 return (-1); 971 } 972 if (signal(SIGTERM, int_handler) == SIG_ERR) { 973 perror(gettext("auditreduce: SIGTERM signal failed")); 974 return (-1); 975 } 976 if (signal(SIGPWR, int_handler) == SIG_ERR) { 977 perror(gettext("auditreduce: SIGPWR signal failed")); 978 return (-1); 979 } 980 if (signal(SIGXCPU, int_handler) == SIG_ERR) { 981 perror(gettext("auditreduce: SIGXCPU signal failed")); 982 return (-1); 983 } 984 if (signal(SIGXFSZ, int_handler) == SIG_ERR) { 985 perror(gettext("auditreduce: SIGXFSZ signal failed")); 986 return (-1); 987 } 988 if (signal(SIGSEGV, int_handler) == SIG_ERR) { 989 perror(gettext("auditreduce: SIGSEGV signal failed")); 990 return (-1); 991 } 992 993 return (0); 994 } 995 996 997 /* 998 * .func chld_handler - handle child signals. 999 * .desc Catch the SIGCHLD signals. Remove the root process 1000 * output file because it is in an inconsistent state. 1001 * Print a message giving the signal number and/or return code 1002 * of the child who caused the signal. 1003 * .ret void. 1004 */ 1005 /* ARGSUSED */ 1006 void 1007 chld_handler(int sig) 1008 { 1009 int pid; 1010 int status; 1011 1012 /* 1013 * Get pid and reasons for cause of event. 1014 */ 1015 pid = wait(&status); 1016 1017 if (pid > 0) { 1018 /* 1019 * If child received a signal or exited with a non-zero 1020 * exit status then print message and exit 1021 */ 1022 if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) || 1023 (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) { 1024 (void) fprintf(stderr, 1025 gettext("%s abnormal child termination - "), ar); 1026 1027 if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) { 1028 psignal(WLOBYTE(status), "signal"); 1029 if (WCOREDUMP(status)) 1030 (void) fprintf(stderr, 1031 gettext("core dumped\n")); 1032 } 1033 1034 if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0) 1035 (void) fprintf(stderr, gettext( 1036 "return code %d\n"), 1037 WHIBYTE(status)); 1038 1039 /* 1040 * Get rid of outfile - it is suspect. 1041 */ 1042 if (f_outfile != NULL) { 1043 (void) close_outfile(); 1044 rm_outfile(); 1045 } 1046 /* 1047 * Give statistical info that may be useful. 1048 */ 1049 audit_stats(); 1050 1051 exit(1); 1052 } 1053 } 1054 } 1055 1056 1057 /* 1058 * .func int_handler - handle quit/int signals. 1059 * .desc Catch the keyboard and other environmental signals. 1060 * Remove the root process output file because it is in 1061 * an inconsistent state. 1062 * .ret void. 1063 */ 1064 /* ARGSUSED */ 1065 void 1066 int_handler(int sig) 1067 { 1068 if (getpid() == root_pid) { 1069 (void) close_outfile(); 1070 rm_outfile(); 1071 exit(1); 1072 } 1073 /* 1074 * For a child process don't give an error exit or the 1075 * parent process will catch it with the chld_handler and 1076 * try to erase the outfile again. 1077 */ 1078 exit(0); 1079 }