1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 /*
30 * The Secure SunOS audit reduction tool - auditreduce.
31 * Document SM0071 is the primary source of information on auditreduce.
32 *
33 * Composed of 4 source modules:
34 * main.c - main driver.
35 * option.c - command line option processing.
36 * process.c - record/file/process functions.
37 * time.c - date/time handling.
38 *
39 * Main(), write_header(), audit_stats(), and a_calloc()
40 * are the only functions visible outside this module.
41 */
42
43 #include <siginfo.h>
44 #include <locale.h>
45 #include <libintl.h>
46 #include "auditr.h"
47 #include "auditrd.h"
48
49 #if !defined(TEXT_DOMAIN)
50 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
51 #endif
52
53 extern void derive_str(time_t, char *);
54 extern int process_options(int, char **);
55 extern int mproc(audit_pcb_t *);
56 extern void init_tokens(void); /* shared with praudit */
57
58 static int a_pow(int, int);
59 static void calc_procs(void);
60 static void chld_handler(int);
61 static int close_outfile(void);
62 static void c_close(audit_pcb_t *, int);
63 static void delete_infiles(void);
64 static void gather_pcb(audit_pcb_t *, int, int);
65 static void init_options(void);
66 static int init_sig(void);
67 static void int_handler(int);
68 static int mfork(audit_pcb_t *, int, int, int);
69 static void mcount(int, int);
70 static int open_outfile(void);
71 static void p_close(audit_pcb_t *);
72 static int rename_outfile(void);
73 static void rm_mem(audit_pcb_t *);
74 static void rm_outfile(void);
75 static void trim_mem(audit_pcb_t *);
76 static int write_file_token(time_t);
77 static int write_trailer(void);
78
79 /*
80 * File globals.
81 */
82 static int max_sproc; /* maximum number of subprocesses per process */
83 static int total_procs; /* number of processes in the process tree */
84 static int total_layers; /* number of layers in the process tree */
85
86 char errbuf[ERRBUF_SZ]; /* for creating error messages with sprintf */
87
88 /*
89 * .func main - main.
90 * .desc The beginning. Main() calls each of the initialization routines
91 * and then allocates the root pcb. Then it calls mfork() to get
92 * the work done.
93 * .call main(argc, argv).
94 * .arg argc - number of arguments.
95 * .arg argv - array of pointers to arguments.
96 * .ret 0 - via exit() - no errors detected.
97 * .ret 1 - via exit() - errors detected (messages printed).
98 */
99 int
100 main(int argc, char **argv)
101 {
102 int ret;
103 audit_pcb_t *pcb;
104
105 /* Internationalization */
106 (void) setlocale(LC_ALL, "");
107 (void) textdomain(TEXT_DOMAIN);
108
109 root_pid = getpid(); /* know who is root process for error */
110 init_options(); /* initialize options */
111 init_tokens(); /* initialize token processing table */
112 if (init_sig()) /* initialize signals */
113 exit(1);
114 if (process_options(argc, argv))
115 exit(1); /* process command line options */
116 if (open_outfile()) /* setup root process output stream */
117 exit(1);
118 calc_procs(); /* see how many subprocesses we need */
119 /*
120 * Allocate the root pcb and set it up.
121 */
122 pcb = (audit_pcb_t *)a_calloc(1, sizeof (audit_pcb_t));
123 pcb->pcb_procno = root_pid;
124 pcb->pcb_flags |= PF_ROOT;
125 pcb->pcb_fpw = stdout;
126 pcb->pcb_time = -1;
127 /*
128 * Now start the whole thing rolling.
129 */
130 if (mfork(pcb, pcbnum, 0, pcbnum - 1)) {
131 /*
132 * Error in processing somewhere. A message is already printed.
133 * Display usage statistics and remove the outfile.
134 */
135 if (getpid() == root_pid) {
136 audit_stats();
137 (void) close_outfile();
138 rm_outfile();
139 }
140 exit(1);
141 }
142 /*
143 * Clean up afterwards.
144 * Only do outfile cleanup if we are root process.
145 */
146 if (getpid() == root_pid) {
147 if ((ret = write_trailer()) == 0) { /* write trailer to file */
148
149 ret = close_outfile(); /* close the outfile */
150 }
151 /*
152 * If there was an error in cleanup then remove outfile.
153 */
154 if (ret) {
155 rm_outfile();
156 exit(1);
157 }
158 /*
159 * And lastly delete the infiles if the user so wishes.
160 */
161 if (f_delete)
162 delete_infiles();
163 }
164 return (0);
165 /*NOTREACHED*/
166 }
167
168
169 /*
170 * .func mfork - main fork routine.
171 * .desc Create a (sub-)tree of processses if needed, or just do the work
172 * if we have few enough groups to process. This is a recursive routine
173 * which stops recursing when the number of files to process is small
174 * enough. Each call to mfork() is responsible for a range of pcbs
175 * from audit_pcbs[]. This range is designated by the lo and hi
176 * arguments (inclusive). If the number of pcbs is small enough
177 * then we have hit a leaf of the tree and mproc() is called to
178 * do the processing. Otherwise we fork some processes and break
179 * the range of pcbs up amongst them.
180 * .call ret = mfork(pcb, nsp, lo, hi).
181 * .arg pcb - ptr to pcb that is root node of the to-be-created tree.
182 * .arg nsp - number of sub-processes this tree must process.
183 * .arg lo - lower-limit of process number range. Index into audit_pcbs.
184 * .arg hi - higher limit of pcb range. Index into audit_pcbs.
185 * .ret 0 - succesful completion.
186 * .ret -1 - error encountered in processing - message already printed.
187 */
188 static int
189 mfork(audit_pcb_t *pcb, int nsp, int lo, int hi)
190 {
191 int range, procno, i, tofork, nnsp, nrem;
192 int fildes[2];
193 audit_pcb_t *pcbn;
194
195 #if AUDIT_PROC_TRACE
196 (void) fprintf(stderr, "mfork: nsp %d %d->%d\n", nsp, lo, hi);
197 #endif
198
199 /*
200 * The range of pcb's to process is small enough now. Do the work.
201 */
202 if (nsp <= max_sproc) {
203 pcb->pcb_flags |= PF_LEAF; /* leaf in process tree */
204 pcb->pcb_below = audit_pcbs; /* proc pcbs from audit_pcbs */
205 gather_pcb(pcb, lo, hi);
206 trim_mem(pcb); /* trim allocated memory */
207 return (mproc(pcb)); /* do the work */
208 }
209 /*
210 * Too many pcb's for one process - must fork.
211 * Try to balance the tree as it grows and make it short and fat.
212 * The thing to minimize is the number of times a record passes
213 * through a pipe.
214 */
215 else {
216 /*
217 * Fork less than the maximum number of processes.
218 */
219 if (nsp <= max_sproc * (max_sproc - 1)) {
220 tofork = nsp / max_sproc;
221 if (nsp % max_sproc)
222 tofork++; /* how many to fork */
223 }
224 /*
225 * Fork the maximum number of processes.
226 */
227 else {
228 tofork = max_sproc; /* how many to fork */
229 }
230 /*
231 * Allocate the nodes below us in the process tree.
232 */
233 pcb->pcb_below = (audit_pcb_t *)
234 a_calloc(tofork, sizeof (*pcb));
235 nnsp = nsp / tofork; /* # of pcbs per forked process */
236 nrem = nsp % tofork; /* remainder to spread around */
237 /*
238 * Loop to fork all of the subs. Open a pipe for each.
239 * If there are any errors in pipes, forks, or getting streams
240 * for the pipes then quit altogether.
241 */
242 for (i = 0; i < tofork; i++) {
243 pcbn = &pcb->pcb_below[i];
244 pcbn->pcb_time = -1;
245 if (pipe(fildes)) {
246 perror(gettext(
247 "auditreduce: couldn't get a pipe"));
248 return (-1);
249 }
250 /*
251 * Convert descriptors to streams.
252 */
253 if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) {
254 perror(gettext("auditreduce: couldn't get read stream for pipe"));
255 return (-1);
256 }
257 if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) {
258 perror(gettext("auditreduce: couldn't get write stream for pipe"));
259 return (-1);
260 }
261 if ((procno = fork()) == -1) {
262 perror(gettext("auditreduce: fork failed"));
263 return (-1);
264 }
265 /*
266 * Calculate the range of pcbs from audit_pcbs [] this
267 * branch of the tree will be responsible for.
268 */
269 range = (nrem > 0) ? nnsp + 1 : nnsp;
270 /*
271 * Child route.
272 */
273 if (procno == 0) {
274 pcbn->pcb_procno = getpid();
275 c_close(pcb, i); /* close unused streams */
276 /*
277 * Continue resolving this branch.
278 */
279 return (mfork(pcbn, range, lo, lo + range - 1));
280 }
281 /* Parent route. */
282 else {
283 pcbn->pcb_procno = i;
284 /* allocate buffer to hold record */
285 pcbn->pcb_rec = (char *)a_calloc(1,
286 AUDITBUFSIZE);
287 pcbn->pcb_size = AUDITBUFSIZE;
288 p_close(pcbn); /* close unused streams */
289
290 nrem--;
291 lo += range;
292 }
293 }
294 /*
295 * Done forking all of the subs.
296 */
297 gather_pcb(pcb, 0, tofork - 1);
298 trim_mem(pcb); /* free unused memory */
299 return (mproc(pcb));
300 }
301 }
302
303
304 /*
305 * .func trim_mem - trim memory usage.
306 * .desc Free un-needed allocated memory.
307 * .call trim_mem(pcb).
308 * .arg pcb - ptr to pcb for current process.
309 * .ret void.
310 */
311 static void
312 trim_mem(audit_pcb_t *pcb)
313 {
314 int count;
315 size_t size;
316
317 /*
318 * For the root don't free anything. We need to save audit_pcbs[]
319 * in case we are deleting the infiles at the end.
320 */
321 if (pcb->pcb_flags & PF_ROOT)
322 return;
323 /*
324 * For a leaf save its part of audit_pcbs[] and then remove it all.
325 */
326 if (pcb->pcb_flags & PF_LEAF) {
327 count = pcb->pcb_count;
328 size = sizeof (audit_pcb_t);
329 /* allocate a new buffer to hold the pcbs */
330 pcb->pcb_below = (audit_pcb_t *)a_calloc(count, size);
331 /* save this pcb's portion */
332 (void) memcpy((void *) pcb->pcb_below,
333 (void *) &audit_pcbs[pcb->pcb_lo], count * size);
334 rm_mem(pcb);
335 gather_pcb(pcb, 0, count - 1);
336 }
337 /*
338 * If this is an intermediate node then just remove it all.
339 */
340 else {
341 rm_mem(pcb);
342 }
343 }
344
345
346 /*
347 * .func rm_mem - remove memory.
348 * .desc Remove unused memory associated with audit_pcbs[]. For each
349 * pcb in audit_pcbs[] free the record buffer and all of
350 * the fcbs. Then free audit_pcbs[].
351 * .call rm_mem(pcbr).
352 * .arg pcbr - ptr to pcb of current process.
353 * .ret void.
354 */
355 static void
356 rm_mem(audit_pcb_t *pcbr)
357 {
358 int i;
359 audit_pcb_t *pcb;
360 audit_fcb_t *fcb, *fcbn;
361
362 for (i = 0; i < pcbsize; i++) {
363 /*
364 * Don't free the record buffer and fcbs for the pcbs this
365 * process is using.
366 */
367 if (pcbr->pcb_flags & PF_LEAF) {
368 if (pcbr->pcb_lo <= i || i <= pcbr->pcb_hi)
369 continue;
370 }
371 pcb = &audit_pcbs[i];
372 free(pcb->pcb_rec);
373 for (fcb = pcb->pcb_first; fcb != NULL; /* */) {
374 fcbn = fcb->fcb_next;
375 free((char *)fcb);
376 fcb = fcbn;
377 }
378 }
379 free((char *)audit_pcbs);
380 }
381
382
383 /*
384 * .func c_close - close unused streams.
385 * .desc This is called for each child process just after being born.
386 * The child closes the read stream for the pipe to its parent.
387 * It also closes the read streams for the other children that
388 * have been born before it. If any closes fail a warning message
389 * is printed, but processing continues.
390 * .call ret = c_close(pcb, i).
391 * .arg pcb - ptr to the child's parent pcb.
392 * .arg i - iteration # of child in forking loop.
393 * .ret void.
394 */
395 static void
396 c_close(audit_pcb_t *pcb, int i)
397 {
398 int j;
399 audit_pcb_t *pcbt;
400
401 /*
402 * Do all pcbs in parent's group up to and including us
403 */
404 for (j = 0; j <= i; j++) {
405 pcbt = &pcb->pcb_below[j];
406 if (fclose(pcbt->pcb_fpr) == EOF) {
407 if (!f_quiet)
408 perror(gettext("auditreduce: initial close on pipe failed"));
409 }
410 /*
411 * Free the buffer allocated to hold incoming records.
412 */
413 if (i != j) {
414 free(pcbt->pcb_rec);
415 }
416 }
417 }
418
419
420 /*
421 * .func p_close - close unused streams for parent.
422 * .desc Called by the parent right after forking a child.
423 * Closes the write stream on the pipe to the child since
424 * we will never use it.
425 * .call p_close(pcbn),
426 * .arg pcbn - ptr to pcb.
427 * .ret void.
428 */
429 static void
430 p_close(audit_pcb_t *pcbn)
431 {
432 if (fclose(pcbn->pcb_fpw) == EOF) {
433 if (!f_quiet)
434 perror(gettext("auditreduce: close for write pipe failed"));
435 }
436 }
437
438
439 /*
440 * .func audit_stats - print statistics.
441 * .desc Print usage statistics for the user if the run fails.
442 * Tells them how many files they had and how many groups this
443 * totalled. Also tell them how many layers and processes the
444 * process tree had.
445 * .call audit_stats().
446 * .arg none.
447 * .ret void.
448 */
449 void
450 audit_stats(void)
451 {
452 struct rlimit rl;
453
454 if (getrlimit(RLIMIT_NOFILE, &rl) != -1)
455 (void) fprintf(stderr,
456 gettext("%s The system allows %d files per process.\n"),
457 ar, rl.rlim_cur);
458 (void) fprintf(stderr, gettext(
459 "%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
460 ar, filenum, pcbnum, total_procs, total_layers);
461 }
462
463
464 /*
465 * .func gather_pcb - gather pcbs.
466 * .desc Gather together the range of the sub-processes that we are
467 * responsible for. For a pcb that controls processes this is all
468 * of the sub-processes that it forks. For a pcb that controls
469 * files this is the the range of pcbs from audit_pcbs[].
470 * .call gather_pcb(pcb, lo, hi).
471 * .arg pcb - ptr to pcb.
472 * .arg lo - lo index into pcb_below.
473 * .arg hi - hi index into pcb_below.
474 * .ret void.
475 */
476 static void
477 gather_pcb(audit_pcb_t *pcb, int lo, int hi)
478 {
479 pcb->pcb_lo = lo;
480 pcb->pcb_hi = hi;
481 pcb->pcb_count = hi - lo + 1;
482 }
483
484
485 /*
486 * .func calc_procs - calculate process parameters.
487 * .desc Calculate the current run's paramters regarding how many
488 * processes will have to be forked (maybe none).
489 * 5 is subtracted from maxfiles_proc to allow for stdin, stdout,
490 * stderr, and the pipe to a parent process. The outfile
491 * in the root process is assigned to stdout. The unused half of each
492 * pipe is closed, to allow for more connections, but we still
493 * have to have the 5th spot because in order to get the pipe
494 * we need 2 descriptors up front.
495 * .call calc_procs().
496 * .arg none.
497 * .ret void.
498 */
499 static void
500 calc_procs(void)
501 {
502 int val;
503 int maxfiles_proc;
504 struct rlimit rl;
505
506 if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
507 perror("auditreduce: getrlimit");
508 exit(1);
509 }
510
511 maxfiles_proc = rl.rlim_cur;
512
513 max_sproc = maxfiles_proc - 5; /* max subprocesses per process */
514
515 /*
516 * Calculate how many layers the process tree has.
517 */
518 total_layers = 1;
519 for (/* */; /* */; /* */) {
520 val = a_pow(max_sproc, total_layers);
521 if (val > pcbnum)
522 break;
523 total_layers++;
524 }
525 /*
526 * Count how many processes are in the process tree.
527 */
528 mcount(pcbnum, 0);
529
530 #if AUDIT_PROC_TRACE
531 (void) fprintf(stderr,
532 "pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
533 pcbnum, filenum, maxfiles_proc, max_sproc,
534 total_layers, total_procs);
535 #endif
536 }
537
538
539 static int
540 a_pow(int base, int exp)
541 {
542 int i;
543 int answer;
544
545 if (exp == 0) {
546 answer = 1;
547 } else {
548 answer = base;
549 for (i = 0; i < (exp - 1); i++)
550 answer *= base;
551 }
552 return (answer);
553 }
554
555
556 /*
557 * .func mcount - main count.
558 * .desc Go through the motions of building the process tree just
559 * to count how many processes there are. Don't really
560 * build anything. Answer is in global var total_procs.
561 * .call mcount(nsp, lo).
562 * .arg nsp - number of subs for this tree branch.
563 * .arg lo - lo side of range of subs.
564 * .ret void.
565 */
566 static void
567 mcount(int nsp, int lo)
568 {
569 int range, i, tofork, nnsp, nrem;
570
571 total_procs++; /* count another process created */
572
573 if (nsp > max_sproc) {
574 if (nsp <= max_sproc * (max_sproc - 1)) {
575 tofork = nsp / max_sproc;
576 if (nsp % max_sproc)
577 tofork++;
578 } else {
579 tofork = max_sproc;
580 }
581 nnsp = nsp / tofork;
582 nrem = nsp % tofork;
583 for (i = 0; i < tofork; i++) {
584 range = (nrem > 0) ? nnsp + 1 : nnsp;
585 mcount(range, lo);
586 nrem--;
587 lo += range;
588 }
589 }
590 }
591
592
593 /*
594 * .func delete_infiles - delete the input files.
595 * .desc If the user asked us to (via 'D' flag) then unlink the input files.
596 * .call ret = delete_infiles().
597 * .arg none.
598 * .ret void.
599 */
600 static void
601 delete_infiles(void)
602 {
603 int i;
604 audit_pcb_t *pcb;
605 audit_fcb_t *fcb;
606
607 for (i = 0; i < pcbsize; i++) {
608 pcb = &audit_pcbs[i];
609 fcb = pcb->pcb_dfirst;
610 while (fcb != NULL) {
611 /*
612 * Only delete a file if it was succesfully processed.
613 * If there were any read errors or bad records
614 * then don't delete it.
615 * There may still be unprocessed records in it.
616 */
617 if (fcb->fcb_flags & FF_DELETE) {
618 if (unlink(fcb->fcb_file)) {
619 if (f_verbose) {
620 (void) sprintf(errbuf, gettext(
621 "%s delete on %s failed"),
622 ar, fcb->fcb_file);
623 }
624 perror(errbuf);
625 }
626 }
627 fcb = fcb->fcb_next;
628 }
629 }
630 }
631
632
633 /*
634 * .func rm_outfile - remove the outfile.
635 * .desc Remove the file we are writing the records to. We do this if
636 * processing failed and we are quitting before finishing.
637 * Update - don't actually remove the outfile, but generate
638 * a warning about its possible heathen nature.
639 * .call ret = rm_outfile().
640 * .arg none.
641 * .ret void.
642 */
643 static void
644 rm_outfile(void)
645 {
646 #if 0
647 if (f_outfile) {
648 if (unlink(f_outtemp) == -1) {
649 (void) sprintf(errbuf,
650 gettext("%s delete on %s failed"),
651 ar, f_outtemp);
652 perror(errbuf);
653 }
654 }
655 #else
656 (void) fprintf(stderr,
657 gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
658 ar,
659 (f_outfile == NULL) ? gettext("standard output") : f_outfile);
660 #endif
661 }
662
663
664 /*
665 * .func close_outfile - close the outfile.
666 * .desc Close the file we are writing records to.
667 * .call ret = close_outfile().
668 * .arg none.
669 * .ret 0 - close was succesful.
670 * .ret -1 - close failed.
671 */
672 static int
673 close_outfile(void)
674 {
675 if (fclose(stdout) == EOF) {
676 (void) sprintf(errbuf, gettext("%s close on %s failed"),
677 ar, f_outfile ? f_outfile : "standard output");
678 perror(errbuf);
679 return (-1);
680 }
681 (void) fsync(fileno(stdout));
682 return (rename_outfile());
683 }
684
685
686 /*
687 * .func write_header - write audit file header.
688 * .desc Write an audit file header to the output stream. The time in the
689 * header is the time of the first record written to the stream. This
690 * routine is called by the process handling the root node of the
691 * process tree just before it writes the first record to the output
692 * stream.
693 * .ret 0 - succesful write.
694 * .ret -1 - failed write - message printed.
695 */
696 int
697 write_header(void)
698 {
699 return (write_file_token(f_start));
700 }
701
702
703 static int
704 write_file_token(time_t when)
705 {
706 adr_t adr; /* adr ptr */
707 struct timeval tv; /* time now */
708 char for_adr[16]; /* plenty of room */
709 #ifdef _LP64
710 char token_id = AUT_OTHER_FILE64;
711 #else
712 char token_id = AUT_OTHER_FILE32;
713 #endif
714 short i = 1;
715 char c = '\0';
716
717 tv.tv_sec = when;
718 tv.tv_usec = 0;
719 adr_start(&adr, for_adr);
720 adr_char(&adr, &token_id, 1);
721 #ifdef _LP64
722 adr_int64(&adr, (int64_t *)&tv, 2);
723 #else
724 adr_int32(&adr, (int32_t *)&tv, 2);
725 #endif
726 adr_short(&adr, &i, 1);
727 adr_char(&adr, &c, 1);
728
729 if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) !=
730 adr_count(&adr)) {
731 if (when == f_start) {
732 (void) sprintf(errbuf,
733 gettext("%s error writing header to %s. "),
734 ar,
735 f_outfile ? f_outfile :
736 gettext("standard output"));
737 } else {
738 (void) sprintf(errbuf,
739 gettext("%s error writing trailer to %s. "),
740 ar,
741 f_outfile ? f_outfile :
742 gettext("standard output"));
743 }
744 perror(errbuf);
745 return (-1);
746 }
747 return (0);
748 }
749
750
751 /*
752 * .func write_trailer - write audit file trailer.
753 * .desc Write an audit file trailer to the output stream. The finish
754 * time for the trailer is the time of the last record written
755 * to the stream.
756 * .ret 0 - succesful write.
757 * .ret -1 - failed write - message printed.
758 */
759 static int
760 write_trailer(void)
761 {
762 return (write_file_token(f_end));
763 }
764
765
766 /*
767 * .func rename_outfile - rename the outfile.
768 * .desc If the user used the -O flag they only gave us the suffix name
769 * for the outfile. We have to add the time stamps to put the filename
770 * in the proper audit file name format. The start time will be the time
771 * of the first record in the file and the end time will be the time of
772 * the last record in the file.
773 * .ret 0 - rename succesful.
774 * .ret -1 - rename failed - message printed.
775 */
776 static int
777 rename_outfile(void)
778 {
779 char f_newfile[MAXFILELEN];
780 char buf1[15], buf2[15];
781 char *f_file, *f_nfile, *f_time, *f_name;
782
783 if (f_outfile != NULL) {
784 /*
785 * Get string representations of start and end times.
786 */
787 derive_str(f_start, buf1);
788 derive_str(f_end, buf2);
789
790 f_nfile = f_time = f_newfile; /* working copy */
791 f_file = f_name = f_outfile; /* their version */
792 while (*f_file) {
793 if (*f_file == '/') { /* look for filename */
794 f_time = f_nfile + 1;
795 f_name = f_file + 1;
796 }
797 *f_nfile++ = *f_file++; /* make copy of their version */
798 }
799 *f_time = '\0';
800 /* start time goes first */
801 (void) strcat(f_newfile, buf1);
802 (void) strcat(f_newfile, ".");
803 /* then the finish time */
804 (void) strcat(f_newfile, buf2);
805 (void) strcat(f_newfile, ".");
806 /* and the name they gave us */
807 (void) strcat(f_newfile, f_name);
808
809 #if AUDIT_FILE
810 (void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n",
811 f_outfile, f_newfile);
812 #endif
813
814 #if AUDIT_RENAME
815 if (rename(f_outtemp, f_newfile) == -1) {
816 (void) fprintf(stderr,
817 "%s rename of %s to %s failed.\n",
818 ar, f_outtemp, f_newfile);
819 return (-1);
820 }
821 f_outfile = f_newfile;
822 #else
823 if (rename(f_outtemp, f_outfile) == -1) {
824 (void) fprintf(stderr,
825 gettext("%s rename of %s to %s failed.\n"),
826 ar, f_outtemp, f_outfile);
827 return (-1);
828 }
829 #endif
830 }
831 return (0);
832 }
833
834
835 /*
836 * .func open_outfile - open the outfile.
837 * .desc Open the outfile specified by the -O option. Assign it to the
838 * the standard output. Get a unique temporary name to use so we
839 * don't clobber an existing file.
840 * .ret 0 - no errors detected.
841 * .ret -1 - errors in processing (message already printed).
842 */
843 static int
844 open_outfile(void)
845 {
846 int tmpfd = -1;
847
848 if (f_outfile != NULL) {
849 f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8);
850 (void) strcpy(f_outtemp, f_outfile);
851 (void) strcat(f_outtemp, "XXXXXX");
852 if ((tmpfd = mkstemp(f_outtemp)) == -1) {
853 (void) sprintf(errbuf,
854 gettext("%s couldn't create temporary file"), ar);
855 perror(errbuf);
856 return (-1);
857 }
858 (void) fflush(stdout);
859 if (tmpfd != fileno(stdout)) {
860 if ((dup2(tmpfd, fileno(stdout))) == -1) {
861 (void) sprintf(errbuf,
862 gettext("%s can't assign %s to the "
863 "standard output"), ar, f_outfile);
864 perror(errbuf);
865 return (-1);
866 }
867 (void) close(tmpfd);
868 }
869 }
870 return (0);
871 }
872
873
874 /*
875 * .func init_options - initialize the options.
876 * .desc Give initial and/or default values to some options.
877 * .call init_options();
878 * .arg none.
879 * .ret void.
880 */
881 static void
882 init_options(void)
883 {
884 struct timeval tp;
885 struct timezone tpz;
886
887 /*
888 * Get current time for general use.
889 */
890 if (gettimeofday(&tp, &tpz) == -1)
891 perror(gettext("auditreduce: initial getttimeofday failed"));
892
893 time_now = tp.tv_sec; /* save for general use */
894 f_start = 0; /* first record time default */
895 f_end = time_now; /* last record time default */
896 m_after = 0; /* Jan 1, 1970 00:00:00 */
897
898 /*
899 * Setup initial size of audit_pcbs[].
900 */
901 pcbsize = PCB_INITSIZE; /* initial size of file-holding pcb's */
902
903 audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t));
904
905 /* description of 'current' error */
906 error_str = gettext("initial error");
907
908 }
909
910
911 /*
912 * .func a_calloc - audit calloc.
913 * .desc Calloc with check for failure. This is called by all of the
914 * places that want memory.
915 * .call ptr = a_calloc(nelem, size).
916 * .arg nelem - number of elements to allocate.
917 * .arg size - size of each element.
918 * .ret ptr - ptr to allocated and zeroed memory.
919 * .ret never - if calloc fails then we never return.
920 */
921 void *
922 a_calloc(int nelem, size_t size)
923 {
924 void *ptr;
925
926 if ((ptr = calloc((unsigned)nelem, size)) == NULL) {
927 perror(gettext("auditreduce: memory allocation failed"));
928 exit(1);
929 }
930 return (ptr);
931 }
932
933
934 /*
935 * .func init_sig - initial signal catching.
936 *
937 * .desc
938 * Setup the signal catcher to catch the SIGCHLD signal plus
939 * "environmental" signals -- keyboard plus other externally
940 * generated signals such as out of file space or cpu time. If a
941 * child exits with either a non-zero exit code or was killed by
942 * a signal to it then we will also exit with a non-zero exit
943 * code. In this way abnormal conditions can be passed up to the
944 * root process and the entire run be halted. Also catch the int
945 * and quit signals. Remove the output file since it is in an
946 * inconsistent state.
947 * .call ret = init_sig().
948 * .arg none.
949 * .ret 0 - no errors detected.
950 * .ret -1 - signal failed (message printed).
951 */
952 static int
953 init_sig(void)
954 {
955 if (signal(SIGCHLD, chld_handler) == SIG_ERR) {
956 perror(gettext("auditreduce: SIGCHLD signal failed"));
957 return (-1);
958 }
959
960 if (signal(SIGHUP, int_handler) == SIG_ERR) {
961 perror(gettext("auditreduce: SIGHUP signal failed"));
962 return (-1);
963 }
964 if (signal(SIGINT, int_handler) == SIG_ERR) {
965 perror(gettext("auditreduce: SIGINT signal failed"));
966 return (-1);
967 }
968 if (signal(SIGQUIT, int_handler) == SIG_ERR) {
969 perror(gettext("auditreduce: SIGQUIT signal failed"));
970 return (-1);
971 }
972 if (signal(SIGABRT, int_handler) == SIG_ERR) {
973 perror(gettext("auditreduce: SIGABRT signal failed"));
974 return (-1);
975 }
976 if (signal(SIGTERM, int_handler) == SIG_ERR) {
977 perror(gettext("auditreduce: SIGTERM signal failed"));
978 return (-1);
979 }
980 if (signal(SIGPWR, int_handler) == SIG_ERR) {
981 perror(gettext("auditreduce: SIGPWR signal failed"));
982 return (-1);
983 }
984 if (signal(SIGXCPU, int_handler) == SIG_ERR) {
985 perror(gettext("auditreduce: SIGXCPU signal failed"));
986 return (-1);
987 }
988 if (signal(SIGXFSZ, int_handler) == SIG_ERR) {
989 perror(gettext("auditreduce: SIGXFSZ signal failed"));
990 return (-1);
991 }
992 if (signal(SIGSEGV, int_handler) == SIG_ERR) {
993 perror(gettext("auditreduce: SIGSEGV signal failed"));
994 return (-1);
995 }
996
997 return (0);
998 }
999
1000
1001 /*
1002 * .func chld_handler - handle child signals.
1003 * .desc Catch the SIGCHLD signals. Remove the root process
1004 * output file because it is in an inconsistent state.
1005 * Print a message giving the signal number and/or return code
1006 * of the child who caused the signal.
1007 * .ret void.
1008 */
1009 /* ARGSUSED */
1010 void
1011 chld_handler(int sig)
1012 {
1013 int pid;
1014 int status;
1015
1016 /*
1017 * Get pid and reasons for cause of event.
1018 */
1019 pid = wait(&status);
1020
1021 if (pid > 0) {
1022 /*
1023 * If child received a signal or exited with a non-zero
1024 * exit status then print message and exit
1025 */
1026 if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) ||
1027 (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) {
1028 (void) fprintf(stderr,
1029 gettext("%s abnormal child termination - "), ar);
1030
1031 if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) {
1032 psignal(WLOBYTE(status), "signal");
1033 if (WCOREDUMP(status))
1034 (void) fprintf(stderr,
1035 gettext("core dumped\n"));
1036 }
1037
1038 if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)
1039 (void) fprintf(stderr, gettext(
1040 "return code %d\n"),
1041 WHIBYTE(status));
1042
1043 /*
1044 * Get rid of outfile - it is suspect.
1045 */
1046 if (f_outfile != NULL) {
1047 (void) close_outfile();
1048 rm_outfile();
1049 }
1050 /*
1051 * Give statistical info that may be useful.
1052 */
1053 audit_stats();
1054
1055 exit(1);
1056 }
1057 }
1058 }
1059
1060
1061 /*
1062 * .func int_handler - handle quit/int signals.
1063 * .desc Catch the keyboard and other environmental signals.
1064 * Remove the root process output file because it is in
1065 * an inconsistent state.
1066 * .ret void.
1067 */
1068 /* ARGSUSED */
1069 void
1070 int_handler(int sig)
1071 {
1072 if (getpid() == root_pid) {
1073 (void) close_outfile();
1074 rm_outfile();
1075 exit(1);
1076 }
1077 /*
1078 * For a child process don't give an error exit or the
1079 * parent process will catch it with the chld_handler and
1080 * try to erase the outfile again.
1081 */
1082 exit(0);
1083 }