/* * 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. */ /* * Copyright 2019 Joyent, Inc. * Copyright 2024 Oxide Computer Company */ /* * 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 #include #include #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) { static 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); }