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