8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / auditrecord / auditrecord.pl
blob8c39e634aa1dcad6845203c51aa1f82764083ee3
1 #!/usr/perl5/bin/perl
3 # CDDL HEADER START
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
20 # CDDL HEADER END
23 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 # Use is subject to license terms.
27 # auditrecord - display one or more audit records
29 require 5.8.4;
30 use strict;
31 use warnings;
33 our (%opt, $parse, $callFilter, $debug,
34 %attr, %event, %class, %skipClass, %token, %noteAlias,
35 $title, $note, $name, $col1, $col2, $col3, $skip);
37 use Getopt::Std;
38 use locale;
39 use POSIX qw(locale_h);
40 use Sun::Solaris::Utils qw(gettext textdomain);
41 use Sun::Solaris::BSM::_BSMparse;
43 setlocale(LC_ALL, "");
44 textdomain(TEXT_DOMAIN);
46 if (!getopts('adhe:c:i:p:s:', \%opt) || @ARGV) {
47 my $errString =
48 gettext("$0 takes no arguments other than switches.\n");
49 print STDERR $errString if (@ARGV);
50 usage();
51 exit (1);
54 unless ($opt{a} || $opt{c} || $opt{e} || $opt{h} || $opt{i} ||
55 $opt{p} || $opt{s}) {
56 usage();
57 exit (1);
60 my %options;
61 $options{'classFilter'} = $opt{c}; # filter on this class
62 $debug = $opt{d}; # debug mode on
63 $options{'eventFilter'} = $opt{e}; # filter on this event
64 my $html = $opt{h}; # output in html format
65 $options{'idFilter'} = $opt{i}; # filter on this id
66 $callFilter = $opt{p}; # filter on this program name
67 $callFilter = $opt{s} if ($opt{s}); # filter on this system call
69 if (defined($callFilter)) {
70 $callFilter = qr/\b$callFilter\b/;
71 } else {
72 $callFilter = qr//;
74 $parse = new Sun::Solaris::BSM::_BSMparse($debug, \%options);
76 my ($attr, $token, $skipClass, $noteAlias) = $parse->readAttr();
77 %attr = %$attr;
78 %token = %$token;
79 %noteAlias = %$noteAlias;
80 %skipClass = %$skipClass;
82 %class = %{$parse->readClass()};
83 %event = %{$parse->readEvent()};
85 # the calls to readControl and readUser are for debug; they are not
86 # needed for generation of record formats. 'ignore' means if there
87 # is no permission to read the file, don't die, just soldier on.
89 # $error is L10N'd by $parse
91 if ($debug) {
92 my ($cnt, $error);
94 # verify audit_control content
95 ($cnt, $error) = $parse->readControl('ignore');
96 print STDERR $error if ($cnt);
98 # verify audit_user content
99 ($cnt, $error) = $parse->readUser('ignore');
100 print STDERR $error if ($cnt);
102 # check audit_event, audit_display_attr
103 ($cnt, $error) = $parse->ckAttrEvent();
104 print STDERR $error if ($cnt);
107 # check for invalid class to -c option if supplied
108 if (defined $options{'classFilter'}) {
109 my $invalidClass = gettext('Invalid class %s supplied.');
110 my $isInvalidClass = 0;
111 foreach (split(/\s*,\s*/, $options{'classFilter'})) {
112 unless (exists $class{$_}) {
113 printf STDERR "$invalidClass\n", $_;
114 $isInvalidClass = 1;
117 exit (1) if $isInvalidClass;
120 if ($html) {
121 writeHTML();
122 } else {
123 writeASCII();
126 exit (0);
128 # writeASCII -- collect what's been read from various sources and
129 # output the formatted audit records
131 sub writeASCII {
132 my $label;
134 my $errString;
136 foreach $label (sort(keys(%event))) {
137 my $description;
138 my @case;
140 my ($id, $class, $eventDescription) = @{$event{$label}};
142 our ($title, $note, $name, $col1, $col2, $col3);
144 my ($skipThisClass, $mask) = classToMask($class, $label);
146 next if ($skipThisClass);
148 $mask = sprintf("0x%08X", $mask);
150 ($name, $description, $title, $skip, @case) =
151 getAttributes($label, $eventDescription);
153 next if ($name eq 'undefined');
155 next unless $description =~ $callFilter;
157 $~ = 'nameLine';
158 write;
160 $note = $skip;
161 $~ = 'wrapped1';
162 while ($note) {
163 write;
165 next if ($skip);
167 $~ = 'threeColumns';
168 ($col1, $col2, $col3) = getCallInfo($id, $name, $description);
169 my @col1 = split(/\s*;\s*/, $col1);
170 my @col2 = split(/\s*;\s*/, $col2);
171 my @col3 = split(/\s*;\s*/, $col3);
172 my $rows = $#col1;
173 $rows = $#col2 if ($#col2 > $rows);
174 $rows = $#col3 if ($#col3 > $rows);
175 for (my $i = 0; $i <= $rows; $i++) {
176 $col1 = defined ($col1[$i]) ? $col1[$i] : '';
177 $col2 = defined ($col2[$i]) ? $col2[$i] : '';
178 $col3 = defined ($col3[$i]) ? 'See ' . $col3[$i] : '';
179 write;
181 $col1 = 'event ID';
182 $col2 = $id;
183 $col3 = $label;
184 write;
186 $col1 = 'class';
187 $col2 = $class;
188 $col3 = "($mask)";
189 write;
191 my $haveFormat = 0;
192 my $caseElement;
194 foreach $caseElement (@case) {
195 # $note1 is the "case" description
196 # $note2 is a "note"
197 my ($note1, $format, $comment, $note2) = @$caseElement;
199 $note = $note1;
200 $~ = 'wrapped1';
201 while ($note) {
202 write;
204 unless (defined($format)) {
205 $errString = gettext(
206 "missing format field: %s");
207 printf STDERR ("$errString\n", $label);
208 next;
210 unless ($format eq 'none') {
211 $haveFormat = 1;
213 my $list = getFormatList($format, $id);
215 my @format = split(/\s*:\s*/, $list);
216 my @comment = split(/\s*:\s*/, $comment);
218 my $item;
220 foreach $item (@format) {
221 $~ = 'twoColumns';
222 ($col1, $col2) =
223 getFormatLine($item, $label,
224 @comment);
225 write;
226 $~ = "col2Wrapped";
227 while ($col2) {
228 write;
232 $note2 = $noteAlias{$note2} if ($noteAlias{$note2});
233 if ($note2) {
234 $note = $note2;
235 $~ = 'space';
236 write;
237 $~ = 'wrapped1';
238 while ($note) {
239 write;
243 unless ($haveFormat) {
244 $~ = 'wrapped1';
245 $note = gettext('No format information available');
246 write;
251 # writeHTML -- collect what's been read from various sources
252 # and output the formatted audit records
255 sub writeHTML {
256 my $label;
258 my $description;
259 my @case;
261 my $docTitle = gettext("Audit Record Formats");
263 print qq{
264 <!doctype html PUBLIC "-//IETF//DTD HTML//EN">
265 <html>
266 <head>
267 <title>$docTitle</title>
268 <META http-equiv="Content-Style-Type" content="text/css">
269 </head>
271 <body TEXT="#000000" BGCOLOR="#F0F0F0">
274 my $tableRows = 0; # work around Netscape large table bug
275 startTable(); # by generating multiple tables
277 foreach $label (sort(keys(%event))) {
278 my ($id, $class, $eventDescription) = @{$event{$label}};
280 our ($title, $name, $note, $col1, $col2, $col3);
282 my ($skipThisClass, $mask) = classToMask($class, $label);
284 next if ($skipThisClass);
286 $mask = sprintf("0x%08X", $mask);
288 my $description;
290 ($name, $description, $title, $skip, @case) =
291 getAttributes($label, $eventDescription);
293 next if ($name eq 'undefined');
295 next unless $description =~ $callFilter;
297 $tableRows++;
298 if ($tableRows > 50) {
299 endTable();
300 startTable();
301 $tableRows = 0;
304 my ($callType, $callName);
305 ($callType, $callName, $description) =
306 getCallInfo($id, $name, $description);
307 $description =~ s/\s*;\s*/<br>/g;
309 my $titleName = $title;
310 if ($callName) {
311 $titleName = $callName;
313 $titleName =~ s/\s*;\s*/<br>/g;
314 $titleName = '&nbsp;' if ($titleName eq $title);
316 print qq{
317 <tr bgcolor="#C0C0C0">
318 <td>$label</td>
319 <td>$id</td>
320 <td>$class</td>
321 <td>$mask</td>
322 </tr>
323 <tr>
324 <td colspan=2>$titleName</td>
325 <td colspan=2>$description</td>
326 </tr>
327 <tr>
328 <td colspan=4>
329 <pre>
332 $note = $skip;
333 $~ = 'wrapped2';
334 while ($note) {
335 write;
337 next if ($skip);
339 my $haveFormat = 0;
340 my $caseElement;
342 foreach $caseElement (@case) {
343 my ($note1, $format, $comment, $note2) = @$caseElement;
345 $note = $note1;
346 $~ = 'wrapped2';
347 while ($note) {
348 write;
350 unless (defined($format)) {
351 my $errString = gettext(
352 "Missing format field: %s\n");
353 printf STDERR ($errString, $label);
354 next;
356 unless ($format eq 'none') {
357 $haveFormat = 1;
359 my $list = getFormatList($format, $id);
361 my @format = split(/\s*:\s*/, $list);
362 my @comment = split(/\s*:\s*/, $comment);
363 my $item;
365 $~ = 'twoColumns';
366 foreach $item (@format) {
367 ($col1, $col2) =
368 getFormatLine($item, $label,
369 @comment);
370 write;
373 if ($note2) {
374 $note2 = $noteAlias{$note2} if ($noteAlias{$note2});
375 $note = $note2;
376 $~ = 'space';
377 write;
378 $~ = 'wrapped2';
379 while ($note) {
380 write;
384 unless ($haveFormat) {
385 $~ = 'wrapped2';
386 $note = 'No format information available';
387 write;
389 print q{
390 </pre>
391 </td/>
392 </tr>
395 endTable();
398 sub startTable {
400 print q{
401 <table border=1>
402 <tr bgcolor="#C0C0C0">
403 <th>Event Name</th>
404 <th>Event ID</th>
405 <th>Event Class</th>
406 <th>Mask</th>
407 </tr>
408 <tr>
409 <th colspan=2>Call Name</th>
410 <th colspan=2>Reference</th>
411 <tr>
412 <tr>
413 <th colspan=4>Format</th>
414 </tr>
418 sub endTable {
420 print q{
421 </table>
422 </body>
423 </html>
427 # classToMask: One, given a class list, it calculates the mask; Two,
428 # it checks to see if every item on the class list is marked for
429 # skipping, and if so, sets a flag.
431 sub classToMask {
432 my $classList = shift;
433 my $label = shift;
434 my $mask = 0;
436 my @classes = split(/\s*,\s*/, $classList);
437 my $skipThisClass = 0;
439 my $thisClass;
440 foreach $thisClass (@classes) {
441 unless (defined($class{$thisClass})) {
442 my $errString = gettext(
443 "%s not found in audit_class. Omitting %s\n");
444 $errString = sprintf($errString, $thisClass,
445 $label);
446 print STDERR $errString if ($debug);
447 next;
449 $skipThisClass = 1 if ($skipClass{$thisClass});
450 $mask |= $class{$thisClass};
452 return ($skipThisClass, $mask);
455 # getAttributes: Combine fields from %event and %attr; a description
456 # in the attribute file overrides a description from audit_event
458 sub getAttributes {
459 my $label = shift;
460 my $desc = shift; # description from audit_event
462 my ($description, $title, $skip, @case);
464 my $errString = gettext("%s not found in attribute file.");
465 my $name = gettext("undefined");
467 if (defined($attr{$label})) {
468 ($name, $description, $title, $skip, @case) = @{$attr{$label}};
469 if ($description eq 'none') {
470 if ($desc eq 'blank') {
471 $description = '';
472 } else {
473 $description = $desc;
476 $name = '' if ($name eq 'none');
477 $title = $name if (($title eq 'none') || (!defined($title)));
478 } else {
479 printf STDERR ("$errString\n", $label) if ($debug);
481 return ($name, $description, $title, $skip, @case);
484 # getCallInfo: the system call or program name for an audit record can
485 # usually be derived from the event name; %attr provides exceptions to
486 # this rule
488 sub getCallInfo {
489 my $id = shift;
490 my $name = shift;
491 my $desc = shift;
493 my $callType;
494 my $callName;
495 my $description;
497 if ($name) {
498 if ($id < 6000) {
499 $callType = 'system call';
500 } else {
501 $callType = 'program';
503 ($callName) = split(/\s*:\s*/, $name);
504 } else {
505 $callType = '';
506 $callName = '';
508 $description = '';
509 $description = "$desc" if ($desc);
511 return ($callType, $callName, $description);
514 # getFormatList: determine the order and details of kernel vs user
515 # audit records. If the first token is "head" then the token list
516 # is explicit, otherwise the header, subject and return are implied.
518 sub getFormatList {
519 my $format = shift;
520 my $id = shift;
522 my $list;
524 if ($format =~ /^head:/) {
525 $list = $format;
527 elsif ($format eq 'kernel') {
528 $list = $parse->{'kernelDefault'};
529 $list =~ s/insert://;
530 } elsif ($format eq 'user') {
531 $list = $parse->{'userDefault'};
532 $list =~ s/insert://;
533 } elsif ($id < 6000) {
534 $list = $parse->{'kernelDefault'};
535 $list =~ s/insert/$format/;
536 } else {
537 $list = $parse->{'userDefault'};
538 $list =~ s/insert/$format/;
540 return ($list);
543 # getFormatLine: the arguments from the attribute 'format' are
544 # expanded to their printable form and also paired with a comment if
545 # one exists
547 sub getFormatLine {
548 my $arg = shift;
549 my $label = shift;
550 my @comment = @_;
552 my $isOption = 0;
554 my ($token, $comment);
556 my $cmt = -1;
557 if ($arg =~ s/(\D*)(\d+)$/$1/) { # trailing digits select a comment
558 $cmt = $2 - 1;
560 $isOption = 1 if ($arg =~ s/^\[(.+)\]$/$1/);
562 if (defined($token{$arg})) { # expand abbreviated name to token
563 $token = $token{$arg};
564 } else {
565 $token = $arg; # no abbreviation found
567 $token = '['.$token.']' if ($isOption);
569 if ($cmt > -1) {
570 unless(defined($comment[$cmt])) {
571 my $errString = gettext(
572 "missing comment for %s %s token %d\n");
573 printf STDERR ($errString, $label, $token,
574 $cmt);
575 $comment = gettext('missing comment field');
576 } else {
577 $comment = $comment[$cmt];
578 $comment =~ s/&colon;/:/g; #':' is a delimiter
580 } else {
581 $comment = '';
583 unless (defined($token) && defined($comment)) {
584 my $errString = gettext("attribute format/comment error for %s\n");
585 printf STDERR ($errString, $label);
587 return ($token, $comment);
590 sub usage {
591 print "$0 [ -d ] [ -h ] {[ -a ] | [ -e event ] |\n";
592 print "\t[ -c class ] | [-i id ] | [ -p program ] |\n";
593 print "\t[ -s syscall ]}\n";
596 format nameLine =
598 @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
599 $title
602 format threeColumns =
603 @<<<<<<<<<< @<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
604 $col1, $col2, $col3
607 format twoColumns =
608 @<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
609 $col1, $col2
611 format col2Wrapped =
612 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
613 $col2
616 format space =
620 format wrapped1 =
621 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
622 $note
625 format wrapped2 =
626 ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
627 $note