| /* |
| * 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); |
| } |