/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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.
* process.c - record/file/process functions.
* time.c - date/time handling.
*
* 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)
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#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.
*/
static int max_sproc; /* maximum number of subprocesses per process */
static int total_procs; /* number of processes in the process tree */
static int total_layers; /* number of layers in the process tree */
/*
* .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
main(int argc, char **argv)
{
int ret;
audit_pcb_t *pcb;
/* Internationalization */
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
root_pid = getpid(); /* know who is root process for error */
init_options(); /* initialize options */
init_tokens(); /* initialize token processing table */
if (init_sig()) /* initialize signals */
exit(1);
if (process_options(argc, argv))
exit(1); /* process command line options */
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.
*/
pcb = (audit_pcb_t *)a_calloc(1, sizeof (audit_pcb_t));
pcb->pcb_procno = root_pid;
pcb->pcb_flags |= PF_ROOT;
pcb->pcb_fpw = stdout;
pcb->pcb_time = -1;
/*
* Now start the whole thing rolling.
*/
if (mfork(pcb, pcbnum, 0, pcbnum - 1)) {
/*
* Error in processing somewhere. A message is already printed.
* Display usage statistics and remove the outfile.
*/
if (getpid() == root_pid) {
audit_stats();
(void) close_outfile();
rm_outfile();
}
exit(1);
}
/*
* Clean up afterwards.
* Only do outfile cleanup if we are root process.
*/
if (getpid() == root_pid) {
if ((ret = write_trailer()) == 0) { /* write trailer to file */
ret = close_outfile(); /* close the outfile */
}
/*
* 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)
delete_infiles();
}
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
mfork(audit_pcb_t *pcb, int nsp, int lo, int hi)
{
int range, procno, i, tofork, nnsp, nrem;
int fildes[2];
audit_pcb_t *pcbn;
#if AUDIT_PROC_TRACE
(void) fprintf(stderr, "mfork: nsp %d %d->%d\n", nsp, lo, hi);
#endif
/*
* The range of pcb's to process is small enough now. Do the work.
*/
if (nsp <= max_sproc) {
pcb->pcb_flags |= PF_LEAF; /* leaf in process tree */
pcb->pcb_below = audit_pcbs; /* proc pcbs from audit_pcbs */
gather_pcb(pcb, lo, hi);
trim_mem(pcb); /* trim allocated memory */
return (mproc(pcb)); /* 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.
*/
if (nsp <= max_sproc * (max_sproc - 1)) {
tofork = nsp / max_sproc;
if (nsp % max_sproc)
tofork++; /* how many to fork */
}
/*
* Fork the maximum number of processes.
*/
else {
tofork = max_sproc; /* how many to fork */
}
/*
* Allocate the nodes below us in the process tree.
*/
pcb->pcb_below = (audit_pcb_t *)
a_calloc(tofork, sizeof (*pcb));
nnsp = nsp / tofork; /* # of pcbs per forked process */
nrem = nsp % tofork; /* remainder to spread around */
/*
* 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++) {
pcbn = &pcb->pcb_below[i];
pcbn->pcb_time = -1;
if (pipe(fildes)) {
perror(gettext(
"auditreduce: couldn't get a pipe"));
return (-1);
}
/*
* Convert descriptors to streams.
*/
if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) {
perror(gettext("auditreduce: couldn't get read stream for pipe"));
return (-1);
}
if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) {
perror(gettext("auditreduce: couldn't get write stream for pipe"));
return (-1);
}
if ((procno = fork()) == -1) {
perror(gettext("auditreduce: fork failed"));
return (-1);
}
/*
* Calculate the range of pcbs from audit_pcbs [] this
* branch of the tree will be responsible for.
*/
range = (nrem > 0) ? nnsp + 1 : nnsp;
/*
* Child route.
*/
if (procno == 0) {
pcbn->pcb_procno = getpid();
c_close(pcb, i); /* close unused streams */
/*
* Continue resolving this branch.
*/
return (mfork(pcbn, range, lo, lo + range - 1));
}
/* Parent route. */
else {
pcbn->pcb_procno = i;
/* allocate buffer to hold record */
pcbn->pcb_rec = (char *)a_calloc(1,
AUDITBUFSIZE);
pcbn->pcb_size = AUDITBUFSIZE;
p_close(pcbn); /* close unused streams */
nrem--;
lo += range;
}
}
/*
* Done forking all of the subs.
*/
gather_pcb(pcb, 0, tofork - 1);
trim_mem(pcb); /* free unused memory */
return (mproc(pcb));
}
}
/*
* .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
trim_mem(audit_pcb_t *pcb)
{
int count;
size_t size;
/*
* For the root don't free anything. We need to save audit_pcbs[]
* in case we are deleting the infiles at the end.
*/
if (pcb->pcb_flags & PF_ROOT)
return;
/*
* For a leaf save its part of audit_pcbs[] and then remove it all.
*/
if (pcb->pcb_flags & PF_LEAF) {
count = pcb->pcb_count;
size = sizeof (audit_pcb_t);
/* allocate a new buffer to hold the pcbs */
pcb->pcb_below = (audit_pcb_t *)a_calloc(count, size);
/* save this pcb's portion */
(void) memcpy((void *) pcb->pcb_below,
(void *) &audit_pcbs[pcb->pcb_lo], count * size);
rm_mem(pcb);
gather_pcb(pcb, 0, count - 1);
}
/*
* If this is an intermediate node then just remove it all.
*/
else {
rm_mem(pcb);
}
}
/*
* .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
rm_mem(audit_pcb_t *pcbr)
{
int i;
audit_pcb_t *pcb;
audit_fcb_t *fcb, *fcbn;
for (i = 0; i < pcbsize; i++) {
/*
* Don't free the record buffer and fcbs for the pcbs this
* process is using.
*/
if (pcbr->pcb_flags & PF_LEAF) {
if (pcbr->pcb_lo <= i || i <= pcbr->pcb_hi)
continue;
}
pcb = &audit_pcbs[i];
free(pcb->pcb_rec);
for (fcb = pcb->pcb_first; fcb != NULL; /* */) {
fcbn = fcb->fcb_next;
free((char *)fcb);
fcb = fcbn;
}
}
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
c_close(audit_pcb_t *pcb, int i)
{
int j;
audit_pcb_t *pcbt;
/*
* Do all pcbs in parent's group up to and including us
*/
for (j = 0; j <= i; j++) {
pcbt = &pcb->pcb_below[j];
if (fclose(pcbt->pcb_fpr) == EOF) {
if (!f_quiet)
perror(gettext("auditreduce: initial close on pipe failed"));
}
/*
* Free the buffer allocated to hold incoming records.
*/
if (i != j) {
free(pcbt->pcb_rec);
}
}
}
/*
* .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
p_close(audit_pcb_t *pcbn)
{
if (fclose(pcbn->pcb_fpw) == EOF) {
if (!f_quiet)
perror(gettext("auditreduce: close for write pipe failed"));
}
}
/*
* .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)
{
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) != -1)
(void) fprintf(stderr,
gettext("%s The system allows %d files per process.\n"),
ar, rl.rlim_cur);
(void) fprintf(stderr, gettext(
"%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
ar, filenum, pcbnum, total_procs, total_layers);
}
/*
* .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
gather_pcb(audit_pcb_t *pcb, int lo, int hi)
{
pcb->pcb_lo = lo;
pcb->pcb_hi = hi;
pcb->pcb_count = hi - lo + 1;
}
/*
* .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;
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
perror("auditreduce: getrlimit");
exit(1);
}
maxfiles_proc = rl.rlim_cur;
max_sproc = maxfiles_proc - 5; /* max subprocesses per process */
/*
* Calculate how many layers the process tree has.
*/
total_layers = 1;
for (/* */; /* */; /* */) {
val = a_pow(max_sproc, total_layers);
if (val > pcbnum)
break;
total_layers++;
}
/*
* Count how many processes are in the process tree.
*/
mcount(pcbnum, 0);
#if AUDIT_PROC_TRACE
(void) fprintf(stderr,
"pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
pcbnum, filenum, maxfiles_proc, max_sproc,
total_layers, total_procs);
#endif
}
static int
a_pow(int base, int exp)
{
int i;
int answer;
if (exp == 0) {
answer = 1;
} else {
answer = base;
for (i = 0; i < (exp - 1); i++)
answer *= base;
}
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
mcount(int nsp, int lo)
{
int range, i, tofork, nnsp, nrem;
total_procs++; /* count another process created */
if (nsp > max_sproc) {
if (nsp <= max_sproc * (max_sproc - 1)) {
tofork = nsp / max_sproc;
if (nsp % max_sproc)
tofork++;
} else {
tofork = max_sproc;
}
nnsp = nsp / tofork;
nrem = nsp % tofork;
for (i = 0; i < tofork; i++) {
range = (nrem > 0) ? nnsp + 1 : nnsp;
mcount(range, lo);
nrem--;
lo += range;
}
}
}
/*
* .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;
audit_pcb_t *pcb;
audit_fcb_t *fcb;
for (i = 0; i < pcbsize; i++) {
pcb = &audit_pcbs[i];
fcb = pcb->pcb_dfirst;
while (fcb != NULL) {
/*
* 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 (fcb->fcb_flags & FF_DELETE) {
if (unlink(fcb->fcb_file)) {
if (f_verbose) {
(void) sprintf(errbuf, gettext(
"%s delete on %s failed"),
ar, fcb->fcb_file);
}
perror(errbuf);
}
}
fcb = fcb->fcb_next;
}
}
}
/*
* .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) {
if (unlink(f_outtemp) == -1) {
(void) sprintf(errbuf,
gettext("%s delete on %s failed"),
ar, f_outtemp);
perror(errbuf);
}
}
#else
(void) fprintf(stderr,
gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
ar,
(f_outfile == NULL) ? gettext("standard output") : f_outfile);
#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)
{
if (fclose(stdout) == EOF) {
(void) sprintf(errbuf, gettext("%s close on %s failed"),
ar, f_outfile ? f_outfile : "standard output");
perror(errbuf);
return (-1);
}
(void) fsync(fileno(stdout));
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
write_file_token(time_t when)
{
adr_t adr; /* adr ptr */
struct timeval tv; /* time now */
char for_adr[16]; /* plenty of room */
#ifdef _LP64
char token_id = AUT_OTHER_FILE64;
#else
char token_id = AUT_OTHER_FILE32;
#endif
short i = 1;
char c = '\0';
tv.tv_sec = when;
tv.tv_usec = 0;
adr_start(&adr, for_adr);
adr_char(&adr, &token_id, 1);
#ifdef _LP64
adr_int64(&adr, (int64_t *)&tv, 2);
#else
adr_int32(&adr, (int32_t *)&tv, 2);
#endif
adr_short(&adr, &i, 1);
adr_char(&adr, &c, 1);
if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) !=
adr_count(&adr)) {
if (when == f_start) {
(void) sprintf(errbuf,
gettext("%s error writing header to %s. "),
ar,
f_outfile ? f_outfile :
gettext("standard output"));
} else {
(void) sprintf(errbuf,
gettext("%s error writing trailer to %s. "),
ar,
f_outfile ? f_outfile :
gettext("standard output"));
}
perror(errbuf);
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)
{
char f_newfile[MAXFILELEN];
char buf1[15], buf2[15];
char *f_file, *f_nfile, *f_time, *f_name;
if (f_outfile != NULL) {
/*
* Get string representations of start and end times.
*/
derive_str(f_start, buf1);
derive_str(f_end, buf2);
f_nfile = f_time = f_newfile; /* working copy */
f_file = f_name = f_outfile; /* their version */
while (*f_file) {
if (*f_file == '/') { /* look for filename */
f_time = f_nfile + 1;
f_name = f_file + 1;
}
*f_nfile++ = *f_file++; /* make copy of their version */
}
*f_time = '\0';
/* start time goes first */
(void) strcat(f_newfile, buf1);
(void) strcat(f_newfile, ".");
/* then the finish time */
(void) strcat(f_newfile, buf2);
(void) strcat(f_newfile, ".");
/* and the name they gave us */
(void) strcat(f_newfile, f_name);
#if AUDIT_FILE
(void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n",
f_outfile, f_newfile);
#endif
#if AUDIT_RENAME
if (rename(f_outtemp, f_newfile) == -1) {
(void) fprintf(stderr,
"%s rename of %s to %s failed.\n",
ar, f_outtemp, f_newfile);
return (-1);
}
f_outfile = f_newfile;
#else
if (rename(f_outtemp, f_outfile) == -1) {
(void) fprintf(stderr,
gettext("%s rename of %s to %s failed.\n"),
ar, f_outtemp, f_outfile);
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)
{
int tmpfd = -1;
if (f_outfile != NULL) {
f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8);
(void) strcpy(f_outtemp, f_outfile);
(void) strcat(f_outtemp, "XXXXXX");
if ((tmpfd = mkstemp(f_outtemp)) == -1) {
(void) sprintf(errbuf,
gettext("%s couldn't create temporary file"), ar);
perror(errbuf);
return (-1);
}
(void) fflush(stdout);
if (tmpfd != fileno(stdout)) {
if ((dup2(tmpfd, fileno(stdout))) == -1) {
(void) sprintf(errbuf,
gettext("%s can't assign %s to the "
"standard output"), ar, f_outfile);
perror(errbuf);
return (-1);
}
(void) close(tmpfd);
}
}
return (0);
}
/*
* .func init_options - initialize the options.
* .desc Give initial and/or default values to some options.
* .call init_options();
* .arg none.
* .ret void.
*/
static void
init_options(void)
{
struct timeval tp;
struct timezone tpz;
/*
* Get current time for general use.
*/
if (gettimeofday(&tp, &tpz) == -1)
perror(gettext("auditreduce: initial getttimeofday failed"));
time_now = tp.tv_sec; /* save for general use */
f_start = 0; /* first record time default */
f_end = time_now; /* last record time default */
m_after = 0; /* Jan 1, 1970 00:00:00 */
/*
* Setup initial size of audit_pcbs[].
*/
pcbsize = PCB_INITSIZE; /* initial size of file-holding pcb's */
audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t));
/* description of 'current' error */
error_str = gettext("initial 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 *
a_calloc(int nelem, size_t size)
{
void *ptr;
if ((ptr = calloc((unsigned)nelem, size)) == NULL) {
perror(gettext("auditreduce: memory allocation failed"));
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)
{
if (signal(SIGCHLD, chld_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGCHLD signal failed"));
return (-1);
}
if (signal(SIGHUP, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGHUP signal failed"));
return (-1);
}
if (signal(SIGINT, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGINT signal failed"));
return (-1);
}
if (signal(SIGQUIT, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGQUIT signal failed"));
return (-1);
}
if (signal(SIGABRT, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGABRT signal failed"));
return (-1);
}
if (signal(SIGTERM, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGTERM signal failed"));
return (-1);
}
if (signal(SIGPWR, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGPWR signal failed"));
return (-1);
}
if (signal(SIGXCPU, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGXCPU signal failed"));
return (-1);
}
if (signal(SIGXFSZ, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGXFSZ signal failed"));
return (-1);
}
if (signal(SIGSEGV, int_handler) == SIG_ERR) {
perror(gettext("auditreduce: SIGSEGV signal failed"));
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.
* Print a message giving the signal number and/or return code
* of the child who caused the signal.
* .ret void.
*/
/* ARGSUSED */
void
chld_handler(int sig)
{
int pid;
int status;
/*
* Get pid and reasons for cause of event.
*/
pid = wait(&status);
if (pid > 0) {
/*
* If child received a signal or exited with a non-zero
* exit status then print message and exit
*/
if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) ||
(WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) {
(void) fprintf(stderr,
gettext("%s abnormal child termination - "), ar);
if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) {
psignal(WLOBYTE(status), "signal");
if (WCOREDUMP(status))
(void) fprintf(stderr,
gettext("core dumped\n"));
}
if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)
(void) fprintf(stderr, gettext(
"return code %d\n"),
WHIBYTE(status));
/*
* Get rid of outfile - it is suspect.
*/
if (f_outfile != NULL) {
(void) close_outfile();
rm_outfile();
}
/*
* Give statistical info that may be useful.
*/
audit_stats();
exit(1);
}
}
}
/*
* .func int_handler - handle quit/int signals.
* .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
int_handler(int sig)
{
if (getpid() == root_pid) {
(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);
}