xref: /onnv-gate/usr/src/cmd/auditreduce/main.c (revision 8889:6e60e6119528)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*8889STon.Nguyen@Sun.COM  * Common Development and Distribution License (the "License").
6*8889STon.Nguyen@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*8889STon.Nguyen@Sun.COM 
220Sstevel@tonic-gate /*
23*8889STon.Nguyen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * The Secure SunOS audit reduction tool - auditreduce.
290Sstevel@tonic-gate  * Document SM0071 is the primary source of information on auditreduce.
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * Composed of 4 source modules:
320Sstevel@tonic-gate  * main.c - main driver.
330Sstevel@tonic-gate  * option.c - command line option processing.
340Sstevel@tonic-gate  * process.c - record/file/process functions.
350Sstevel@tonic-gate  * time.c - date/time handling.
360Sstevel@tonic-gate  *
370Sstevel@tonic-gate  * Main(), write_header(), audit_stats(), and a_calloc()
380Sstevel@tonic-gate  * are the only functions visible outside this module.
390Sstevel@tonic-gate  */
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #include <siginfo.h>
420Sstevel@tonic-gate #include <locale.h>
430Sstevel@tonic-gate #include <libintl.h>
440Sstevel@tonic-gate #include "auditr.h"
450Sstevel@tonic-gate #include "auditrd.h"
460Sstevel@tonic-gate 
470Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
480Sstevel@tonic-gate #define	TEXT_DOMAIN "SUNW_OST_OSCMD"
490Sstevel@tonic-gate #endif
500Sstevel@tonic-gate 
510Sstevel@tonic-gate extern void	derive_str(time_t, char *);
520Sstevel@tonic-gate extern int	process_options(int, char **);
530Sstevel@tonic-gate extern int	mproc(audit_pcb_t *);
540Sstevel@tonic-gate extern void	init_tokens(void);	/* shared with praudit */
550Sstevel@tonic-gate 
560Sstevel@tonic-gate static int	a_pow(int, int);
570Sstevel@tonic-gate static void	calc_procs(void);
580Sstevel@tonic-gate static void	chld_handler(int);
590Sstevel@tonic-gate static int	close_outfile(void);
600Sstevel@tonic-gate static void	c_close(audit_pcb_t *, int);
610Sstevel@tonic-gate static void	delete_infiles(void);
620Sstevel@tonic-gate static void	gather_pcb(audit_pcb_t *, int, int);
630Sstevel@tonic-gate static void	init_options(void);
640Sstevel@tonic-gate static int	init_sig(void);
650Sstevel@tonic-gate static void	int_handler(int);
660Sstevel@tonic-gate static int	mfork(audit_pcb_t *, int, int, int);
670Sstevel@tonic-gate static void	mcount(int, int);
680Sstevel@tonic-gate static int	open_outfile(void);
690Sstevel@tonic-gate static void	p_close(audit_pcb_t *);
700Sstevel@tonic-gate static int	rename_outfile(void);
710Sstevel@tonic-gate static void	rm_mem(audit_pcb_t *);
720Sstevel@tonic-gate static void	rm_outfile(void);
730Sstevel@tonic-gate static void	trim_mem(audit_pcb_t *);
740Sstevel@tonic-gate static int	write_file_token(time_t);
750Sstevel@tonic-gate static int	write_trailer(void);
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate  * File globals.
790Sstevel@tonic-gate  */
800Sstevel@tonic-gate static int	max_sproc;	/* maximum number of subprocesses per process */
810Sstevel@tonic-gate static int	total_procs;	/* number of processes in the process tree */
820Sstevel@tonic-gate static int	total_layers;	/* number of layers in the process tree */
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /*
850Sstevel@tonic-gate  * .func main - main.
860Sstevel@tonic-gate  * .desc The beginning. Main() calls each of the initialization routines
870Sstevel@tonic-gate  *	and then allocates the root pcb. Then it calls mfork() to get
880Sstevel@tonic-gate  *	the work done.
890Sstevel@tonic-gate  * .call	main(argc, argv).
900Sstevel@tonic-gate  * .arg	argc	- number of arguments.
910Sstevel@tonic-gate  * .arg	argv	- array of pointers to arguments.
920Sstevel@tonic-gate  * .ret	0	- via exit() - no errors detected.
930Sstevel@tonic-gate  * .ret	1	- via exit() - errors detected (messages printed).
940Sstevel@tonic-gate  */
950Sstevel@tonic-gate int
main(int argc,char ** argv)960Sstevel@tonic-gate main(int argc, char **argv)
970Sstevel@tonic-gate {
980Sstevel@tonic-gate 	int	ret;
990Sstevel@tonic-gate 	audit_pcb_t *pcb;
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	/* Internationalization */
1020Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1030Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate 	root_pid = getpid();	/* know who is root process for error */
1060Sstevel@tonic-gate 	init_options();		/* initialize options */
1070Sstevel@tonic-gate 	init_tokens();		/* initialize token processing table */
1080Sstevel@tonic-gate 	if (init_sig())		/* initialize signals */
1090Sstevel@tonic-gate 		exit(1);
1100Sstevel@tonic-gate 	if (process_options(argc, argv))
1110Sstevel@tonic-gate 		exit(1);	/* process command line options */
1120Sstevel@tonic-gate 	if (open_outfile())	/* setup root process output stream */
1130Sstevel@tonic-gate 		exit(1);
1140Sstevel@tonic-gate 	calc_procs();		/* see how many subprocesses we need */
1150Sstevel@tonic-gate 	/*
1160Sstevel@tonic-gate 	 * Allocate the root pcb and set it up.
1170Sstevel@tonic-gate 	 */
1180Sstevel@tonic-gate 	pcb = (audit_pcb_t *)a_calloc(1, sizeof (audit_pcb_t));
1190Sstevel@tonic-gate 	pcb->pcb_procno = root_pid;
1200Sstevel@tonic-gate 	pcb->pcb_flags |= PF_ROOT;
1210Sstevel@tonic-gate 	pcb->pcb_fpw = stdout;
1220Sstevel@tonic-gate 	pcb->pcb_time = -1;
1230Sstevel@tonic-gate 	/*
1240Sstevel@tonic-gate 	 * Now start the whole thing rolling.
1250Sstevel@tonic-gate 	 */
1260Sstevel@tonic-gate 	if (mfork(pcb, pcbnum, 0, pcbnum - 1)) {
1270Sstevel@tonic-gate 		/*
1280Sstevel@tonic-gate 		 * Error in processing somewhere. A message is already printed.
1290Sstevel@tonic-gate 		 * Display usage statistics and remove the outfile.
1300Sstevel@tonic-gate 		 */
1310Sstevel@tonic-gate 		if (getpid() == root_pid) {
1320Sstevel@tonic-gate 			audit_stats();
1330Sstevel@tonic-gate 			(void) close_outfile();
1340Sstevel@tonic-gate 			rm_outfile();
1350Sstevel@tonic-gate 		}
1360Sstevel@tonic-gate 		exit(1);
1370Sstevel@tonic-gate 	}
1380Sstevel@tonic-gate 	/*
1390Sstevel@tonic-gate 	 * Clean up afterwards.
1400Sstevel@tonic-gate 	 * Only do outfile cleanup if we are root process.
1410Sstevel@tonic-gate 	 */
1420Sstevel@tonic-gate 	if (getpid() == root_pid) {
1430Sstevel@tonic-gate 		if ((ret = write_trailer()) == 0) { /* write trailer to file */
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 			ret = close_outfile();	/* close the outfile */
1460Sstevel@tonic-gate 		}
1470Sstevel@tonic-gate 		/*
1480Sstevel@tonic-gate 		 * If there was an error in cleanup then remove outfile.
1490Sstevel@tonic-gate 		 */
1500Sstevel@tonic-gate 		if (ret) {
1510Sstevel@tonic-gate 			rm_outfile();
1520Sstevel@tonic-gate 			exit(1);
1530Sstevel@tonic-gate 		}
1540Sstevel@tonic-gate 		/*
1550Sstevel@tonic-gate 		 * And lastly delete the infiles if the user so wishes.
1560Sstevel@tonic-gate 		 */
1570Sstevel@tonic-gate 		if (f_delete)
1580Sstevel@tonic-gate 			delete_infiles();
1590Sstevel@tonic-gate 	}
1600Sstevel@tonic-gate 	return (0);
1610Sstevel@tonic-gate /*NOTREACHED*/
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate /*
1660Sstevel@tonic-gate  * .func mfork - main fork routine.
1670Sstevel@tonic-gate  * .desc Create a (sub-)tree of processses if needed, or just do the work
1680Sstevel@tonic-gate  *	if we have few enough groups to process. This is a recursive routine
1690Sstevel@tonic-gate  *	which stops recursing when the number of files to process is small
1700Sstevel@tonic-gate  *	enough. Each call to mfork() is responsible for a range of pcbs
1710Sstevel@tonic-gate  *	from audit_pcbs[]. This range is designated by the lo and hi
1720Sstevel@tonic-gate  *	arguments (inclusive). If the number of pcbs is small enough
1730Sstevel@tonic-gate  *	then we have hit a leaf of the tree and mproc() is called to
1740Sstevel@tonic-gate  *	do the processing. Otherwise we fork some processes and break
1750Sstevel@tonic-gate  *	the range of pcbs up amongst them.
1760Sstevel@tonic-gate  * .call	ret = mfork(pcb, nsp, lo, hi).
1770Sstevel@tonic-gate  * .arg	pcb	- ptr to pcb that is root node of the to-be-created tree.
1780Sstevel@tonic-gate  * .arg	nsp	- number of sub-processes this tree must process.
1790Sstevel@tonic-gate  * .arg	lo	- lower-limit of process number range. Index into audit_pcbs.
1800Sstevel@tonic-gate  * .arg	hi	- higher limit of pcb range. Index into audit_pcbs.
1810Sstevel@tonic-gate  * .ret	0	- succesful completion.
1820Sstevel@tonic-gate  * .ret	-1	- error encountered in processing - message already printed.
1830Sstevel@tonic-gate  */
1840Sstevel@tonic-gate static int
mfork(audit_pcb_t * pcb,int nsp,int lo,int hi)1850Sstevel@tonic-gate mfork(audit_pcb_t *pcb, int nsp, int lo, int hi)
1860Sstevel@tonic-gate {
1870Sstevel@tonic-gate 	int	range, procno, i, tofork, nnsp, nrem;
1880Sstevel@tonic-gate 	int	fildes[2];
1890Sstevel@tonic-gate 	audit_pcb_t *pcbn;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate #if AUDIT_PROC_TRACE
1920Sstevel@tonic-gate 	(void) fprintf(stderr, "mfork: nsp %d %d->%d\n", nsp, lo, hi);
1930Sstevel@tonic-gate #endif
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	/*
1960Sstevel@tonic-gate 	 * The range of pcb's to process is small enough now. Do the work.
1970Sstevel@tonic-gate 	 */
1980Sstevel@tonic-gate 	if (nsp <= max_sproc) {
1990Sstevel@tonic-gate 		pcb->pcb_flags |= PF_LEAF;	/* leaf in process tree */
2000Sstevel@tonic-gate 		pcb->pcb_below = audit_pcbs;	/* proc pcbs from audit_pcbs */
2010Sstevel@tonic-gate 		gather_pcb(pcb, lo, hi);
2020Sstevel@tonic-gate 		trim_mem(pcb);			/* trim allocated memory */
2030Sstevel@tonic-gate 		return (mproc(pcb));		/* do the work */
2040Sstevel@tonic-gate 	}
2050Sstevel@tonic-gate 	/*
2060Sstevel@tonic-gate 	 * Too many pcb's for one process - must fork.
2070Sstevel@tonic-gate 	 * Try to balance the tree as it grows and make it short and fat.
2080Sstevel@tonic-gate 	 * The thing to minimize is the number of times a record passes
2090Sstevel@tonic-gate 	 * through a pipe.
2100Sstevel@tonic-gate 	 */
2110Sstevel@tonic-gate 	else {
2120Sstevel@tonic-gate 		/*
2130Sstevel@tonic-gate 		 * Fork less than the maximum number of processes.
2140Sstevel@tonic-gate 		 */
2150Sstevel@tonic-gate 		if (nsp <= max_sproc * (max_sproc - 1)) {
2160Sstevel@tonic-gate 			tofork = nsp / max_sproc;
2170Sstevel@tonic-gate 			if (nsp % max_sproc)
2180Sstevel@tonic-gate 				tofork++;	/* how many to fork */
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 		/*
2210Sstevel@tonic-gate 		 * Fork the maximum number of processes.
2220Sstevel@tonic-gate 		 */
2230Sstevel@tonic-gate 		else {
2240Sstevel@tonic-gate 			tofork = max_sproc;	/* how many to fork */
2250Sstevel@tonic-gate 		}
2260Sstevel@tonic-gate 		/*
2270Sstevel@tonic-gate 		 * Allocate the nodes below us in the process tree.
2280Sstevel@tonic-gate 		 */
2290Sstevel@tonic-gate 		pcb->pcb_below = (audit_pcb_t *)
2300Sstevel@tonic-gate 			a_calloc(tofork, sizeof (*pcb));
2310Sstevel@tonic-gate 		nnsp = nsp / tofork;	/* # of pcbs per forked process */
2320Sstevel@tonic-gate 		nrem = nsp % tofork;	/* remainder to spread around */
2330Sstevel@tonic-gate 		/*
2340Sstevel@tonic-gate 		 * Loop to fork all of the subs. Open a pipe for each.
2350Sstevel@tonic-gate 		 * If there are any errors in pipes, forks, or getting streams
2360Sstevel@tonic-gate 		 * for the pipes then quit altogether.
2370Sstevel@tonic-gate 		 */
2380Sstevel@tonic-gate 		for (i = 0; i < tofork; i++) {
2390Sstevel@tonic-gate 			pcbn = &pcb->pcb_below[i];
2400Sstevel@tonic-gate 			pcbn->pcb_time = -1;
2410Sstevel@tonic-gate 			if (pipe(fildes)) {
2420Sstevel@tonic-gate 				perror(gettext(
2430Sstevel@tonic-gate 					"auditreduce: couldn't get a pipe"));
2440Sstevel@tonic-gate 				return (-1);
2450Sstevel@tonic-gate 			}
2460Sstevel@tonic-gate 			/*
2470Sstevel@tonic-gate 			 * Convert descriptors to streams.
2480Sstevel@tonic-gate 			 */
2490Sstevel@tonic-gate 			if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) {
2500Sstevel@tonic-gate 	perror(gettext("auditreduce: couldn't get read stream for pipe"));
2510Sstevel@tonic-gate 				return (-1);
2520Sstevel@tonic-gate 			}
2530Sstevel@tonic-gate 			if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) {
2540Sstevel@tonic-gate 	perror(gettext("auditreduce: couldn't get write stream for pipe"));
2550Sstevel@tonic-gate 				return (-1);
2560Sstevel@tonic-gate 			}
2570Sstevel@tonic-gate 			if ((procno = fork()) == -1) {
2580Sstevel@tonic-gate 				perror(gettext("auditreduce: fork failed"));
2590Sstevel@tonic-gate 				return (-1);
2600Sstevel@tonic-gate 			}
2610Sstevel@tonic-gate 			/*
2620Sstevel@tonic-gate 			 * Calculate the range of pcbs from audit_pcbs [] this
2630Sstevel@tonic-gate 			 * branch of the tree will be responsible for.
2640Sstevel@tonic-gate 			 */
2650Sstevel@tonic-gate 			range = (nrem > 0) ? nnsp + 1 : nnsp;
2660Sstevel@tonic-gate 			/*
2670Sstevel@tonic-gate 			 * Child route.
2680Sstevel@tonic-gate 			 */
2690Sstevel@tonic-gate 			if (procno == 0) {
2700Sstevel@tonic-gate 				pcbn->pcb_procno = getpid();
2710Sstevel@tonic-gate 				c_close(pcb, i); /* close unused streams */
2720Sstevel@tonic-gate 				/*
2730Sstevel@tonic-gate 				 * Continue resolving this branch.
2740Sstevel@tonic-gate 				 */
2750Sstevel@tonic-gate 				return (mfork(pcbn, range, lo, lo + range - 1));
2760Sstevel@tonic-gate 			}
2770Sstevel@tonic-gate 			/* Parent route. */
2780Sstevel@tonic-gate 			else {
2790Sstevel@tonic-gate 				pcbn->pcb_procno = i;
2800Sstevel@tonic-gate 				/* allocate buffer to hold record */
2810Sstevel@tonic-gate 				pcbn->pcb_rec = (char *)a_calloc(1,
2820Sstevel@tonic-gate 				    AUDITBUFSIZE);
2830Sstevel@tonic-gate 				pcbn->pcb_size = AUDITBUFSIZE;
2840Sstevel@tonic-gate 				p_close(pcbn);	/* close unused streams */
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 				nrem--;
2870Sstevel@tonic-gate 				lo += range;
2880Sstevel@tonic-gate 			}
2890Sstevel@tonic-gate 		}
2900Sstevel@tonic-gate 		/*
2910Sstevel@tonic-gate 		 * Done forking all of the subs.
2920Sstevel@tonic-gate 		 */
2930Sstevel@tonic-gate 		gather_pcb(pcb, 0, tofork - 1);
2940Sstevel@tonic-gate 		trim_mem(pcb);			/* free unused memory */
2950Sstevel@tonic-gate 		return (mproc(pcb));
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate }
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate  * .func	trim_mem - trim memory usage.
3020Sstevel@tonic-gate  * .desc	Free un-needed allocated memory.
3030Sstevel@tonic-gate  * .call	trim_mem(pcb).
3040Sstevel@tonic-gate  * .arg	pcb	- ptr to pcb for current process.
3050Sstevel@tonic-gate  * .ret	void.
3060Sstevel@tonic-gate  */
3070Sstevel@tonic-gate static void
trim_mem(audit_pcb_t * pcb)3080Sstevel@tonic-gate trim_mem(audit_pcb_t *pcb)
3090Sstevel@tonic-gate {
3100Sstevel@tonic-gate 	int	count;
3110Sstevel@tonic-gate 	size_t	size;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	/*
3140Sstevel@tonic-gate 	 * For the root don't free anything. We need to save audit_pcbs[]
3150Sstevel@tonic-gate 	 * in case we are deleting the infiles at the end.
3160Sstevel@tonic-gate 	 */
3170Sstevel@tonic-gate 	if (pcb->pcb_flags & PF_ROOT)
3180Sstevel@tonic-gate 		return;
3190Sstevel@tonic-gate 	/*
3200Sstevel@tonic-gate 	 * For a leaf save its part of audit_pcbs[] and then remove it all.
3210Sstevel@tonic-gate 	 */
3220Sstevel@tonic-gate 	if (pcb->pcb_flags & PF_LEAF) {
3230Sstevel@tonic-gate 		count = pcb->pcb_count;
3240Sstevel@tonic-gate 		size = sizeof (audit_pcb_t);
3250Sstevel@tonic-gate 		/* allocate a new buffer to hold the pcbs */
3260Sstevel@tonic-gate 		pcb->pcb_below = (audit_pcb_t *)a_calloc(count, size);
3270Sstevel@tonic-gate 		/* save this pcb's portion */
3280Sstevel@tonic-gate 		(void) memcpy((void *) pcb->pcb_below,
3290Sstevel@tonic-gate 		    (void *) &audit_pcbs[pcb->pcb_lo], count * size);
3300Sstevel@tonic-gate 		rm_mem(pcb);
3310Sstevel@tonic-gate 		gather_pcb(pcb, 0, count - 1);
3320Sstevel@tonic-gate 	}
3330Sstevel@tonic-gate 		/*
3340Sstevel@tonic-gate 		 * If this is an intermediate node then just remove it all.
3350Sstevel@tonic-gate 		 */
3360Sstevel@tonic-gate 	else {
3370Sstevel@tonic-gate 		rm_mem(pcb);
3380Sstevel@tonic-gate 	}
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate /*
3430Sstevel@tonic-gate  * .func	rm_mem - remove memory.
3440Sstevel@tonic-gate  * .desc	Remove unused memory associated with audit_pcbs[]. For each
3450Sstevel@tonic-gate  *	pcb in audit_pcbs[] free the record buffer and all of
3460Sstevel@tonic-gate  *	the fcbs. Then free audit_pcbs[].
3470Sstevel@tonic-gate  * .call	rm_mem(pcbr).
3480Sstevel@tonic-gate  * .arg	pcbr	- ptr to pcb of current process.
3490Sstevel@tonic-gate  * .ret	void.
3500Sstevel@tonic-gate  */
3510Sstevel@tonic-gate static void
rm_mem(audit_pcb_t * pcbr)3520Sstevel@tonic-gate rm_mem(audit_pcb_t *pcbr)
3530Sstevel@tonic-gate {
3540Sstevel@tonic-gate 	int	i;
3550Sstevel@tonic-gate 	audit_pcb_t *pcb;
3560Sstevel@tonic-gate 	audit_fcb_t *fcb, *fcbn;
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	for (i = 0; i < pcbsize; i++) {
3590Sstevel@tonic-gate 		/*
3600Sstevel@tonic-gate 		 * Don't free the record buffer and fcbs for the pcbs this
3610Sstevel@tonic-gate 		 * process is using.
3620Sstevel@tonic-gate 		 */
3630Sstevel@tonic-gate 		if (pcbr->pcb_flags & PF_LEAF) {
3640Sstevel@tonic-gate 			if (pcbr->pcb_lo <= i || i <= pcbr->pcb_hi)
3650Sstevel@tonic-gate 				continue;
3660Sstevel@tonic-gate 		}
3670Sstevel@tonic-gate 		pcb = &audit_pcbs[i];
3680Sstevel@tonic-gate 		free(pcb->pcb_rec);
3690Sstevel@tonic-gate 		for (fcb = pcb->pcb_first; fcb != NULL; /* */) {
3700Sstevel@tonic-gate 			fcbn = fcb->fcb_next;
3710Sstevel@tonic-gate 			free((char *)fcb);
3720Sstevel@tonic-gate 			fcb = fcbn;
3730Sstevel@tonic-gate 		}
3740Sstevel@tonic-gate 	}
3750Sstevel@tonic-gate 	free((char *)audit_pcbs);
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate /*
3800Sstevel@tonic-gate  * .func	c_close - close unused streams.
3810Sstevel@tonic-gate  * .desc	This is called for each child process just after being born.
3820Sstevel@tonic-gate  *	The child closes the read stream for the pipe to its parent.
3830Sstevel@tonic-gate  *	It also closes the read streams for the other children that
3840Sstevel@tonic-gate  *	have been born before it. If any closes fail a warning message
3850Sstevel@tonic-gate  *	is printed, but processing continues.
3860Sstevel@tonic-gate  * .call	ret = c_close(pcb, i).
3870Sstevel@tonic-gate  * .arg	pcb	- ptr to the child's parent pcb.
3880Sstevel@tonic-gate  * .arg	i	- iteration # of child in forking loop.
3890Sstevel@tonic-gate  * .ret	void.
3900Sstevel@tonic-gate  */
3910Sstevel@tonic-gate static void
c_close(audit_pcb_t * pcb,int i)3920Sstevel@tonic-gate c_close(audit_pcb_t *pcb, int	i)
3930Sstevel@tonic-gate {
3940Sstevel@tonic-gate 	int	j;
3950Sstevel@tonic-gate 	audit_pcb_t *pcbt;
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	/*
3980Sstevel@tonic-gate 	 * Do all pcbs in parent's group up to and including us
3990Sstevel@tonic-gate 	 */
4000Sstevel@tonic-gate 	for (j = 0; j <= i; j++) {
4010Sstevel@tonic-gate 		pcbt = &pcb->pcb_below[j];
4020Sstevel@tonic-gate 		if (fclose(pcbt->pcb_fpr) == EOF) {
4030Sstevel@tonic-gate 			if (!f_quiet)
4040Sstevel@tonic-gate 		perror(gettext("auditreduce: initial close on pipe failed"));
4050Sstevel@tonic-gate 		}
4060Sstevel@tonic-gate 		/*
4070Sstevel@tonic-gate 		 * Free the buffer allocated to hold incoming records.
4080Sstevel@tonic-gate 		 */
4090Sstevel@tonic-gate 		if (i != j) {
4100Sstevel@tonic-gate 			free(pcbt->pcb_rec);
4110Sstevel@tonic-gate 		}
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate /*
4170Sstevel@tonic-gate  * .func	p_close - close unused streams for parent.
4180Sstevel@tonic-gate  * .desc	Called by the parent right after forking a child.
4190Sstevel@tonic-gate  *	Closes the write stream on the pipe to the child since
4200Sstevel@tonic-gate  *	we will never use it.
4210Sstevel@tonic-gate  * .call	p_close(pcbn),
4220Sstevel@tonic-gate  * .arg	pcbn	- ptr to pcb.
4230Sstevel@tonic-gate  * .ret	void.
4240Sstevel@tonic-gate  */
4250Sstevel@tonic-gate static void
p_close(audit_pcb_t * pcbn)4260Sstevel@tonic-gate p_close(audit_pcb_t *pcbn)
4270Sstevel@tonic-gate {
4280Sstevel@tonic-gate 	if (fclose(pcbn->pcb_fpw) == EOF) {
4290Sstevel@tonic-gate 		if (!f_quiet)
4300Sstevel@tonic-gate 		perror(gettext("auditreduce: close for write pipe failed"));
4310Sstevel@tonic-gate 	}
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate  * .func	audit_stats - print statistics.
4370Sstevel@tonic-gate  * .desc	Print usage statistics for the user if the run fails.
4380Sstevel@tonic-gate  *	Tells them how many files they had and how many groups this
4390Sstevel@tonic-gate  *	totalled. Also tell them how many layers and processes the
4400Sstevel@tonic-gate  *	process tree had.
4410Sstevel@tonic-gate  * .call	audit_stats().
4420Sstevel@tonic-gate  * .arg	none.
4430Sstevel@tonic-gate  * .ret	void.
4440Sstevel@tonic-gate  */
4450Sstevel@tonic-gate void
audit_stats(void)4460Sstevel@tonic-gate audit_stats(void)
4470Sstevel@tonic-gate {
4480Sstevel@tonic-gate 	struct rlimit rl;
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	if (getrlimit(RLIMIT_NOFILE, &rl) != -1)
4510Sstevel@tonic-gate 		(void) fprintf(stderr,
4520Sstevel@tonic-gate 		    gettext("%s The system allows %d files per process.\n"),
4530Sstevel@tonic-gate 		    ar, rl.rlim_cur);
4540Sstevel@tonic-gate 	(void) fprintf(stderr, gettext(
4550Sstevel@tonic-gate "%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
4560Sstevel@tonic-gate 		ar, filenum, pcbnum, total_procs, total_layers);
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate /*
4610Sstevel@tonic-gate  * .func gather_pcb - gather pcbs.
4620Sstevel@tonic-gate  * .desc Gather together the range of the sub-processes that we are
4630Sstevel@tonic-gate  *	responsible for. For a pcb that controls processes this is all
4640Sstevel@tonic-gate  *	of the sub-processes that it forks. For a pcb that controls
4650Sstevel@tonic-gate  *	files this is the the range of pcbs from audit_pcbs[].
4660Sstevel@tonic-gate  * .call gather_pcb(pcb, lo, hi).
4670Sstevel@tonic-gate  * .arg	pcb	- ptr to pcb.
4680Sstevel@tonic-gate  * .arg	lo	- lo index into pcb_below.
4690Sstevel@tonic-gate  * .arg	hi	- hi index into pcb_below.
4700Sstevel@tonic-gate  * .ret	void.
4710Sstevel@tonic-gate  */
4720Sstevel@tonic-gate static void
gather_pcb(audit_pcb_t * pcb,int lo,int hi)4730Sstevel@tonic-gate gather_pcb(audit_pcb_t *pcb, int lo, int hi)
4740Sstevel@tonic-gate {
4750Sstevel@tonic-gate 	pcb->pcb_lo = lo;
4760Sstevel@tonic-gate 	pcb->pcb_hi = hi;
4770Sstevel@tonic-gate 	pcb->pcb_count = hi - lo + 1;
4780Sstevel@tonic-gate }
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate /*
4820Sstevel@tonic-gate  * .func calc_procs - calculate process parameters.
4830Sstevel@tonic-gate  * .desc Calculate the current run's paramters regarding how many
4840Sstevel@tonic-gate  *	processes will have to be forked (maybe none).
4850Sstevel@tonic-gate  *	5 is subtracted from maxfiles_proc to allow for stdin, stdout,
4860Sstevel@tonic-gate  *	stderr, and the pipe to a parent process. The outfile
4870Sstevel@tonic-gate  *	in the root process is assigned to stdout. The unused half of each
4880Sstevel@tonic-gate  *	pipe is closed, to allow for more connections, but we still
4890Sstevel@tonic-gate  *	have to have the 5th spot because in order to get the pipe
4900Sstevel@tonic-gate  *	we need 2 descriptors up front.
4910Sstevel@tonic-gate  * .call calc_procs().
4920Sstevel@tonic-gate  * .arg	none.
4930Sstevel@tonic-gate  * .ret	void.
4940Sstevel@tonic-gate  */
4950Sstevel@tonic-gate static void
calc_procs(void)4960Sstevel@tonic-gate calc_procs(void)
4970Sstevel@tonic-gate {
4980Sstevel@tonic-gate 	int	val;
4990Sstevel@tonic-gate 	int	maxfiles_proc;
5000Sstevel@tonic-gate 	struct rlimit rl;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
5030Sstevel@tonic-gate 		perror("auditreduce: getrlimit");
5040Sstevel@tonic-gate 		exit(1);
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	maxfiles_proc = rl.rlim_cur;
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 	max_sproc = maxfiles_proc - 5;	/* max subprocesses per process */
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	/*
5120Sstevel@tonic-gate 	 * Calculate how many layers the process tree has.
5130Sstevel@tonic-gate 	 */
5140Sstevel@tonic-gate 	total_layers = 1;
5150Sstevel@tonic-gate 	for (/* */; /* */; /* */) {
5160Sstevel@tonic-gate 		val = a_pow(max_sproc, total_layers);
5170Sstevel@tonic-gate 		if (val > pcbnum)
5180Sstevel@tonic-gate 			break;
5190Sstevel@tonic-gate 		total_layers++;
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 	/*
5220Sstevel@tonic-gate 	 * Count how many processes are in the process tree.
5230Sstevel@tonic-gate 	 */
5240Sstevel@tonic-gate 	mcount(pcbnum, 0);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate #if AUDIT_PROC_TRACE
5270Sstevel@tonic-gate 	(void) fprintf(stderr,
5280Sstevel@tonic-gate 	    "pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
5290Sstevel@tonic-gate 	    pcbnum, filenum, maxfiles_proc, max_sproc,
5300Sstevel@tonic-gate 	    total_layers, total_procs);
5310Sstevel@tonic-gate #endif
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate static int
a_pow(int base,int exp)5360Sstevel@tonic-gate a_pow(int base, int exp)
5370Sstevel@tonic-gate {
5380Sstevel@tonic-gate 	int	i;
5390Sstevel@tonic-gate 	int	answer;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	if (exp == 0) {
5420Sstevel@tonic-gate 		answer = 1;
5430Sstevel@tonic-gate 	} else {
5440Sstevel@tonic-gate 		answer = base;
5450Sstevel@tonic-gate 		for (i = 0; i < (exp - 1); i++)
5460Sstevel@tonic-gate 			answer *= base;
5470Sstevel@tonic-gate 	}
5480Sstevel@tonic-gate 	return (answer);
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate /*
5530Sstevel@tonic-gate  * .func mcount - main count.
5540Sstevel@tonic-gate  * .desc Go through the motions of building the process tree just
5550Sstevel@tonic-gate  *	to count how many processes there are. Don't really
5560Sstevel@tonic-gate  *	build anything. Answer is in global var total_procs.
5570Sstevel@tonic-gate  * .call mcount(nsp, lo).
5580Sstevel@tonic-gate  * .arg	nsp	- number of subs for this tree branch.
5590Sstevel@tonic-gate  * .arg	lo	- lo side of range of subs.
5600Sstevel@tonic-gate  * .ret	void.
5610Sstevel@tonic-gate  */
5620Sstevel@tonic-gate static void
mcount(int nsp,int lo)5630Sstevel@tonic-gate mcount(int nsp, int lo)
5640Sstevel@tonic-gate {
5650Sstevel@tonic-gate 	int	range, i, tofork, nnsp, nrem;
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	total_procs++;		/* count another process created */
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	if (nsp > max_sproc) {
5700Sstevel@tonic-gate 		if (nsp <= max_sproc * (max_sproc - 1)) {
5710Sstevel@tonic-gate 			tofork = nsp / max_sproc;
5720Sstevel@tonic-gate 			if (nsp % max_sproc)
5730Sstevel@tonic-gate 				tofork++;
5740Sstevel@tonic-gate 		} else {
5750Sstevel@tonic-gate 			tofork = max_sproc;
5760Sstevel@tonic-gate 		}
5770Sstevel@tonic-gate 		nnsp = nsp / tofork;
5780Sstevel@tonic-gate 		nrem = nsp % tofork;
5790Sstevel@tonic-gate 		for (i = 0; i < tofork; i++) {
5800Sstevel@tonic-gate 			range = (nrem > 0) ? nnsp + 1 : nnsp;
5810Sstevel@tonic-gate 			mcount(range, lo);
5820Sstevel@tonic-gate 			nrem--;
5830Sstevel@tonic-gate 			lo += range;
5840Sstevel@tonic-gate 		}
5850Sstevel@tonic-gate 	}
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate /*
5900Sstevel@tonic-gate  * .func delete_infiles - delete the input files.
5910Sstevel@tonic-gate  * .desc If the user asked us to (via 'D' flag) then unlink the input files.
5920Sstevel@tonic-gate  * .call ret = delete_infiles().
5930Sstevel@tonic-gate  * .arg none.
5940Sstevel@tonic-gate  * .ret void.
5950Sstevel@tonic-gate  */
5960Sstevel@tonic-gate static void
delete_infiles(void)5970Sstevel@tonic-gate delete_infiles(void)
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate 	int	i;
6000Sstevel@tonic-gate 	audit_pcb_t *pcb;
6010Sstevel@tonic-gate 	audit_fcb_t *fcb;
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	for (i = 0; i < pcbsize; i++) {
6040Sstevel@tonic-gate 		pcb = &audit_pcbs[i];
6050Sstevel@tonic-gate 		fcb = pcb->pcb_dfirst;
6060Sstevel@tonic-gate 		while (fcb != NULL) {
6070Sstevel@tonic-gate 			/*
6080Sstevel@tonic-gate 			 * Only delete a file if it was succesfully processed.
6090Sstevel@tonic-gate 			 * If there were any read errors or bad records
6100Sstevel@tonic-gate 			 * then don't delete it.
6110Sstevel@tonic-gate 			 * There may still be unprocessed records in it.
6120Sstevel@tonic-gate 			 */
6130Sstevel@tonic-gate 			if (fcb->fcb_flags & FF_DELETE) {
6140Sstevel@tonic-gate 				if (unlink(fcb->fcb_file)) {
6150Sstevel@tonic-gate 					if (f_verbose) {
6160Sstevel@tonic-gate 						(void) sprintf(errbuf, gettext(
6170Sstevel@tonic-gate 						"%s delete on %s failed"),
6180Sstevel@tonic-gate 						ar, fcb->fcb_file);
6190Sstevel@tonic-gate 					}
6200Sstevel@tonic-gate 					perror(errbuf);
6210Sstevel@tonic-gate 				}
6220Sstevel@tonic-gate 			}
6230Sstevel@tonic-gate 			fcb = fcb->fcb_next;
6240Sstevel@tonic-gate 		}
6250Sstevel@tonic-gate 	}
6260Sstevel@tonic-gate }
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate /*
6300Sstevel@tonic-gate  * .func rm_outfile - remove the outfile.
6310Sstevel@tonic-gate  * .desc Remove the file we are writing the records to. We do this if
6320Sstevel@tonic-gate  *	processing failed and we are quitting before finishing.
6330Sstevel@tonic-gate  *	Update - don't actually remove the outfile, but generate
6340Sstevel@tonic-gate  *	a warning about its possible heathen nature.
6350Sstevel@tonic-gate  * .call ret = rm_outfile().
6360Sstevel@tonic-gate  * .arg	none.
6370Sstevel@tonic-gate  * .ret	void.
6380Sstevel@tonic-gate  */
6390Sstevel@tonic-gate static void
rm_outfile(void)6400Sstevel@tonic-gate rm_outfile(void)
6410Sstevel@tonic-gate {
6420Sstevel@tonic-gate #if 0
6430Sstevel@tonic-gate 	if (f_outfile) {
6440Sstevel@tonic-gate 		if (unlink(f_outtemp) == -1) {
6450Sstevel@tonic-gate 			(void) sprintf(errbuf,
6460Sstevel@tonic-gate 				gettext("%s delete on %s failed"),
6470Sstevel@tonic-gate 				ar, f_outtemp);
6480Sstevel@tonic-gate 			perror(errbuf);
6490Sstevel@tonic-gate 		}
6500Sstevel@tonic-gate 	}
6510Sstevel@tonic-gate #else
6520Sstevel@tonic-gate 	(void) fprintf(stderr,
6530Sstevel@tonic-gate gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
6540Sstevel@tonic-gate 		ar,
6550Sstevel@tonic-gate 		(f_outfile == NULL) ? gettext("standard output") : f_outfile);
6560Sstevel@tonic-gate #endif
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate /*
6610Sstevel@tonic-gate  * .func	close_outfile - close the outfile.
6620Sstevel@tonic-gate  * .desc	Close the file we are writing records to.
6630Sstevel@tonic-gate  * .call	ret = close_outfile().
6640Sstevel@tonic-gate  * .arg	none.
6650Sstevel@tonic-gate  * .ret	0	- close was succesful.
6660Sstevel@tonic-gate  * .ret	-1	- close failed.
6670Sstevel@tonic-gate  */
6680Sstevel@tonic-gate static int
close_outfile(void)6690Sstevel@tonic-gate close_outfile(void)
6700Sstevel@tonic-gate {
6710Sstevel@tonic-gate 	if (fclose(stdout) == EOF) {
6720Sstevel@tonic-gate 		(void) sprintf(errbuf, gettext("%s close on %s failed"),
6730Sstevel@tonic-gate 		    ar, f_outfile ? f_outfile : "standard output");
6740Sstevel@tonic-gate 		perror(errbuf);
6750Sstevel@tonic-gate 		return (-1);
6760Sstevel@tonic-gate 	}
6770Sstevel@tonic-gate 	(void) fsync(fileno(stdout));
6780Sstevel@tonic-gate 	return (rename_outfile());
6790Sstevel@tonic-gate }
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate /*
6830Sstevel@tonic-gate  * .func write_header - write audit file header.
6840Sstevel@tonic-gate  * .desc Write an audit file header to the output stream. The time in the
6850Sstevel@tonic-gate  *	header is the time of the first record written to the stream. This
6860Sstevel@tonic-gate  *	routine is called by the process handling the root node of the
6870Sstevel@tonic-gate  *	process tree just before it writes the first record to the output
6880Sstevel@tonic-gate  *	stream.
6890Sstevel@tonic-gate  * .ret	0 - succesful write.
6900Sstevel@tonic-gate  * .ret -1 - failed write - message printed.
6910Sstevel@tonic-gate  */
6920Sstevel@tonic-gate int
write_header(void)6930Sstevel@tonic-gate write_header(void)
6940Sstevel@tonic-gate {
6950Sstevel@tonic-gate 	return (write_file_token(f_start));
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate static int
write_file_token(time_t when)7000Sstevel@tonic-gate write_file_token(time_t when)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	adr_t adr;			/* adr ptr */
7030Sstevel@tonic-gate 	struct timeval tv;		/* time now */
7040Sstevel@tonic-gate 	char	for_adr[16];		/* plenty of room */
7050Sstevel@tonic-gate #ifdef _LP64
7060Sstevel@tonic-gate 	char	token_id = AUT_OTHER_FILE64;
7070Sstevel@tonic-gate #else
7080Sstevel@tonic-gate 	char	token_id = AUT_OTHER_FILE32;
7090Sstevel@tonic-gate #endif
7100Sstevel@tonic-gate 	short	i = 1;
7110Sstevel@tonic-gate 	char	c = '\0';
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 	tv.tv_sec = when;
7140Sstevel@tonic-gate 	tv.tv_usec = 0;
7150Sstevel@tonic-gate 	adr_start(&adr, for_adr);
7160Sstevel@tonic-gate 	adr_char(&adr, &token_id, 1);
7170Sstevel@tonic-gate #ifdef _LP64
7180Sstevel@tonic-gate 	adr_int64(&adr, (int64_t *)&tv, 2);
7190Sstevel@tonic-gate #else
7200Sstevel@tonic-gate 	adr_int32(&adr, (int32_t *)&tv, 2);
7210Sstevel@tonic-gate #endif
7220Sstevel@tonic-gate 	adr_short(&adr, &i, 1);
7230Sstevel@tonic-gate 	adr_char(&adr, &c, 1);
7240Sstevel@tonic-gate 
7250Sstevel@tonic-gate 	if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) !=
7260Sstevel@tonic-gate 	    adr_count(&adr)) {
7270Sstevel@tonic-gate 		if (when == f_start) {
7280Sstevel@tonic-gate 			(void) sprintf(errbuf,
7290Sstevel@tonic-gate 				gettext("%s error writing header to %s. "),
7300Sstevel@tonic-gate 				ar,
7310Sstevel@tonic-gate 				f_outfile ? f_outfile :
7320Sstevel@tonic-gate 					gettext("standard output"));
7330Sstevel@tonic-gate 		} else {
7340Sstevel@tonic-gate 			(void) sprintf(errbuf,
7350Sstevel@tonic-gate 				gettext("%s error writing trailer to %s. "),
7360Sstevel@tonic-gate 				ar,
7370Sstevel@tonic-gate 				f_outfile ? f_outfile :
7380Sstevel@tonic-gate 					gettext("standard output"));
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 		perror(errbuf);
7410Sstevel@tonic-gate 		return (-1);
7420Sstevel@tonic-gate 	}
7430Sstevel@tonic-gate 	return (0);
7440Sstevel@tonic-gate }
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate /*
7480Sstevel@tonic-gate  * .func  write_trailer - write audit file trailer.
7490Sstevel@tonic-gate  * .desc  Write an audit file trailer to the output stream. The finish
7500Sstevel@tonic-gate  *	time for the trailer is the time of the last record written
7510Sstevel@tonic-gate  *	to the stream.
7520Sstevel@tonic-gate  * .ret	0 - succesful write.
7530Sstevel@tonic-gate  * .ret	-1 - failed write - message printed.
7540Sstevel@tonic-gate  */
7550Sstevel@tonic-gate static int
write_trailer(void)7560Sstevel@tonic-gate write_trailer(void)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate 	return (write_file_token(f_end));
7590Sstevel@tonic-gate }
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate /*
7630Sstevel@tonic-gate  * .func rename_outfile - rename the outfile.
7640Sstevel@tonic-gate  * .desc If the user used the -O flag they only gave us the suffix name
7650Sstevel@tonic-gate  *	for the outfile. We have to add the time stamps to put the filename
7660Sstevel@tonic-gate  *	in the proper audit file name format. The start time will be the time
7670Sstevel@tonic-gate  *	of the first record in the file and the end time will be the time of
7680Sstevel@tonic-gate  *	the last record in the file.
7690Sstevel@tonic-gate  * .ret	0 - rename succesful.
7700Sstevel@tonic-gate  * .ret	-1 - rename failed - message printed.
7710Sstevel@tonic-gate  */
7720Sstevel@tonic-gate static int
rename_outfile(void)7730Sstevel@tonic-gate rename_outfile(void)
7740Sstevel@tonic-gate {
7750Sstevel@tonic-gate 	char	f_newfile[MAXFILELEN];
7760Sstevel@tonic-gate 	char	buf1[15], buf2[15];
7770Sstevel@tonic-gate 	char	*f_file, *f_nfile, *f_time, *f_name;
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	if (f_outfile != NULL) {
7800Sstevel@tonic-gate 		/*
7810Sstevel@tonic-gate 		 * Get string representations of start and end times.
7820Sstevel@tonic-gate 		 */
7830Sstevel@tonic-gate 		derive_str(f_start, buf1);
7840Sstevel@tonic-gate 		derive_str(f_end, buf2);
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 		f_nfile = f_time = f_newfile;	/* working copy */
7870Sstevel@tonic-gate 		f_file = f_name = f_outfile;	/* their version */
7880Sstevel@tonic-gate 		while (*f_file) {
7890Sstevel@tonic-gate 			if (*f_file == '/') {	/* look for filename */
7900Sstevel@tonic-gate 				f_time = f_nfile + 1;
7910Sstevel@tonic-gate 				f_name = f_file + 1;
7920Sstevel@tonic-gate 			}
7930Sstevel@tonic-gate 			*f_nfile++ = *f_file++;	/* make copy of their version */
7940Sstevel@tonic-gate 		}
7950Sstevel@tonic-gate 		*f_time = '\0';
7960Sstevel@tonic-gate 		/* start time goes first */
7970Sstevel@tonic-gate 		(void) strcat(f_newfile, buf1);
7980Sstevel@tonic-gate 		(void) strcat(f_newfile, ".");
7990Sstevel@tonic-gate 		/* then the finish time */
8000Sstevel@tonic-gate 		(void) strcat(f_newfile, buf2);
8010Sstevel@tonic-gate 		(void) strcat(f_newfile, ".");
8020Sstevel@tonic-gate 		/* and the name they gave us */
8030Sstevel@tonic-gate 		(void) strcat(f_newfile, f_name);
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate #if AUDIT_FILE
8060Sstevel@tonic-gate 		(void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n",
8070Sstevel@tonic-gate 			f_outfile, f_newfile);
8080Sstevel@tonic-gate #endif
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate #if AUDIT_RENAME
8110Sstevel@tonic-gate 		if (rename(f_outtemp, f_newfile) == -1) {
8120Sstevel@tonic-gate 			(void) fprintf(stderr,
8130Sstevel@tonic-gate 			    "%s rename of %s to %s failed.\n",
8140Sstevel@tonic-gate 			    ar, f_outtemp, f_newfile);
8150Sstevel@tonic-gate 			return (-1);
8160Sstevel@tonic-gate 		}
8170Sstevel@tonic-gate 		f_outfile = f_newfile;
8180Sstevel@tonic-gate #else
8190Sstevel@tonic-gate 		if (rename(f_outtemp, f_outfile) == -1) {
8200Sstevel@tonic-gate 			(void) fprintf(stderr,
8210Sstevel@tonic-gate 			    gettext("%s rename of %s to %s failed.\n"),
8220Sstevel@tonic-gate 			    ar, f_outtemp, f_outfile);
8230Sstevel@tonic-gate 			return (-1);
8240Sstevel@tonic-gate 		}
8250Sstevel@tonic-gate #endif
8260Sstevel@tonic-gate 	}
8270Sstevel@tonic-gate 	return (0);
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate /*
8320Sstevel@tonic-gate  * .func open_outfile - open the outfile.
8330Sstevel@tonic-gate  * .desc Open the outfile specified by the -O option. Assign it to the
8340Sstevel@tonic-gate  *	the standard output. Get a unique temporary name to use so we
8350Sstevel@tonic-gate  *	don't clobber an existing file.
8360Sstevel@tonic-gate  * .ret	0 - no errors detected.
8370Sstevel@tonic-gate  * .ret	-1 - errors in processing (message already printed).
8380Sstevel@tonic-gate  */
8390Sstevel@tonic-gate static int
open_outfile(void)8400Sstevel@tonic-gate open_outfile(void)
8410Sstevel@tonic-gate {
8420Sstevel@tonic-gate 	int	tmpfd = -1;
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	if (f_outfile != NULL) {
8450Sstevel@tonic-gate 		f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8);
8460Sstevel@tonic-gate 		(void) strcpy(f_outtemp, f_outfile);
8470Sstevel@tonic-gate 		(void) strcat(f_outtemp, "XXXXXX");
8480Sstevel@tonic-gate 		if ((tmpfd = mkstemp(f_outtemp)) == -1) {
8490Sstevel@tonic-gate 			(void) sprintf(errbuf,
8500Sstevel@tonic-gate 			    gettext("%s couldn't create temporary file"), ar);
8510Sstevel@tonic-gate 			perror(errbuf);
8520Sstevel@tonic-gate 			return (-1);
8530Sstevel@tonic-gate 		}
8540Sstevel@tonic-gate 		(void) fflush(stdout);
8550Sstevel@tonic-gate 		if (tmpfd != fileno(stdout)) {
8560Sstevel@tonic-gate 			if ((dup2(tmpfd, fileno(stdout))) == -1) {
8570Sstevel@tonic-gate 				(void) sprintf(errbuf,
8580Sstevel@tonic-gate 				    gettext("%s can't assign %s to the "
8590Sstevel@tonic-gate 				    "standard output"), ar, f_outfile);
8600Sstevel@tonic-gate 				perror(errbuf);
8610Sstevel@tonic-gate 				return (-1);
8620Sstevel@tonic-gate 			}
8630Sstevel@tonic-gate 			(void) close(tmpfd);
8640Sstevel@tonic-gate 		}
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate 	return (0);
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate /*
8710Sstevel@tonic-gate  * .func init_options - initialize the options.
8720Sstevel@tonic-gate  * .desc Give initial and/or default values to some options.
8730Sstevel@tonic-gate  * .call init_options();
8740Sstevel@tonic-gate  * .arg	none.
8750Sstevel@tonic-gate  * .ret	void.
8760Sstevel@tonic-gate  */
8770Sstevel@tonic-gate static void
init_options(void)8780Sstevel@tonic-gate init_options(void)
8790Sstevel@tonic-gate {
8800Sstevel@tonic-gate 	struct timeval tp;
8810Sstevel@tonic-gate 	struct timezone tpz;
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	/*
8840Sstevel@tonic-gate 	 * Get current time for general use.
8850Sstevel@tonic-gate 	 */
8860Sstevel@tonic-gate 	if (gettimeofday(&tp, &tpz) == -1)
8870Sstevel@tonic-gate 		perror(gettext("auditreduce: initial getttimeofday failed"));
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	time_now = tp.tv_sec;		/* save for general use */
8900Sstevel@tonic-gate 	f_start = 0;			/* first record time default */
8910Sstevel@tonic-gate 	f_end = time_now;		/* last record time default */
8920Sstevel@tonic-gate 	m_after = 0;			/* Jan 1, 1970 00:00:00 */
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 	/*
8950Sstevel@tonic-gate 	 * Setup initial size of audit_pcbs[].
8960Sstevel@tonic-gate 	 */
8970Sstevel@tonic-gate 	pcbsize = PCB_INITSIZE;		/* initial size of file-holding pcb's */
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t));
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 	/* description of 'current' error */
9020Sstevel@tonic-gate 	error_str = gettext("initial error");
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate }
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate /*
9080Sstevel@tonic-gate  * .func a_calloc - audit calloc.
9090Sstevel@tonic-gate  * .desc Calloc with check for failure. This is called by all of the
9100Sstevel@tonic-gate  *	places that want memory.
9110Sstevel@tonic-gate  * .call ptr = a_calloc(nelem, size).
9120Sstevel@tonic-gate  * .arg	nelem - number of elements to allocate.
9130Sstevel@tonic-gate  * .arg	size - size of each element.
9140Sstevel@tonic-gate  * .ret	ptr - ptr to allocated and zeroed memory.
9150Sstevel@tonic-gate  * .ret	never - if calloc fails then we never return.
9160Sstevel@tonic-gate  */
9170Sstevel@tonic-gate void	*
a_calloc(int nelem,size_t size)9180Sstevel@tonic-gate a_calloc(int nelem, size_t size)
9190Sstevel@tonic-gate {
9200Sstevel@tonic-gate 	void	*ptr;
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	if ((ptr = calloc((unsigned)nelem, size)) == NULL) {
9230Sstevel@tonic-gate 		perror(gettext("auditreduce: memory allocation failed"));
9240Sstevel@tonic-gate 		exit(1);
9250Sstevel@tonic-gate 	}
9260Sstevel@tonic-gate 	return (ptr);
9270Sstevel@tonic-gate }
9280Sstevel@tonic-gate 
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate /*
9310Sstevel@tonic-gate  * .func init_sig - initial signal catching.
9320Sstevel@tonic-gate  *
9330Sstevel@tonic-gate  * .desc
9340Sstevel@tonic-gate  *	Setup the signal catcher to catch the SIGCHLD signal plus
9350Sstevel@tonic-gate  *	"environmental" signals -- keyboard plus other externally
9360Sstevel@tonic-gate  *	generated signals such as out of file space or cpu time.  If a
9370Sstevel@tonic-gate  *	child exits with either a non-zero exit code or was killed by
9380Sstevel@tonic-gate  *	a signal to it then we will also exit with a non-zero exit
9390Sstevel@tonic-gate  *	code. In this way abnormal conditions can be passed up to the
9400Sstevel@tonic-gate  *	root process and the entire run be halted. Also catch the int
9410Sstevel@tonic-gate  *	and quit signals. Remove the output file since it is in an
9420Sstevel@tonic-gate  *	inconsistent state.
9430Sstevel@tonic-gate  * .call ret = init_sig().
9440Sstevel@tonic-gate  * .arg none.
9450Sstevel@tonic-gate  * .ret 0 - no errors detected.
9460Sstevel@tonic-gate  * .ret -1 - signal failed (message printed).
9470Sstevel@tonic-gate  */
9480Sstevel@tonic-gate static int
init_sig(void)9490Sstevel@tonic-gate init_sig(void)
9500Sstevel@tonic-gate {
9510Sstevel@tonic-gate 	if (signal(SIGCHLD, chld_handler) == SIG_ERR) {
9520Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGCHLD signal failed"));
9530Sstevel@tonic-gate 		return (-1);
9540Sstevel@tonic-gate 	}
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 	if (signal(SIGHUP, int_handler) == SIG_ERR) {
9570Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGHUP signal failed"));
9580Sstevel@tonic-gate 		return (-1);
9590Sstevel@tonic-gate 	}
9600Sstevel@tonic-gate 	if (signal(SIGINT, int_handler) == SIG_ERR) {
9610Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGINT signal failed"));
9620Sstevel@tonic-gate 		return (-1);
9630Sstevel@tonic-gate 	}
9640Sstevel@tonic-gate 	if (signal(SIGQUIT, int_handler) == SIG_ERR) {
9650Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGQUIT signal failed"));
9660Sstevel@tonic-gate 		return (-1);
9670Sstevel@tonic-gate 	}
9680Sstevel@tonic-gate 	if (signal(SIGABRT, int_handler) == SIG_ERR) {
9690Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGABRT signal failed"));
9700Sstevel@tonic-gate 		return (-1);
9710Sstevel@tonic-gate 	}
9720Sstevel@tonic-gate 	if (signal(SIGTERM, int_handler) == SIG_ERR) {
9730Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGTERM signal failed"));
9740Sstevel@tonic-gate 		return (-1);
9750Sstevel@tonic-gate 	}
9760Sstevel@tonic-gate 	if (signal(SIGPWR, int_handler) == SIG_ERR) {
9770Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGPWR signal failed"));
9780Sstevel@tonic-gate 		return (-1);
9790Sstevel@tonic-gate 	}
9800Sstevel@tonic-gate 	if (signal(SIGXCPU, int_handler) == SIG_ERR) {
9810Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGXCPU signal failed"));
9820Sstevel@tonic-gate 		return (-1);
9830Sstevel@tonic-gate 	}
9840Sstevel@tonic-gate 	if (signal(SIGXFSZ, int_handler) == SIG_ERR) {
9850Sstevel@tonic-gate 		perror(gettext("auditreduce: SIGXFSZ signal failed"));
9860Sstevel@tonic-gate 		return (-1);
9870Sstevel@tonic-gate 	}
988*8889STon.Nguyen@Sun.COM 	if (signal(SIGSEGV, int_handler) == SIG_ERR) {
989*8889STon.Nguyen@Sun.COM 		perror(gettext("auditreduce: SIGSEGV signal failed"));
990*8889STon.Nguyen@Sun.COM 		return (-1);
991*8889STon.Nguyen@Sun.COM 	}
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 	return (0);
9940Sstevel@tonic-gate }
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate /*
9980Sstevel@tonic-gate  * .func chld_handler - handle child signals.
9990Sstevel@tonic-gate  * .desc Catch the SIGCHLD signals. Remove the root process
10000Sstevel@tonic-gate  *	output file because it is in an inconsistent state.
10010Sstevel@tonic-gate  *	Print a message giving the signal number and/or return code
10020Sstevel@tonic-gate  *	of the child who caused the signal.
10030Sstevel@tonic-gate  * .ret	void.
10040Sstevel@tonic-gate  */
10050Sstevel@tonic-gate /* ARGSUSED */
10060Sstevel@tonic-gate void
chld_handler(int sig)10070Sstevel@tonic-gate chld_handler(int sig)
10080Sstevel@tonic-gate {
10090Sstevel@tonic-gate 	int	pid;
10100Sstevel@tonic-gate 	int	status;
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 	/*
10130Sstevel@tonic-gate 	 * Get pid and reasons for cause of event.
10140Sstevel@tonic-gate 	 */
10150Sstevel@tonic-gate 	pid = wait(&status);
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate 	if (pid > 0) {
10180Sstevel@tonic-gate 		/*
10190Sstevel@tonic-gate 		 * If child received a signal or exited with a non-zero
10200Sstevel@tonic-gate 		 * exit status then print message and exit
10210Sstevel@tonic-gate 		 */
10220Sstevel@tonic-gate 		if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) ||
10230Sstevel@tonic-gate 		    (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) {
10240Sstevel@tonic-gate 			(void) fprintf(stderr,
10250Sstevel@tonic-gate 			    gettext("%s abnormal child termination - "), ar);
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate 			if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) {
10280Sstevel@tonic-gate 				psignal(WLOBYTE(status), "signal");
10290Sstevel@tonic-gate 				if (WCOREDUMP(status))
10300Sstevel@tonic-gate 					(void) fprintf(stderr,
10310Sstevel@tonic-gate 					    gettext("core dumped\n"));
10320Sstevel@tonic-gate 			}
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 			if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)
10350Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
10360Sstevel@tonic-gate 					"return code %d\n"),
10370Sstevel@tonic-gate 					WHIBYTE(status));
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 			/*
10400Sstevel@tonic-gate 			 * Get rid of outfile - it is suspect.
10410Sstevel@tonic-gate 			 */
10420Sstevel@tonic-gate 			if (f_outfile != NULL) {
10430Sstevel@tonic-gate 				(void) close_outfile();
10440Sstevel@tonic-gate 				rm_outfile();
10450Sstevel@tonic-gate 			}
10460Sstevel@tonic-gate 			/*
10470Sstevel@tonic-gate 			 * Give statistical info that may be useful.
10480Sstevel@tonic-gate 			 */
10490Sstevel@tonic-gate 			audit_stats();
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 			exit(1);
10520Sstevel@tonic-gate 		}
10530Sstevel@tonic-gate 	}
10540Sstevel@tonic-gate }
10550Sstevel@tonic-gate 
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate /*
10580Sstevel@tonic-gate  * .func	int_handler - handle quit/int signals.
10590Sstevel@tonic-gate  * .desc	Catch the keyboard and other environmental signals.
10600Sstevel@tonic-gate  *		Remove the root process output file because it is in
10610Sstevel@tonic-gate  *		an inconsistent state.
10620Sstevel@tonic-gate  * .ret	void.
10630Sstevel@tonic-gate  */
10640Sstevel@tonic-gate /* ARGSUSED */
10650Sstevel@tonic-gate void
int_handler(int sig)10660Sstevel@tonic-gate int_handler(int sig)
10670Sstevel@tonic-gate {
10680Sstevel@tonic-gate 	if (getpid() == root_pid) {
10690Sstevel@tonic-gate 		(void) close_outfile();
10700Sstevel@tonic-gate 		rm_outfile();
10710Sstevel@tonic-gate 		exit(1);
10720Sstevel@tonic-gate 	}
10730Sstevel@tonic-gate 	/*
10740Sstevel@tonic-gate 	 * For a child process don't give an error exit or the
10750Sstevel@tonic-gate 	 * parent process will catch it with the chld_handler and
10760Sstevel@tonic-gate 	 * try to erase the outfile again.
10770Sstevel@tonic-gate 	 */
10780Sstevel@tonic-gate 	exit(0);
10790Sstevel@tonic-gate }
1080