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