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 }