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