/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* The Secure SunOS audit reduction tool - auditreduce.
* Document SM0071 is the primary source of information on auditreduce.
*
* Composed of 4 source modules:
* main.c - main driver.
* option.c - command line option processing.
*
* Main(), write_header(), audit_stats(), and a_calloc()
* are the only functions visible outside this module.
*/
#include <siginfo.h>
#include <locale.h>
#include <libintl.h>
#include "auditr.h"
#include "auditrd.h"
#if !defined(TEXT_DOMAIN)
#endif
extern void derive_str(time_t, char *);
extern int process_options(int, char **);
extern int mproc(audit_pcb_t *);
extern void init_tokens(void); /* shared with praudit */
static int a_pow(int, int);
static void calc_procs(void);
static void chld_handler(int);
static int close_outfile(void);
static void c_close(audit_pcb_t *, int);
static void delete_infiles(void);
static void gather_pcb(audit_pcb_t *, int, int);
static void init_options(void);
static int init_sig(void);
static void int_handler(int);
static int mfork(audit_pcb_t *, int, int, int);
static void mcount(int, int);
static int open_outfile(void);
static void p_close(audit_pcb_t *);
static int rename_outfile(void);
static void rm_mem(audit_pcb_t *);
static void rm_outfile(void);
static void trim_mem(audit_pcb_t *);
static int write_file_token(time_t);
static int write_trailer(void);
/*
* File globals.
*/
/*
* .func main - main.
* .desc The beginning. Main() calls each of the initialization routines
* and then allocates the root pcb. Then it calls mfork() to get
* the work done.
* .call main(argc, argv).
* .arg argc - number of arguments.
* .arg argv - array of pointers to arguments.
* .ret 0 - via exit() - no errors detected.
* .ret 1 - via exit() - errors detected (messages printed).
*/
int
{
int ret;
/* Internationalization */
(void) textdomain(TEXT_DOMAIN);
init_options(); /* initialize options */
init_tokens(); /* initialize token processing table */
if (init_sig()) /* initialize signals */
exit(1);
if (open_outfile()) /* setup root process output stream */
exit(1);
calc_procs(); /* see how many subprocesses we need */
/*
* Allocate the root pcb and set it up.
*/
/*
* Now start the whole thing rolling.
*/
/*
* Error in processing somewhere. A message is already printed.
* Display usage statistics and remove the outfile.
*/
audit_stats();
(void) close_outfile();
rm_outfile();
}
exit(1);
}
/*
* Clean up afterwards.
* Only do outfile cleanup if we are root process.
*/
}
/*
* If there was an error in cleanup then remove outfile.
*/
if (ret) {
rm_outfile();
exit(1);
}
/*
* And lastly delete the infiles if the user so wishes.
*/
if (f_delete)
}
return (0);
/*NOTREACHED*/
}
/*
* .func mfork - main fork routine.
* .desc Create a (sub-)tree of processses if needed, or just do the work
* if we have few enough groups to process. This is a recursive routine
* which stops recursing when the number of files to process is small
* enough. Each call to mfork() is responsible for a range of pcbs
* from audit_pcbs[]. This range is designated by the lo and hi
* arguments (inclusive). If the number of pcbs is small enough
* then we have hit a leaf of the tree and mproc() is called to
* do the processing. Otherwise we fork some processes and break
* the range of pcbs up amongst them.
* .call ret = mfork(pcb, nsp, lo, hi).
* .arg pcb - ptr to pcb that is root node of the to-be-created tree.
* .arg nsp - number of sub-processes this tree must process.
* .arg lo - lower-limit of process number range. Index into audit_pcbs.
* .arg hi - higher limit of pcb range. Index into audit_pcbs.
* .ret 0 - succesful completion.
* .ret -1 - error encountered in processing - message already printed.
*/
static int
{
#if AUDIT_PROC_TRACE
#endif
/*
* The range of pcb's to process is small enough now. Do the work.
*/
}
/*
* Too many pcb's for one process - must fork.
* Try to balance the tree as it grows and make it short and fat.
* The thing to minimize is the number of times a record passes
* through a pipe.
*/
else {
/*
* Fork less than the maximum number of processes.
*/
tofork++; /* how many to fork */
}
/*
* Fork the maximum number of processes.
*/
else {
}
/*
* Allocate the nodes below us in the process tree.
*/
/*
* Loop to fork all of the subs. Open a pipe for each.
* If there are any errors in pipes, forks, or getting streams
* for the pipes then quit altogether.
*/
for (i = 0; i < tofork; i++) {
"auditreduce: couldn't get a pipe"));
return (-1);
}
/*
* Convert descriptors to streams.
*/
return (-1);
}
return (-1);
}
return (-1);
}
/*
* Calculate the range of pcbs from audit_pcbs [] this
* branch of the tree will be responsible for.
*/
/*
* Child route.
*/
if (procno == 0) {
/*
* Continue resolving this branch.
*/
}
/* Parent route. */
else {
pcbn->pcb_procno = i;
/* allocate buffer to hold record */
nrem--;
}
}
/*
* Done forking all of the subs.
*/
}
}
/*
* .func trim_mem - trim memory usage.
* .desc Free un-needed allocated memory.
* .call trim_mem(pcb).
* .arg pcb - ptr to pcb for current process.
* .ret void.
*/
static void
{
int count;
/*
* For the root don't free anything. We need to save audit_pcbs[]
* in case we are deleting the infiles at the end.
*/
return;
/*
* For a leaf save its part of audit_pcbs[] and then remove it all.
*/
size = sizeof (audit_pcb_t);
/* allocate a new buffer to hold the pcbs */
/* save this pcb's portion */
}
/*
* If this is an intermediate node then just remove it all.
*/
else {
}
}
/*
* .func rm_mem - remove memory.
* .desc Remove unused memory associated with audit_pcbs[]. For each
* pcb in audit_pcbs[] free the record buffer and all of
* the fcbs. Then free audit_pcbs[].
* .call rm_mem(pcbr).
* .arg pcbr - ptr to pcb of current process.
* .ret void.
*/
static void
{
int i;
for (i = 0; i < pcbsize; i++) {
/*
* Don't free the record buffer and fcbs for the pcbs this
* process is using.
*/
continue;
}
pcb = &audit_pcbs[i];
}
}
free((char *)audit_pcbs);
}
/*
* .func c_close - close unused streams.
* .desc This is called for each child process just after being born.
* The child closes the read stream for the pipe to its parent.
* It also closes the read streams for the other children that
* have been born before it. If any closes fail a warning message
* is printed, but processing continues.
* .call ret = c_close(pcb, i).
* .arg pcb - ptr to the child's parent pcb.
* .arg i - iteration # of child in forking loop.
* .ret void.
*/
static void
{
int j;
/*
* Do all pcbs in parent's group up to and including us
*/
for (j = 0; j <= i; j++) {
if (!f_quiet)
}
/*
* Free the buffer allocated to hold incoming records.
*/
if (i != j) {
}
}
}
/*
* .func p_close - close unused streams for parent.
* .desc Called by the parent right after forking a child.
* Closes the write stream on the pipe to the child since
* we will never use it.
* .call p_close(pcbn),
* .arg pcbn - ptr to pcb.
* .ret void.
*/
static void
{
if (!f_quiet)
}
}
/*
* .func audit_stats - print statistics.
* .desc Print usage statistics for the user if the run fails.
* Tells them how many files they had and how many groups this
* totalled. Also tell them how many layers and processes the
* process tree had.
* .call audit_stats().
* .arg none.
* .ret void.
*/
void
audit_stats(void)
{
gettext("%s The system allows %d files per process.\n"),
"%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
}
/*
* .func gather_pcb - gather pcbs.
* .desc Gather together the range of the sub-processes that we are
* responsible for. For a pcb that controls processes this is all
* of the sub-processes that it forks. For a pcb that controls
* files this is the the range of pcbs from audit_pcbs[].
* .call gather_pcb(pcb, lo, hi).
* .arg pcb - ptr to pcb.
* .arg lo - lo index into pcb_below.
* .arg hi - hi index into pcb_below.
* .ret void.
*/
static void
{
}
/*
* .func calc_procs - calculate process parameters.
* .desc Calculate the current run's paramters regarding how many
* processes will have to be forked (maybe none).
* 5 is subtracted from maxfiles_proc to allow for stdin, stdout,
* stderr, and the pipe to a parent process. The outfile
* in the root process is assigned to stdout. The unused half of each
* pipe is closed, to allow for more connections, but we still
* have to have the 5th spot because in order to get the pipe
* we need 2 descriptors up front.
* .call calc_procs().
* .arg none.
* .ret void.
*/
static void
calc_procs(void)
{
int val;
int maxfiles_proc;
perror("auditreduce: getrlimit");
exit(1);
}
/*
* Calculate how many layers the process tree has.
*/
total_layers = 1;
for (/* */; /* */; /* */) {
break;
total_layers++;
}
/*
* Count how many processes are in the process tree.
*/
#if AUDIT_PROC_TRACE
"pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
#endif
}
static int
{
int i;
int answer;
if (exp == 0) {
answer = 1;
} else {
for (i = 0; i < (exp - 1); i++)
}
return (answer);
}
/*
* .func mcount - main count.
* .desc Go through the motions of building the process tree just
* to count how many processes there are. Don't really
* build anything. Answer is in global var total_procs.
* .call mcount(nsp, lo).
* .arg nsp - number of subs for this tree branch.
* .arg lo - lo side of range of subs.
* .ret void.
*/
static void
{
total_procs++; /* count another process created */
tofork++;
} else {
}
for (i = 0; i < tofork; i++) {
nrem--;
}
}
}
/*
* .func delete_infiles - delete the input files.
* .desc If the user asked us to (via 'D' flag) then unlink the input files.
* .call ret = delete_infiles().
* .arg none.
* .ret void.
*/
static void
delete_infiles(void)
{
int i;
for (i = 0; i < pcbsize; i++) {
pcb = &audit_pcbs[i];
/*
* Only delete a file if it was succesfully processed.
* If there were any read errors or bad records
* then don't delete it.
* There may still be unprocessed records in it.
*/
if (f_verbose) {
"%s delete on %s failed"),
}
}
}
}
}
}
/*
* .func rm_outfile - remove the outfile.
* .desc Remove the file we are writing the records to. We do this if
* processing failed and we are quitting before finishing.
* Update - don't actually remove the outfile, but generate
* a warning about its possible heathen nature.
* .call ret = rm_outfile().
* .arg none.
* .ret void.
*/
static void
rm_outfile(void)
{
#if 0
if (f_outfile) {
gettext("%s delete on %s failed"),
}
}
#else
gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
ar,
#endif
}
/*
* .func close_outfile - close the outfile.
* .desc Close the file we are writing records to.
* .call ret = close_outfile().
* .arg none.
* .ret 0 - close was succesful.
* .ret -1 - close failed.
*/
static int
close_outfile(void)
{
return (-1);
}
return (rename_outfile());
}
/*
* .func write_header - write audit file header.
* .desc Write an audit file header to the output stream. The time in the
* header is the time of the first record written to the stream. This
* routine is called by the process handling the root node of the
* process tree just before it writes the first record to the output
* stream.
* .ret 0 - succesful write.
* .ret -1 - failed write - message printed.
*/
int
write_header(void)
{
return (write_file_token(f_start));
}
static int
{
#ifdef _LP64
#else
#endif
short i = 1;
char c = '\0';
#ifdef _LP64
#else
#endif
gettext("%s error writing header to %s. "),
ar,
gettext("standard output"));
} else {
gettext("%s error writing trailer to %s. "),
ar,
gettext("standard output"));
}
return (-1);
}
return (0);
}
/*
* .func write_trailer - write audit file trailer.
* .desc Write an audit file trailer to the output stream. The finish
* time for the trailer is the time of the last record written
* to the stream.
* .ret 0 - succesful write.
* .ret -1 - failed write - message printed.
*/
static int
write_trailer(void)
{
return (write_file_token(f_end));
}
/*
* .func rename_outfile - rename the outfile.
* .desc If the user used the -O flag they only gave us the suffix name
* for the outfile. We have to add the time stamps to put the filename
* in the proper audit file name format. The start time will be the time
* of the first record in the file and the end time will be the time of
* the last record in the file.
* .ret 0 - rename succesful.
* .ret -1 - rename failed - message printed.
*/
static int
rename_outfile(void)
{
/*
* Get string representations of start and end times.
*/
while (*f_file) {
}
}
*f_time = '\0';
/* start time goes first */
/* then the finish time */
/* and the name they gave us */
#if AUDIT_FILE
#endif
#if AUDIT_RENAME
"%s rename of %s to %s failed.\n",
return (-1);
}
#else
gettext("%s rename of %s to %s failed.\n"),
return (-1);
}
#endif
}
return (0);
}
/*
* .func open_outfile - open the outfile.
* .desc Open the outfile specified by the -O option. Assign it to the
* the standard output. Get a unique temporary name to use so we
* don't clobber an existing file.
* .ret 0 - no errors detected.
* .ret -1 - errors in processing (message already printed).
*/
static int
open_outfile(void)
{
return (-1);
}
gettext("%s can't assign %s to the "
return (-1);
}
}
}
return (0);
}
/*
* .func init_options - initialize the options.
* .call init_options();
* .arg none.
* .ret void.
*/
static void
init_options(void)
{
/*
* Get current time for general use.
*/
f_start = 0; /* first record time default */
m_after = 0; /* Jan 1, 1970 00:00:00 */
/*
* Setup initial size of audit_pcbs[].
*/
/* description of 'current' error */
}
/*
* .func a_calloc - audit calloc.
* .desc Calloc with check for failure. This is called by all of the
* places that want memory.
* .call ptr = a_calloc(nelem, size).
* .arg nelem - number of elements to allocate.
* .arg size - size of each element.
* .ret ptr - ptr to allocated and zeroed memory.
* .ret never - if calloc fails then we never return.
*/
void *
{
void *ptr;
exit(1);
}
return (ptr);
}
/*
* .func init_sig - initial signal catching.
*
* .desc
* Setup the signal catcher to catch the SIGCHLD signal plus
* "environmental" signals -- keyboard plus other externally
* generated signals such as out of file space or cpu time. If a
* child exits with either a non-zero exit code or was killed by
* a signal to it then we will also exit with a non-zero exit
* code. In this way abnormal conditions can be passed up to the
* root process and the entire run be halted. Also catch the int
* and quit signals. Remove the output file since it is in an
* inconsistent state.
* .call ret = init_sig().
* .arg none.
* .ret 0 - no errors detected.
* .ret -1 - signal failed (message printed).
*/
static int
init_sig(void)
{
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (0);
}
/*
* .func chld_handler - handle child signals.
* .desc Catch the SIGCHLD signals. Remove the root process
* output file because it is in an inconsistent state.
* of the child who caused the signal.
* .ret void.
*/
/* ARGSUSED */
void
{
int pid;
int status;
/*
* Get pid and reasons for cause of event.
*/
if (pid > 0) {
/*
* If child received a signal or exited with a non-zero
* exit status then print message and exit
*/
gettext("core dumped\n"));
}
"return code %d\n"),
/*
* Get rid of outfile - it is suspect.
*/
(void) close_outfile();
rm_outfile();
}
/*
* Give statistical info that may be useful.
*/
audit_stats();
exit(1);
}
}
}
/*
* .desc Catch the keyboard and other environmental signals.
* Remove the root process output file because it is in
* an inconsistent state.
* .ret void.
*/
/* ARGSUSED */
void
{
(void) close_outfile();
rm_outfile();
exit(1);
}
/*
* For a child process don't give an error exit or the
* parent process will catch it with the chld_handler and
* try to erase the outfile again.
*/
exit(0);
}