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