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 (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 }
   1080