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