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