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