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 */
30
31 /*
32 * The Secure SunOS audit reduction tool - auditreduce.
33 * Document SM0071 is the primary source of information on auditreduce.
34 *
35 * Composed of 4 source modules:
36 * main.c - main driver.
37 * option.c - command line option processing.
38 * process.c - record/file/process functions.
39 * time.c - date/time handling.
40 *
41 * Main(), write_header(), audit_stats(), and a_calloc()
42 * are the only functions visible outside this module.
43 */
44
45 #include <siginfo.h>
46 #include <locale.h>
47 #include <libintl.h>
48 #include "auditr.h"
49 #include "auditrd.h"
50
51 #if !defined(TEXT_DOMAIN)
52 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
53 #endif
54
55 extern void derive_str(time_t, char *);
56 extern int process_options(int, char **);
57 extern int mproc(audit_pcb_t *);
58 extern void init_tokens(void); /* shared with praudit */
59
60 static int a_pow(int, int);
61 static void calc_procs(void);
62 static void chld_handler(int);
63 static int close_outfile(void);
64 static void c_close(audit_pcb_t *, int);
65 static void delete_infiles(void);
66 static void gather_pcb(audit_pcb_t *, int, int);
67 static void init_options(void);
68 static int init_sig(void);
69 static void int_handler(int);
70 static int mfork(audit_pcb_t *, int, int, int);
71 static void mcount(int, int);
72 static int open_outfile(void);
73 static void p_close(audit_pcb_t *);
74 static int rename_outfile(void);
75 static void rm_mem(audit_pcb_t *);
76 static void rm_outfile(void);
77 static void trim_mem(audit_pcb_t *);
78 static int write_file_token(time_t);
79 static int write_trailer(void);
80
81 /*
82 * File globals.
83 */
84 static int max_sproc; /* maximum number of subprocesses per process */
85 static int total_procs; /* number of processes in the process tree */
86 static int total_layers; /* number of layers in the process tree */
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
main(int argc,char ** argv)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
mfork(audit_pcb_t * pcb,int nsp,int lo,int hi)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 "
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 (void) fprintf(stderr, gettext(
465 "%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
466 ar, filenum, pcbnum, total_procs, total_layers);
467 }
468
469
470 /*
471 * .func gather_pcb - gather pcbs.
472 * .desc Gather together the range of the sub-processes that we are
473 * responsible for. For a pcb that controls processes this is all
474 * of the sub-processes that it forks. For a pcb that controls
475 * files this is the the range of pcbs from audit_pcbs[].
476 * .call gather_pcb(pcb, lo, hi).
477 * .arg pcb - ptr to pcb.
478 * .arg lo - lo index into pcb_below.
479 * .arg hi - hi index into pcb_below.
480 * .ret void.
481 */
482 static void
gather_pcb(audit_pcb_t * pcb,int lo,int hi)483 gather_pcb(audit_pcb_t *pcb, int lo, int hi)
484 {
485 pcb->pcb_lo = lo;
486 pcb->pcb_hi = hi;
487 pcb->pcb_count = hi - lo + 1;
488 }
489
490
491 /*
492 * .func calc_procs - calculate process parameters.
493 * .desc Calculate the current run's paramters regarding how many
494 * processes will have to be forked (maybe none).
495 * 5 is subtracted from maxfiles_proc to allow for stdin, stdout,
496 * stderr, and the pipe to a parent process. The outfile
497 * in the root process is assigned to stdout. The unused half of each
498 * pipe is closed, to allow for more connections, but we still
499 * have to have the 5th spot because in order to get the pipe
500 * we need 2 descriptors up front.
501 * .call calc_procs().
502 * .arg none.
503 * .ret void.
504 */
505 static void
calc_procs(void)506 calc_procs(void)
507 {
508 int val;
509 int maxfiles_proc;
510 struct rlimit rl;
511
512 if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
513 perror("auditreduce: getrlimit");
514 exit(1);
515 }
516
517 maxfiles_proc = rl.rlim_cur;
518
519 max_sproc = maxfiles_proc - 5; /* max subprocesses per process */
520
521 /*
522 * Calculate how many layers the process tree has.
523 */
524 total_layers = 1;
525 for (/* */; /* */; /* */) {
526 val = a_pow(max_sproc, total_layers);
527 if (val > pcbnum)
528 break;
529 total_layers++;
530 }
531 /*
532 * Count how many processes are in the process tree.
533 */
534 mcount(pcbnum, 0);
535
536 #if AUDIT_PROC_TRACE
537 (void) fprintf(stderr,
538 "pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
539 pcbnum, filenum, maxfiles_proc, max_sproc,
540 total_layers, total_procs);
541 #endif
542 }
543
544
545 static int
a_pow(int base,int exp)546 a_pow(int base, int exp)
547 {
548 int i;
549 int answer;
550
551 if (exp == 0) {
552 answer = 1;
553 } else {
554 answer = base;
555 for (i = 0; i < (exp - 1); i++)
556 answer *= base;
557 }
558 return (answer);
559 }
560
561
562 /*
563 * .func mcount - main count.
564 * .desc Go through the motions of building the process tree just
565 * to count how many processes there are. Don't really
566 * build anything. Answer is in global var total_procs.
567 * .call mcount(nsp, lo).
568 * .arg nsp - number of subs for this tree branch.
569 * .arg lo - lo side of range of subs.
570 * .ret void.
571 */
572 static void
mcount(int nsp,int lo)573 mcount(int nsp, int lo)
574 {
575 int range, i, tofork, nnsp, nrem;
576
577 total_procs++; /* count another process created */
578
579 if (nsp > max_sproc) {
580 if (nsp <= max_sproc * (max_sproc - 1)) {
581 tofork = nsp / max_sproc;
582 if (nsp % max_sproc)
583 tofork++;
584 } else {
585 tofork = max_sproc;
586 }
587 nnsp = nsp / tofork;
588 nrem = nsp % tofork;
589 for (i = 0; i < tofork; i++) {
590 range = (nrem > 0) ? nnsp + 1 : nnsp;
591 mcount(range, lo);
592 nrem--;
593 lo += range;
594 }
595 }
596 }
597
598
599 /*
600 * .func delete_infiles - delete the input files.
601 * .desc If the user asked us to (via 'D' flag) then unlink the input files.
602 * .call ret = delete_infiles().
603 * .arg none.
604 * .ret void.
605 */
606 static void
delete_infiles(void)607 delete_infiles(void)
608 {
609 int i;
610 audit_pcb_t *pcb;
611 audit_fcb_t *fcb;
612
613 for (i = 0; i < pcbsize; i++) {
614 pcb = &audit_pcbs[i];
615 fcb = pcb->pcb_dfirst;
616 while (fcb != NULL) {
617 /*
618 * Only delete a file if it was succesfully processed.
619 * If there were any read errors or bad records
620 * then don't delete it.
621 * There may still be unprocessed records in it.
622 */
623 if (fcb->fcb_flags & FF_DELETE) {
624 if (unlink(fcb->fcb_file)) {
625 if (f_verbose) {
626 (void) sprintf(errbuf, gettext(
627 "%s delete on %s failed"),
628 ar, fcb->fcb_file);
629 }
630 perror(errbuf);
631 }
632 }
633 fcb = fcb->fcb_next;
634 }
635 }
636 }
637
638
639 /*
640 * .func rm_outfile - remove the outfile.
641 * .desc Remove the file we are writing the records to. We do this if
642 * processing failed and we are quitting before finishing.
643 * Update - don't actually remove the outfile, but generate
644 * a warning about its possible heathen nature.
645 * .call ret = rm_outfile().
646 * .arg none.
647 * .ret void.
648 */
649 static void
rm_outfile(void)650 rm_outfile(void)
651 {
652 #if 0
653 if (f_outfile) {
654 if (unlink(f_outtemp) == -1) {
655 (void) sprintf(errbuf,
656 gettext("%s delete on %s failed"),
657 ar, f_outtemp);
658 perror(errbuf);
659 }
660 }
661 #else
662 (void) fprintf(stderr,
663 gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
664 ar,
665 (f_outfile == NULL) ? gettext("standard output") : f_outfile);
666 #endif
667 }
668
669
670 /*
671 * .func close_outfile - close the outfile.
672 * .desc Close the file we are writing records to.
673 * .call ret = close_outfile().
674 * .arg none.
675 * .ret 0 - close was succesful.
676 * .ret -1 - close failed.
677 */
678 static int
close_outfile(void)679 close_outfile(void)
680 {
681 if (fclose(stdout) == EOF) {
682 (void) sprintf(errbuf, gettext("%s close on %s failed"),
683 ar, f_outfile ? f_outfile : "standard output");
684 perror(errbuf);
685 return (-1);
686 }
687 (void) fsync(fileno(stdout));
688 return (rename_outfile());
689 }
690
691
692 /*
693 * .func write_header - write audit file header.
694 * .desc Write an audit file header to the output stream. The time in the
695 * header is the time of the first record written to the stream. This
696 * routine is called by the process handling the root node of the
697 * process tree just before it writes the first record to the output
698 * stream.
699 * .ret 0 - succesful write.
700 * .ret -1 - failed write - message printed.
701 */
702 int
write_header(void)703 write_header(void)
704 {
705 return (write_file_token(f_start));
706 }
707
708
709 static int
write_file_token(time_t when)710 write_file_token(time_t when)
711 {
712 adr_t adr; /* adr ptr */
713 struct timeval tv; /* time now */
714 char for_adr[16]; /* plenty of room */
715 #ifdef _LP64
716 char token_id = AUT_OTHER_FILE64;
717 #else
718 char token_id = AUT_OTHER_FILE32;
719 #endif
720 short i = 1;
721 char c = '\0';
722
723 tv.tv_sec = when;
724 tv.tv_usec = 0;
725 adr_start(&adr, for_adr);
726 adr_char(&adr, &token_id, 1);
727 #ifdef _LP64
728 adr_int64(&adr, (int64_t *)&tv, 2);
729 #else
730 adr_int32(&adr, (int32_t *)&tv, 2);
731 #endif
732 adr_short(&adr, &i, 1);
733 adr_char(&adr, &c, 1);
734
735 if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) !=
736 adr_count(&adr)) {
737 if (when == f_start) {
738 (void) sprintf(errbuf,
739 gettext("%s error writing header to %s. "),
740 ar,
741 f_outfile ? f_outfile :
742 gettext("standard output"));
743 } else {
744 (void) sprintf(errbuf,
745 gettext("%s error writing trailer to %s. "),
746 ar,
747 f_outfile ? f_outfile :
748 gettext("standard output"));
749 }
750 perror(errbuf);
751 return (-1);
752 }
753 return (0);
754 }
755
756
757 /*
758 * .func write_trailer - write audit file trailer.
759 * .desc Write an audit file trailer to the output stream. The finish
760 * time for the trailer is the time of the last record written
761 * to the stream.
762 * .ret 0 - succesful write.
763 * .ret -1 - failed write - message printed.
764 */
765 static int
write_trailer(void)766 write_trailer(void)
767 {
768 return (write_file_token(f_end));
769 }
770
771
772 /*
773 * .func rename_outfile - rename the outfile.
774 * .desc If the user used the -O flag they only gave us the suffix name
775 * for the outfile. We have to add the time stamps to put the filename
776 * in the proper audit file name format. The start time will be the time
777 * of the first record in the file and the end time will be the time of
778 * the last record in the file.
779 * .ret 0 - rename succesful.
780 * .ret -1 - rename failed - message printed.
781 */
782 static int
rename_outfile(void)783 rename_outfile(void)
784 {
785 char f_newfile[MAXFILELEN];
786 char buf1[15], buf2[15];
787 char *f_file, *f_nfile, *f_time, *f_name;
788
789 if (f_outfile != NULL) {
790 /*
791 * Get string representations of start and end times.
792 */
793 derive_str(f_start, buf1);
794 derive_str(f_end, buf2);
795
796 f_nfile = f_time = f_newfile; /* working copy */
797 f_file = f_name = f_outfile; /* their version */
798 while (*f_file) {
799 if (*f_file == '/') { /* look for filename */
800 f_time = f_nfile + 1;
801 f_name = f_file + 1;
802 }
803 *f_nfile++ = *f_file++; /* make copy of their version */
804 }
805 *f_time = '\0';
806 /* start time goes first */
807 (void) strcat(f_newfile, buf1);
808 (void) strcat(f_newfile, ".");
809 /* then the finish time */
810 (void) strcat(f_newfile, buf2);
811 (void) strcat(f_newfile, ".");
812 /* and the name they gave us */
813 (void) strcat(f_newfile, f_name);
814
815 #if AUDIT_FILE
816 (void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n",
817 f_outfile, f_newfile);
818 #endif
819
820 #if AUDIT_RENAME
821 if (rename(f_outtemp, f_newfile) == -1) {
822 (void) fprintf(stderr,
823 "%s rename of %s to %s failed.\n",
824 ar, f_outtemp, f_newfile);
825 return (-1);
826 }
827 f_outfile = f_newfile;
828 #else
829 if (rename(f_outtemp, f_outfile) == -1) {
830 (void) fprintf(stderr,
831 gettext("%s rename of %s to %s failed.\n"),
832 ar, f_outtemp, f_outfile);
833 return (-1);
834 }
835 #endif
836 }
837 return (0);
838 }
839
840
841 /*
842 * .func open_outfile - open the outfile.
843 * .desc Open the outfile specified by the -O option. Assign it to the
844 * the standard output. Get a unique temporary name to use so we
845 * don't clobber an existing file.
846 * .ret 0 - no errors detected.
847 * .ret -1 - errors in processing (message already printed).
848 */
849 static int
open_outfile(void)850 open_outfile(void)
851 {
852 int tmpfd = -1;
853
854 if (f_outfile != NULL) {
855 f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8);
856 (void) strcpy(f_outtemp, f_outfile);
857 (void) strcat(f_outtemp, "XXXXXX");
858 if ((tmpfd = mkstemp(f_outtemp)) == -1) {
859 (void) sprintf(errbuf,
860 gettext("%s couldn't create temporary file"), ar);
861 perror(errbuf);
862 return (-1);
863 }
864 (void) fflush(stdout);
865 if (tmpfd != fileno(stdout)) {
866 if ((dup2(tmpfd, fileno(stdout))) == -1) {
867 (void) sprintf(errbuf,
868 gettext("%s can't assign %s to the "
869 "standard output"), ar, f_outfile);
870 perror(errbuf);
871 return (-1);
872 }
873 (void) close(tmpfd);
874 }
875 }
876 return (0);
877 }
878
879
880 /*
881 * .func init_options - initialize the options.
882 * .desc Give initial and/or default values to some options.
883 * .call init_options();
884 * .arg none.
885 * .ret void.
886 */
887 static void
init_options(void)888 init_options(void)
889 {
890 struct timeval tp;
891 struct timezone tpz;
892
893 /*
894 * Get current time for general use.
895 */
896 if (gettimeofday(&tp, &tpz) == -1)
897 perror(gettext("auditreduce: initial getttimeofday failed"));
898
899 time_now = tp.tv_sec; /* save for general use */
900 f_start = 0; /* first record time default */
901 f_end = time_now; /* last record time default */
902 m_after = 0; /* Jan 1, 1970 00:00:00 */
903
904 /*
905 * Setup initial size of audit_pcbs[].
906 */
907 pcbsize = PCB_INITSIZE; /* initial size of file-holding pcb's */
908
909 audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t));
910
911 /* description of 'current' error */
912 error_str = gettext("initial error");
913
914 }
915
916
917 /*
918 * .func a_calloc - audit calloc.
919 * .desc Calloc with check for failure. This is called by all of the
920 * places that want memory.
921 * .call ptr = a_calloc(nelem, size).
922 * .arg nelem - number of elements to allocate.
923 * .arg size - size of each element.
924 * .ret ptr - ptr to allocated and zeroed memory.
925 * .ret never - if calloc fails then we never return.
926 */
927 void *
a_calloc(int nelem,size_t size)928 a_calloc(int nelem, size_t size)
929 {
930 void *ptr;
931
932 if ((ptr = calloc((unsigned)nelem, size)) == NULL) {
933 perror(gettext("auditreduce: memory allocation failed"));
934 exit(1);
935 }
936 return (ptr);
937 }
938
939
940 /*
941 * .func init_sig - initial signal catching.
942 *
943 * .desc
944 * Setup the signal catcher to catch the SIGCHLD signal plus
945 * "environmental" signals -- keyboard plus other externally
946 * generated signals such as out of file space or cpu time. If a
947 * child exits with either a non-zero exit code or was killed by
948 * a signal to it then we will also exit with a non-zero exit
949 * code. In this way abnormal conditions can be passed up to the
950 * root process and the entire run be halted. Also catch the int
951 * and quit signals. Remove the output file since it is in an
952 * inconsistent state.
953 * .call ret = init_sig().
954 * .arg none.
955 * .ret 0 - no errors detected.
956 * .ret -1 - signal failed (message printed).
957 */
958 static int
init_sig(void)959 init_sig(void)
960 {
961 if (signal(SIGCHLD, chld_handler) == SIG_ERR) {
962 perror(gettext("auditreduce: SIGCHLD signal failed"));
963 return (-1);
964 }
965
966 if (signal(SIGHUP, int_handler) == SIG_ERR) {
967 perror(gettext("auditreduce: SIGHUP signal failed"));
968 return (-1);
969 }
970 if (signal(SIGINT, int_handler) == SIG_ERR) {
971 perror(gettext("auditreduce: SIGINT signal failed"));
972 return (-1);
973 }
974 if (signal(SIGQUIT, int_handler) == SIG_ERR) {
975 perror(gettext("auditreduce: SIGQUIT signal failed"));
976 return (-1);
977 }
978 if (signal(SIGABRT, int_handler) == SIG_ERR) {
979 perror(gettext("auditreduce: SIGABRT signal failed"));
980 return (-1);
981 }
982 if (signal(SIGTERM, int_handler) == SIG_ERR) {
983 perror(gettext("auditreduce: SIGTERM signal failed"));
984 return (-1);
985 }
986 if (signal(SIGPWR, int_handler) == SIG_ERR) {
987 perror(gettext("auditreduce: SIGPWR signal failed"));
988 return (-1);
989 }
990 if (signal(SIGXCPU, int_handler) == SIG_ERR) {
991 perror(gettext("auditreduce: SIGXCPU signal failed"));
992 return (-1);
993 }
994 if (signal(SIGXFSZ, int_handler) == SIG_ERR) {
995 perror(gettext("auditreduce: SIGXFSZ signal failed"));
996 return (-1);
997 }
998 if (signal(SIGSEGV, int_handler) == SIG_ERR) {
999 perror(gettext("auditreduce: SIGSEGV signal failed"));
1000 return (-1);
1001 }
1002
1003 return (0);
1004 }
1005
1006
1007 /*
1008 * .func chld_handler - handle child signals.
1009 * .desc Catch the SIGCHLD signals. Remove the root process
1010 * output file because it is in an inconsistent state.
1011 * Print a message giving the signal number and/or return code
1012 * of the child who caused the signal.
1013 * .ret void.
1014 */
1015 /* ARGSUSED */
1016 void
chld_handler(int sig)1017 chld_handler(int sig)
1018 {
1019 int pid;
1020 int status;
1021
1022 /*
1023 * Get pid and reasons for cause of event.
1024 */
1025 pid = wait(&status);
1026
1027 if (pid > 0) {
1028 /*
1029 * If child received a signal or exited with a non-zero
1030 * exit status then print message and exit
1031 */
1032 if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) ||
1033 (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) {
1034 (void) fprintf(stderr,
1035 gettext("%s abnormal child termination - "), ar);
1036
1037 if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) {
1038 psignal(WLOBYTE(status), "signal");
1039 if (WCOREDUMP(status))
1040 (void) fprintf(stderr,
1041 gettext("core dumped\n"));
1042 }
1043
1044 if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)
1045 (void) fprintf(stderr, gettext(
1046 "return code %d\n"),
1047 WHIBYTE(status));
1048
1049 /*
1050 * Get rid of outfile - it is suspect.
1051 */
1052 if (f_outfile != NULL) {
1053 (void) close_outfile();
1054 rm_outfile();
1055 }
1056 /*
1057 * Give statistical info that may be useful.
1058 */
1059 audit_stats();
1060
1061 exit(1);
1062 }
1063 }
1064 }
1065
1066
1067 /*
1068 * .func int_handler - handle quit/int signals.
1069 * .desc Catch the keyboard and other environmental signals.
1070 * Remove the root process output file because it is in
1071 * an inconsistent state.
1072 * .ret void.
1073 */
1074 /* ARGSUSED */
1075 void
int_handler(int sig)1076 int_handler(int sig)
1077 {
1078 if (getpid() == root_pid) {
1079 (void) close_outfile();
1080 rm_outfile();
1081 exit(1);
1082 }
1083 /*
1084 * For a child process don't give an error exit or the
1085 * parent process will catch it with the chld_handler and
1086 * try to erase the outfile again.
1087 */
1088 exit(0);
1089 }
1090