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