Home | History | Annotate | Download | only in libbsm
      1 #!/usr/perl5/bin/perl -w
      2 #
      3 # CDDL HEADER START
      4 #
      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.
      8 #
      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.
     13 #
     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]
     19 #
     20 # CDDL HEADER END
     21 #
     22 #
     23 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24 # Use is subject to license terms.
     25 #
     26 
     27 # auditxml takes the audit record description (.xml file) and
     28 # generates the files needed for the C audit api. 
     29 
     30 my $prog = $0; $prog =~ s|.*/||g;
     31 my $usage = <<EOF;
     32 
     33 Usage: $prog [options] <xml-input-file>
     34 Options:
     35 	-d	Enable debug output
     36 	-e pfx	Internal event prefix (default: AUE)
     37 	-i pfx	Interface prefix (default: adt)
     38 		External event prefix is uppercase version of this string.
     39 	-o dir	Output directory (default: current dir)
     40 
     41 EOF
     42 
     43 use auditxml;
     44 use Getopt::Std;
     45 use strict;
     46 
     47 our $debug = 0; # normal use is to set via the file being parsed.
     48                # <debug set="on"/> or <debug set="off"/> or <debug/>
     49                # if the set attribute is omitted, debug state is toggled
     50                # Override with appDebug, but toggle won't do what you
     51                # want.
     52 my $appDebug = 0; # used after return from "new auditxml";
     53 
     54 # Process command-line options
     55 our ($opt_d, $opt_e, $opt_i, $opt_o);
     56 if (!getopts('de:i:o:') || $#ARGV != 0) {
     57     die $usage;
     58 }
     59 my $outdir = $opt_o || ".";
     60 my $pfx_adt = lc($opt_i) || "adt";
     61 my $pfx_ADT = uc($pfx_adt);
     62 my $pfx_AUE = uc($opt_e) || "AUE";
     63 
     64 $appDebug = $opt_d;
     65 
     66 my $uniLabel = "adr";
     67 my $xlateUniLabelInc = 0;
     68 
     69 
     70 # where everything comes from and where it goes:
     71 
     72 my $xlateFile = "$outdir/${pfx_adt}_xlate.c";
     73 my $headerFile = "$outdir/${pfx_adt}_event_N.h";
     74 
     75 my $filename = $ARGV[0];  # input XML file
     76 my $doc = new auditxml ($filename);
     77 $filename =~ s|.*/||g;
     78 
     79 $debug = $appDebug;
     80 
     81 my $genNotice = "
     82 DO NOT EDIT. This file is auto generated by the Solaris Audit
     83 system from $filename.
     84 
     85 See http://opensolaris.org/os/project/audit/
     86 ";
     87 
     88 # trim leading/trailing newlines
     89 $genNotice =~ s/^\n//s;
     90 $genNotice =~ s/\n$//s;
     91 
     92 my %xlateEventTable = ();
     93 my @xlateTypeList = ();
     94 my %xlateTypeList = ();
     95 my %eventAPI = ();
     96 my %eventExtra = ();
     97 my %headers = ();
     98 my %externalIdNo = ();
     99 my @outputState = ();
    100 my %nameTranslation = ();
    101 my @xlateDefaults = ();
    102 my %xlateDefault = ();
    103 my %msg_list = ();
    104 
    105 my $event;
    106 while ($event = $doc->getNextEvent()) {
    107     my $eventId = $event->getId();
    108     my $eventHeader = $event->getHeader();
    109     my $idNo = $event->getIdNo();
    110     $externalIdNo{$eventId} = $idNo;
    111     addHeader($eventHeader) if defined ($eventHeader);
    112     my $super;
    113     my $omit = $event->getOmit();
    114     my $eventType = '';
    115     if ($super = $event->getSuperClass()) {
    116 	$event = $super;
    117 	$eventType = 'instance';
    118     } else {
    119 	$eventType = $event->getType();
    120     }
    121 
    122     # header file for API use
    123     generateAPIFile($event, $eventId, $eventType, $eventHeader, $idNo)
    124         unless $omit eq 'always';
    125 
    126     # c file table for translation
    127     generateTableC($event, $eventId, $eventType, $eventHeader, $omit);
    128 }
    129 
    130 my $textList;
    131 while ($textList = $doc->getNextMsgId()) {
    132     generateMsgLists($textList);  # enum -> text mappings
    133 }
    134 
    135 printTableC($xlateFile);
    136 printAPIFile($headerFile, $doc);
    137 
    138 exit 0;
    139 
    140 
    141 sub printTableC {
    142     my $file = shift;
    143 
    144     unless (open(Cfile, ">$file")) {
    145 	print STDERR "can't open output file ($file): $!\n";
    146 	return;
    147     }
    148 
    149     my $notice = $genNotice;
    150     $notice =~ s/\n/\n * /gs;
    151     $notice =~ s/\s+\n/\n/gs;
    152     print Cfile <<EOF;
    153 /*
    154  * $notice
    155  */
    156 
    157 #include <bsm/libbsm.h>
    158 #include <adt_xlate.h>
    159 #include <libintl.h>
    160 
    161 EOF
    162     print Cfile "#ifndef _PRAUDIT\n";
    163     print Cfile "/* Internal data type definitions */\n\n";
    164     my $extDef;
    165     foreach $extDef (@xlateTypeList) {
    166       print Cfile "static $extDef\n";
    167     }
    168     @xlateTypeList = ();
    169 
    170     print Cfile "\n/* External event structure to internal event structure */\n\n";
    171 
    172     my @pointers = ();
    173 
    174     foreach my $eventId (sort keys %xlateEventTable) {
    175 	if ($xlateEventTable{$eventId}) {
    176 	    my ($ref1, $eventType, $firstToken, $eventHeader) =
    177 	      @{$xlateEventTable{$eventId}};
    178 	    my @entries = @$ref1;
    179 	    my $entry;
    180 	    my $entries = $#entries;
    181 	    my $count = $entries + 1;
    182 	    my $externalName = $nameTranslation{$eventId};
    183 	    my $externalRoot = $externalName;
    184 	    $externalRoot =~ s/${pfx_AUE}_//;
    185 	    my $structName = "XX_$externalRoot";
    186 	    my $root = $eventId;
    187 	    $root =~ s/${pfx_AUE}_//;
    188 	    my $externalId = $eventId;
    189 	    $externalId =~ s/${pfx_AUE}_/${pfx_ADT}_/;
    190 
    191 	    unless ($eventType eq 'generic') {
    192 		print Cfile "static struct entry $structName\[$count\] = {\n";
    193 		foreach $entry (@entries) {
    194 		    if ($entries--) {
    195 			$entry =~ s/EOL/,/;
    196 		    }
    197 		    else {
    198 			$entry =~ s/EOL//;
    199 		    }
    200 		    $entry =~ s/selfReference/$structName/;
    201 		    print Cfile "\t$entry\n";
    202 		}
    203 		print Cfile "};\n";
    204 
    205 		print Cfile "static struct translation X_$externalRoot = {\n";
    206 		push (@pointers, "X_$externalRoot");
    207 
    208 		print Cfile "\t0,\n";   # tx_offsetsCalculated = 0
    209 		print Cfile "\t$externalId,\n";
    210 		print Cfile "\t$externalName,\n";
    211 
    212 		print Cfile "\t$count,\n";
    213 		print Cfile "\t&XX_$externalRoot\[$firstToken\],\n";
    214 		print Cfile "\t&XX_$externalRoot\[0\]\n};\n";
    215 	    }
    216 	} else {
    217 	    print STDERR "expected entry for $eventId but none found\n";
    218 	}
    219     }
    220 
    221     my $count = $#pointers + 2;
    222     print Cfile "adt_translation_t *${pfx_adt}_xlate_table[$count] = {\n";
    223 
    224     my $firstEvent = 1;
    225     foreach my $eventId (@pointers) {
    226 	if ($firstEvent) {
    227 	    $firstEvent = 0;
    228 	}
    229 	else {
    230 	    print Cfile ",\n";
    231 	}
    232 	print Cfile "\t&$eventId";
    233     }
    234     print Cfile ",\n\tNULL\n};\n";
    235 
    236     # generate the Event preload() function
    237 
    238     print Cfile <<EOF;
    239 
    240 void
    241 ${pfx_adt}_preload(au_event_t event_id, adt_event_data_t *event_data)
    242 {
    243 	switch (event_id) {
    244 EOF
    245 
    246         foreach my $id (@xlateDefaults) {
    247 		my $adtID = $id;
    248 		$adtID =~ s/${pfx_AUE}/${pfx_ADT}/;
    249 
    250 		print Cfile <<EOF;
    251 	case $adtID:
    252 EOF
    253 		my @preloads = @{$xlateDefault{$id}};
    254 		while (@preloads) {
    255 			my $fieldName = shift @preloads;
    256 			my $default = shift @preloads;
    257 			$id =~ s/${pfx_AUE}_/${pfx_adt}_/;
    258 
    259 			print Cfile <<EOF;
    260 		event_data->$id.$fieldName = $default;
    261 EOF
    262 		}
    263 
    264 		print Cfile <<EOF;
    265 		break;
    266 EOF
    267 	}
    268 
    269     print Cfile <<EOF;
    270 	default:
    271 		break;
    272 	}
    273 }
    274 #endif
    275 
    276 EOF
    277 
    278     print Cfile "/* message lists */\n\n";
    279     my $listName;
    280     my @listName;
    281     foreach $listName (sort keys %msg_list) {
    282         my ($listRef, $headref) = @{$msg_list{$listName}};
    283 	my ($header, $start, $public, $deprecated) = @$headref;
    284 
    285 	my @listValue =  @$listRef;
    286 	my $listValue;
    287 	my $listLength = $#listValue + 1;
    288 
    289 	$listName = 'NULL' if ($#listValue < 0);
    290 
    291         push (@listName, [$listName, $listLength - 1, $start, $public]);
    292 
    293 	next if ($#listValue < 0);
    294 
    295 	print Cfile "/* Deprecated message list */\n" if ($deprecated);
    296 	print Cfile "static char *msg_$listName\[$listLength] = {\n";
    297 
    298 	my $ffirst = 1;
    299 	foreach $listValue (@listValue) {
    300 	    print Cfile ",\n" unless $ffirst;
    301 	    $ffirst = 0;
    302 	    my ($id, $text) = split(/\s*::\s*/, $listValue);
    303 	    if ($text) {
    304 	        print Cfile "\t\"$text\"";
    305 	    }
    306 	    else {
    307 	        print Cfile "\tNULL";
    308 	    }
    309 	}
    310 	print Cfile "\n};\n";
    311     }
    312 
    313     if ($#listName >= 0) {
    314 	print Cfile "\nstruct msg_text ${pfx_adt}_msg_text[", $#listName + 1,
    315 			"] = {\n";
    316 	my $ffirst = 1;
    317 	foreach $listName (@listName) {
    318             my ($name, $max, $start) = @$listName;
    319 	    $start = -$start if $start;
    320             print Cfile ",\n" unless $ffirst;
    321 	    $ffirst = 0;
    322 	    $name = "msg_$name" if ($name ne 'NULL');
    323             print Cfile "\t{0, $max, $name, $start}";
    324 	}
    325 	print Cfile "\n};\n";
    326     }
    327 
    328     close Cfile;
    329 }
    330 
    331 sub printAPIFile {
    332     my $file = shift;
    333     my $xmlDoc = shift;
    334 
    335     my @Hfile;
    336     @Hfile = openHeaderFiles($file);
    337 
    338     my $notice = $genNotice;
    339     $notice =~ s/\n/\n * /gs;
    340     $notice =~ s/\s+\n/\n/gs;
    341 
    342     foreach my $header (keys %headers) {
    343     	next unless $Hfile[$header];
    344 	*Hfile = $Hfile[$header];
    345 	my $include = "adt.h";
    346 	my $adt_event_n = "_${pfx_ADT}_EVENT_H";
    347 	if ($header > 0) {
    348 	    $include = "${pfx_adt}_event.h";
    349 	    $adt_event_n = "_${pfx_ADT}_EVENT_".$header."_H";
    350 	}
    351 	print Hfile <<EOF;
    352 /*
    353  * $notice
    354  */
    355 
    356 #ifndef $adt_event_n
    357 #define	$adt_event_n
    358 
    359 #include <bsm/$include>
    360 
    361 #ifdef	__cplusplus
    362 extern "C" {
    363 #endif
    364 
    365 /*
    366  * adt_put_event() status values.  Positive values are for kernel-generated
    367  * failure, -1 for user-space.  For ADT_SUCCESS, the adt_put_event() return_val
    368  * is not used; the convention is to set it to ADT_SUCCESS.
    369  */
    370 #define	ADT_SUCCESS	0
    371 #define	ADT_FAILURE	-1
    372 
    373 EOF
    374     }
    375 
    376     foreach my $listName (sort keys %msg_list) {
    377 	my $shortName = uc $listName;
    378 	$shortName =~ s/_TEXT//;
    379 
    380         my ($listRef, $headref) = @{$msg_list{$listName}};
    381 	my ($header, $start, $public, $deprecated) = @$headref;
    382 	next unless $Hfile[$header];
    383 	*Hfile = $Hfile[$header];
    384 
    385 	print Hfile "/* Deprecated message list */\n" if $deprecated;
    386 	print Hfile "#define\t${pfx_ADT}_$shortName\t$start\n" if $start;
    387 
    388 	my @listValue =  @$listRef;
    389 	next unless ($#listValue >= 0);
    390 	print Hfile "enum\t${pfx_adt}_$listName", " {\n";
    391 
    392 	my $listValue;
    393 	my $i = 0;
    394 	my $j = $#listValue;
    395 	my $comma = ',';
    396 	foreach $listValue (@listValue) {
    397 	    my ($id, $text) = split(/\s*::\s*/, $listValue);
    398 	    $comma = '' if $i++ == $j;
    399 	    if ($start) {
    400 		$start = " = $start$comma";
    401 	    } else {
    402 	        $start = "$comma\t";
    403 	    }
    404 	    $text = "(no token will be generated)" unless $text;
    405 	    my $line = "\t${pfx_ADT}_$shortName"."_$id$start\t/* ";
    406 	    # ensure whole line does not exceed 80 chars
    407 	    my $eline = $line.$text;
    408 	    #expand tabs
    409 	    1 while $eline =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
    410 	    if ((length($eline) > 77) && ($line =~ /\t\t/)) {
    411 	    	# 77 = 80 - length(" */")
    412 		# strip off double tab so that comment can be longer
    413 		$line =~ s/\t\t/\t/;
    414 		# shorten eline; don't mind where the spaces are removed, it is
    415 		# only $eline length which matters
    416 		$eline =~ s/ {8}//; 
    417 	    }
    418 	    if (length($eline) > 77) { # 80 - length(" */")
    419 	    	# here we use negative length in substr to leave off from the
    420 		# right side; 74 = 77 - length("...")
    421 	    	$line .= substr($text, 0, 74 - length($eline));
    422 		# strip off part of last word (already cut)
    423 		$line =~ s/\s(\S+)$/ /;
    424 		$line .= "...";
    425 	    } else {
    426 	    	$line .= $text;
    427 	    }
    428 	    print Hfile "$line */\n";
    429 	    $start = '';
    430 	}
    431 	print Hfile "};\n";
    432     }
    433 
    434     # generate defines for external event names
    435 
    436     foreach my $eventId (sort keys %eventAPI) {
    437         my ($header, $idNo) = @{$eventExtra{$eventId}};
    438 	unless (defined ($header)) {
    439 	    print STDERR "missing header selection for $eventId\n";
    440 	    next;
    441 	}
    442 	*Hfile = $Hfile[$header];
    443 	next unless $Hfile[$header];
    444 
    445 	my $l = length($eventId) + 8; # label plus preceding #define\t
    446 	$l = 5 - int(($l + 8)/8);
    447 	$l = 1 if $l < 1;
    448 	my $tab = "\t" x $l;
    449 
    450         print STDERR "missing id number for $eventId\n" unless $idNo;
    451 
    452 	$eventId =~ s/${pfx_AUE}_/${pfx_ADT}_/;
    453 	print Hfile "#define\t$eventId$tab$idNo\n";
    454     }
    455 
    456 
    457     # generate per-event structures
    458 
    459     foreach my $eventId (sort keys %eventAPI) {
    460         my ($header, $idNo) = @{$eventExtra{$eventId}};
    461 	my $dataId = $eventId;
    462 	$dataId =~ s/^${pfx_AUE}_/${pfx_adt}_/;
    463 	unless(defined ($header)) {
    464 	    print STDERR "$eventId is missing the header assignment\n";
    465 	    next;
    466 	}
    467 	*Hfile = $Hfile[$header];
    468 	next unless $Hfile[$header];
    469 
    470 	my $externalId = $eventId;
    471 	$externalId =~ s/${pfx_AUE}_/${pfx_ADT}_/;
    472 
    473 	print Hfile "\nstruct $dataId {\t/* $externalId */\n";
    474 
    475 	my @entries = @{$eventAPI{$eventId}};
    476 	my $entry;
    477 	if ($#entries < 0) {
    478 	    print Hfile "\tint\tdummy;\t/* not used */\n";
    479 	} else {
    480 	    foreach $entry (@entries) {
    481 		$entry =~ s/termid/adt_termid_t/;
    482 		print Hfile "\t$entry\n";
    483 	    }
    484 	}
    485 	print Hfile "};\n";
    486 	$eventId =~ s/^${pfx_AUE}_/${pfx_adt}_/;
    487 	print Hfile "typedef struct $dataId $eventId","_t;\n";
    488     }
    489 
    490     foreach my $header (sort keys %headers) {
    491 	$outputState[$header] = 0;
    492     }
    493     
    494     foreach my $eventId (sort keys %eventAPI) {
    495         my ($header, $idNo) = @{$eventExtra{$eventId}};
    496 	unless(defined ($header)) {
    497 	    # don't print duplicate error message
    498 	    next;
    499 	}
    500 	*Hfile = $Hfile[$header];
    501 	next unless $Hfile[$header];
    502 	if ($outputState[$header] == 0) {
    503 	    $outputState[$header] = 1;
    504 	    my $suffix = '';
    505 	    $suffix = "_$header" if $header;
    506 	    print Hfile "\nunion adt_event_data$suffix {\n";
    507 	}
    508         my $elementName = $eventId;
    509 	$elementName =~ s/^${pfx_AUE}_/${pfx_adt}_/;
    510 	$eventId =~ s/^${pfx_AUE}_/${pfx_adt}_/;
    511 	$elementName =~ s/_t$//;
    512         
    513 	print Hfile "\t\t$eventId","_t\t$elementName;\n";
    514     }
    515     foreach my $header (sort keys %headers) {
    516 	if ($outputState[$header]) {
    517 	    *Hfile = $Hfile[$header];
    518 	    next unless $Hfile[$header];
    519 	    print Hfile "};\n";
    520 	}
    521     }
    522     foreach my $header (keys %headers) {
    523     	next unless $Hfile[$header];
    524 	*Hfile = $Hfile[$header];
    525 	my $adt_event_n = "_${pfx_ADT}_EVENT_H";
    526 	if ($header > 0) {
    527 	    $adt_event_n = "_${pfx_ADT}_EVENT_".$header."_H";
    528 	}
    529 	print Hfile <<EOF;
    530 
    531 
    532 #ifndef	${pfx_ADT}_PRIVATE
    533 #define	${pfx_ADT}_PRIVATE
    534 
    535 /*
    536  * These interfaces are project private and will change without
    537  * notice as needed for the Solaris Audit project.
    538  */
    539 
    540 extern	void	adt_get_auid(const adt_session_data_t *, au_id_t *);
    541 extern	void	adt_set_auid(const adt_session_data_t *, const au_id_t);
    542 
    543 extern	void	adt_get_mask(const adt_session_data_t *, au_mask_t *);
    544 extern	void	adt_set_mask(const adt_session_data_t *, const au_mask_t *);
    545 
    546 extern	void	adt_get_termid(const adt_session_data_t *, au_tid_addr_t *);
    547 extern	void	adt_set_termid(const adt_session_data_t *,
    548     const au_tid_addr_t *);
    549 
    550 extern	void	adt_get_asid(const adt_session_data_t *, au_asid_t *);
    551 extern	void	adt_set_asid(const adt_session_data_t *, const au_asid_t);
    552 extern	au_asid_t adt_get_unique_id(au_id_t);
    553 extern	void	adt_load_table(const adt_session_data_t *, adt_translation_t **,
    554     void (*preload)(au_event_t, adt_event_data_t *));
    555 
    556 extern	void	${pfx_adt}_preload(au_event_t, adt_event_data_t *);
    557 
    558 extern adt_translation_t *${pfx_adt}_xlate_table[];
    559 
    560 #endif
    561 
    562 #ifdef	__cplusplus
    563 }
    564 #endif
    565 
    566 #endif	/* $adt_event_n */
    567 EOF
    568     }
    569     closeHeaderFiles(@Hfile);
    570 }
    571 
    572 sub generateTableC {
    573     my $event = shift;
    574     my $eventId = shift;
    575     my $eventType = shift;
    576     my $eventHeader = shift;
    577     my $omit = shift;
    578 
    579     my %tokenType = (
    580 	#
    581 	#	tokenTypes are the ones that are actually defined
    582 	#	for use in adt.xml audit records
    583 	#
    584 
    585 	#	  'acl'			=> 'AUT_ACL',		# not defined
    586 	#	  'arbitrary'		=> 'AUT_ARBITRARY',	# not defined
    587 	#	  'arg'			=> 'AUT_ARG',		# not defined
    588 	#	  'attr'		=> 'AUT_ATTR',
    589 		  'command'		=> 'AUT_CMD',
    590 		  'command_alt'		=> 'ADT_CMD_ALT',	# dummy token id
    591 	#	  'date'		=> 'AUT_TEXT',		# not used
    592 	#	  'exec_args'   	=> 'AUT_EXEC_ARGS',	# not defined
    593 	#	  'exec_env'    	=> 'AUT_EXEC_ENV',	# not defined
    594 	#	  'exit'        	=> 'AUT_EXIT',		# not defined
    595 		  'fmri'        	=> 'AUT_FMRI',
    596 	#	  'groups'      	=> 'AUT_GROUPS',	# not defined
    597 	#	  'header'      	=> 'AUT_HEADER',	# not defined
    598 		  'in_peer'     	=> 'ADT_IN_PEER',	# dummy token id
    599 		  'in_remote'     	=> 'ADT_IN_REMOTE',	# dummy token id
    600 		  'tid'          	=> 'AUT_TID',
    601 	#	  'ipc'         	=> 'AUT_IPC',		# not defined
    602 	#	  'ipc_perm'    	=> 'AUT_IPC_PERM',	# not defined
    603 		  'iport'		=> 'AUT_IPORT',
    604 		  'label'		=> 'AUT_LABEL',
    605 		  'newgroups'   	=> 'AUT_NEWGROUPS',
    606 	#	  'opaque'      	=> 'AUT_OPAQUE',	# not defined
    607 		  'path'        	=> 'AUT_PATH',
    608 		  'path_list'		=> '-AUT_PATH',		# dummy token id
    609 		  'process'     	=> 'AUT_PROCESS',
    610 		  'priv_effective'	=> 'ADT_AUT_PRIV_E',	# dummy token id
    611 		  'priv_limit'		=> 'ADT_AUT_PRIV_L', 	# dummy token id
    612 		  'priv_inherit'	=> 'ADT_AUT_PRIV_I',	# dummy token id
    613 		  'return'      	=> 'AUT_RETURN',
    614 	#	  'seq'         	=> 'AUT_SEQ',		# not defined
    615 	#	  'socket'      	=> 'AUT_SOCKET',	# not defined
    616 	#	  'socket-inet' 	=> 'AUT_SOCKET_INET',
    617 		  'subject'     	=> 'AUT_SUBJECT',
    618 		  'text'        	=> 'AUT_TEXT',
    619 	#	  'trailer'     	=> 'AUT_TRAILER',	# not defined
    620 		  'uauth'		=> 'AUT_UAUTH',
    621 		  'zonename'		=> 'AUT_ZONENAME'
    622 		 );
    623 
    624     my @xlateEntryList = ();
    625 
    626     my $external = $event->getExternal();
    627     my $internal = $event->getInternal();
    628 
    629     unless ($external) {
    630 	print STDERR "No external object captured for event $eventId\n";
    631 	return;
    632     }
    633     if ($eventType) {
    634 	$nameTranslation{$eventId} = $eventId;
    635     } else {
    636 	$nameTranslation{$eventId} = $external->getInternalName();
    637     }
    638     unless ($internal) {
    639 	print STDERR "No internal object captured for event $eventId\n";
    640 	return;
    641     }
    642     my @entryRef = $internal->getEntries();
    643     my $entryRef;
    644     my @tokenOrder = ();
    645     my $firstTokenIndex = 0; # djdj not used yet, djdj BUG!
    646     			     # needs to be used by translate table
    647 
    648     if ($internal->isReorder()) { # prescan the entry list to get the token order
    649       my @inputOrder;
    650       foreach $entryRef (@entryRef) {
    651 	my ($intEntry, $entry) = @$entryRef;
    652 	push (@inputOrder, $intEntry->getAttr('order'));
    653       }
    654 
    655       my $i; # walk down the inputOrder list once
    656       my $k = 1; # discover next in line
    657       my $l = 0; # who should point to next in line
    658       for ($i = 0; $i <= $#inputOrder; $i++) {
    659 	my $j;
    660 	for ($j = 0; $j <= $#inputOrder; $j++) {
    661 	  if ($k == $inputOrder[$j]) {
    662 	    if ($k == 1) {
    663 	        $firstTokenIndex = $j;
    664 	    } else {
    665 	        $tokenOrder[$l] = "&(selfReference[$j])";
    666 	    }
    667 	    $l = $j;
    668 	    last;
    669 	  }
    670 	}
    671 	$k++;
    672       }
    673       $tokenOrder[$l] = 'NULL';
    674     }
    675     else { # default order -- input order same as output
    676       my $i;
    677       my $j;
    678       for ($i = 0; $i < $#entryRef; $i++) {
    679 	my $j = $i + 1;
    680 	$tokenOrder[$i] = "&(selfReference[$j])";
    681       }
    682       $tokenOrder[$#entryRef] = 'NULL';
    683     }
    684 
    685     my $sequence = 0;
    686     foreach $entryRef (@entryRef) {
    687       my ($intEntry, $entry) = @$entryRef;
    688       my $entryId = $entry->getAttr('id');
    689 
    690       my ($extEntry, $unusedEntry, $tokenId) =
    691 	$external->getEntry($entryId);
    692       my $opt = $extEntry->getAttr('opt');
    693 
    694       if ($opt eq 'none') {
    695 	if (defined ($doc->getToken($tokenId))) {
    696 	  if (defined ($tokenType{$tokenId})) {
    697 	    $tokenId = $tokenType{$tokenId};
    698 	  }
    699 	  else {
    700 	    print STDERR "token id $tokenId not implemented\n";
    701 	  }
    702 	}
    703 	else {
    704 	  print STDERR "token = $tokenId is undefined\n";
    705 	  $tokenId = 'error';
    706 	}
    707 	my ($xlate, $jni) =
    708 	  formatTableEntry ('', $tokenId, $eventId, '', 0, 0,
    709 			    $tokenOrder[$sequence], 'NULL', $omit);
    710 	push (@xlateEntryList, $xlate);
    711       }
    712       else {
    713 	my $dataType = $extEntry->getAttr('type');
    714 	$dataType =~ s/\s+//g;   # remove blanks (char * => char*)
    715 
    716 	my $enumGroup = '';
    717 	if ($dataType =~ /^msg/i) {
    718 	    $enumGroup = $dataType;
    719 	    $enumGroup =~ s/^msg\s*//i;
    720 	    $enumGroup = "${pfx_adt}_" . $enumGroup;
    721 	}
    722 	my $required = ($opt eq 'required') ? 1 : 0;
    723 	my $tsol = 0;
    724 	my $tokenId = $intEntry->getAttr('token');
    725 	my $token;
    726 	my $tokenName;
    727 	my $tokenFormat = $intEntry->getAttr('format');
    728 	if (defined ($tokenFormat)) {
    729 	  $tokenFormat = "\"$tokenFormat\"";
    730 	}
    731 	else {
    732 	  $tokenFormat = 'NULL';
    733 	}
    734 	
    735 	if (defined ($token = $doc->getToken($tokenId))) {
    736 	  $tsol = (lc $token->getUsage() eq 'tsol') ? 1 : 0;
    737 	  if (defined ($tokenType{$tokenId})) {
    738 	    $tokenName = $tokenType{$tokenId};
    739 	  }
    740 	  else {
    741 	    print STDERR "token id $tokenId not implemented\n";
    742 	  }
    743 	}
    744 	else {
    745 	  print STDERR 
    746 	    "$tokenId is an unimplemented token ($entryId in $eventId)\n";
    747 	  $tokenName = 'AUT_TEXT';
    748 	}
    749 	my ($xlate, $jni) =
    750 	  formatTableEntry($entryId, $tokenName, $eventId, $dataType, $required,
    751 			   $tsol, $tokenOrder[$sequence], $tokenFormat,
    752 			   $enumGroup, $omit);
    753 	push (@xlateEntryList, $xlate);
    754       }
    755       $sequence++;
    756     }
    757     $xlateEventTable{$eventId} = [\@xlateEntryList, $eventType, $firstTokenIndex,
    758 				 $eventHeader];
    759 }
    760 
    761 sub formatTableEntry {
    762     my ($id, $token, $eventId, $type, $required, $tsol, $sequence, $format,
    763 	$enumGroup, $omitEntry) = @_;
    764 
    765 
    766     # does this map belong in the xml source?  (at least the defaults?)
    767     # fill in the default value only if it is other than zero.
    768     #		      base type		    adt name,	default value
    769     my %entryDef = ( 'au_asid_t'       	=> ['ADT_UINT32',	''],
    770 		     'uint_t'		=> ['ADT_UINT32',      	''],
    771 		     'int'		=> ['ADT_INT',		''],
    772 		     'int32_t'		=> ['ADT_INT32',	''],
    773 		     'uid_t'		=> ['ADT_UID',		'AU_NOAUDITID'],
    774 		     'gid_t'		=> ['ADT_GID',		'AU_NOAUDITID'],
    775 		     'uid_t*'		=> ['ADT_UIDSTAR',	''],
    776 		     'gid_t*'		=> ['ADT_GIDSTAR',	''],
    777 		     'char'		=> ['ADT_CHAR',		''],
    778 		     'char*'		=> ['ADT_CHARSTAR',	''],
    779 		     'char**'		=> ['ADT_CHAR2STAR',	''],
    780 		     'long'		=> ['ADT_LONG',		''],
    781 		     'pid_t'		=> ['ADT_PID',		''],
    782 		     'priv_set_t*'	=> ['ADT_PRIVSTAR',	''],
    783 		     'ulong_t'		=> ['ADT_ULONG',	''],
    784 		     'uint16_t',	=> ['ADT_UINT16',	''],
    785 		     'uint32_t'		=> ['ADT_UINT32',	''],
    786 		     'uint32_t*'	=> ['ADT_UINT32STAR',	''],
    787 		     'uint32_t[]'	=> ['ADT_UINT32ARRAY',  ''],
    788 		     'uint64_t'		=> ['ADT_UINT64',	''],
    789 		     'uint64_t*'	=> ['ADT_UINT64STAR',	''],
    790 		     'm_label_t*'	=> ['ADT_MLABELSTAR',	''],
    791 		     'fd_t'		=> ['ADT_FD',		'-1'],
    792 		    );
    793     my $xlateLabel = $uniLabel.$xlateUniLabelInc;
    794     my $xlateLabelInc = 0;
    795     my $xlateLine = '';
    796     my @jniLine = ();
    797 
    798 	# the list handling should be a simple loop with a loop of one
    799         # falling out naturally.
    800 
    801     unless ($type =~ /,/) {	# if list, then generate sequence of entries
    802       my $dataType;
    803       my $dataSize;
    804       my $xlateLabelRef = '';
    805 
    806       my $arraySize = '';
    807       $arraySize = $1 if ($type =~ s/\[(\d+)\]/[]/);
    808 
    809       my $entryType = ${$entryDef{$type}}[0];
    810 
    811       my @xlateType = ();	# for adt_xlate.c
    812       my $typeCount = 1;
    813 
    814       if ($entryType) {
    815 	$dataType = $entryType;
    816 	$type =~ s/([^*]+)\s*(\*+)/$1 $2/;
    817 	$type =~ s/\[\]//;
    818 	$dataSize = "sizeof ($type)";
    819 	if ($arraySize) {
    820 		$dataSize = "$arraySize * " . $dataSize;
    821 	}
    822 	$xlateLine = "{{$dataType, $dataSize}}";
    823 	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
    824       } elsif ($type eq '') {
    825 	  $xlateLabelRef = 'NULL';
    826       } elsif ($type =~ /^msg/i) {
    827 	$type =~ s/^msg//i;
    828 	$dataType = 'ADT_MSG';
    829 	my $dataEnum = 'ADT_LIST_' . uc $type;
    830 	$xlateLine = "{{$dataType, $dataEnum}}";
    831 	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
    832       } elsif ($type =~ /time_t/i) {
    833 	$dataType = 'ADT_DATE';
    834 	$dataSize = "sizeof (time_t)";
    835 	$xlateLine = "{{$dataType, $dataSize}}";
    836 	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
    837       } elsif ($type =~ /termid/i) {
    838 	$dataType = 'ADT_TERMIDSTAR';
    839 	$dataSize = "sizeof (au_tid_addr_t *)";
    840 	$xlateLine = "{{$dataType, $dataSize}}";
    841 	push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
    842       } elsif (uc $omitEntry eq 'JNI') {
    843 	$xlateLabelRef = 'NULL';
    844       } else {
    845 	print STDERR "$type is not an implemented data type\n";
    846 	$xlateLabelRef = 'NULL';
    847       }
    848       if ($xlateLine && !($xlateTypeList{$xlateLine})) {
    849 	$xlateTypeList{$xlateLine} = $xlateLabel;
    850 	push (@xlateTypeList, "datadef\t$xlateLabel\[1\] =\t$xlateLine;");
    851 	$xlateLabelInc = 1;
    852       } else {
    853 	$xlateLabel = $xlateTypeList{$xlateLine};
    854       }
    855       $xlateLabelRef = '&' . $xlateLabel . '[0]'
    856 	unless $xlateLabelRef eq 'NULL';
    857 
    858       # "EOL" is where a comma should go unless end of list
    859       $xlateLine = "{$token,\t1,\t$xlateLabelRef,\t$sequence,\n" .
    860 	  "\t\t0,\t$required,\t$tsol,\t$format}EOL";
    861       
    862       if (uc $omitEntry ne 'ALWAYS' && ${$entryDef{$type}}[1]) {
    863 	  my @list = ();
    864 	  if ($xlateDefault{$eventId}) {
    865 	      @list = @{$xlateDefault{$eventId}};
    866 	  } else {
    867 	      push (@xlateDefaults, $eventId);
    868 	  }
    869 	  push (@list, $id, ${$entryDef{$type}}[1]);
    870 	  $xlateDefault{$eventId} = \@list;
    871       }
    872     } else {	# is a list
    873       my @type = split(/,/, $type);
    874       my @arraySize = ();
    875       my @id   = split(/,/, $id);
    876       my @jniId  = @id;
    877       my $dataType;
    878       my $typeCount = ($#type + 1);
    879       my @xlateType = ();
    880       my @default = ();
    881 
    882       foreach my $dtype (@type) {
    883 	my $jniId = shift @jniId;
    884 	my $id = shift @id;
    885 	my $arraySize = '';
    886 	$arraySize = $1 if ($dtype =~ s/\[(\d+)\]/[]/);
    887 
    888 	my $entryType = ${$entryDef{$dtype}}[0];
    889 	if ($entryType) {
    890 	  my $type = $dtype;
    891 	  $type =~ s/([^*]+)\s*(\*+)/$1 $2/;
    892 	  $type =~ s/\[\]//;
    893 
    894 	  my $sizeString = "sizeof";
    895 	  $sizeString = "$arraySize * " . $sizeString if $arraySize;
    896 	  push (@xlateType, "\{$entryType, $sizeString ($type)\}");
    897 	  push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
    898 	} elsif ($type =~ /^msg/i) {
    899 	  $type =~ s/^msg//i;
    900 	  $dataType = 'ADT_MSG';
    901 	  my $dataEnum = 'ADT_LIST_' . uc $type;
    902 	  push (@xlateType, "\{$dataType, $dataEnum\}};");
    903 	  push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
    904 	} elsif ($type =~ /time_t/i) {
    905 	  $dataType = 'ADT_DATE';
    906 	  push (@xlateType, "\{$entryType, sizeof ($type)\}");
    907 	  push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
    908 	} elsif ($type =~ /termid/i) {
    909 	  $dataType = 'ADT_TERMIDSTAR';
    910 	  push (@xlateType, "\{$dataType, sizeof (au_tid_addr_t *)\}");
    911 	  push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
    912 	} elsif (uc $omitEntry eq 'JNI') {
    913 	  # nothing to do.
    914 	} else {
    915 	  print STDERR "$dtype is not an implemented data type\n";
    916 	}
    917 	if (uc $omitEntry ne 'ALWAYS' && ${$entryDef{$dtype}}[1]) {
    918 	  push (@default, $id, ${$entryDef{$dtype}}[1]);
    919 	}
    920       }
    921       my $xlateArray = "\[$typeCount\] =\t{" . join(",\n\t\t\t\t", @xlateType) . "};";
    922       
    923       unless ($xlateTypeList{$xlateArray}) {
    924 	$xlateTypeList{$xlateArray} = $xlateLabel;
    925 	$xlateArray = "datadef\t$xlateLabel" . $xlateArray;
    926 	push (@xlateTypeList, $xlateArray);
    927 	$xlateLabelInc = 1;
    928       } else {
    929 	$xlateLabel = $xlateTypeList{$xlateArray};
    930       }
    931       $xlateLine =
    932 	"{$token,\t$typeCount,\t&$xlateLabel\[0\],\t$sequence,\n" .
    933         "\t\t0,\t$required,\t$tsol,\t$format}EOL";
    934       if (@default) {
    935 	  my @list = ();
    936 	  if ($xlateDefault{$eventId}) {
    937 	      @list = @{$xlateDefault{$eventId}};
    938 	  } else {
    939 	      push (@xlateDefaults, $eventId);
    940 	  }
    941 	  push (@list, @default);
    942 	  $xlateDefault{$eventId} = \@list;
    943       }
    944     }
    945     $xlateUniLabelInc++ if $xlateLabelInc;
    946     return ($xlateLine, \@jniLine);
    947 }
    948 
    949 sub generateAPIFile {
    950     my $event = shift;
    951     my $eventId = shift;
    952     my $eventType = shift;
    953     my $eventHeader = shift;
    954     my $idNo = shift;
    955 
    956     my @entryList = ();
    957 
    958     my $external = $event->getExternal();
    959 
    960     if ($eventType && $debug) {
    961 	print STDERR "event $eventId is of type $eventType\n";
    962     }
    963 
    964     return unless $external;
    965 
    966     my ($extEntry, $entry, $tokenId, $format);
    967     while (($extEntry, $entry, $tokenId, $format) = $external->getNextEntry()) {
    968 	last unless $entry;
    969 	my $entryId = $entry->getAttr('id');
    970 
    971 	unless (defined $entryId) {
    972 	    print STDERR "undefined entry id for external $eventId\n";
    973 	    next;
    974 	}
    975 	my $option = $extEntry->getAttr('opt');
    976 	next if ($option eq 'none');
    977 
    978 	if (defined (my $token = $doc->getToken($tokenId))) {
    979 	  $option = 'Trusted Solaris only'
    980 	    if (lc $token->getUsage() eq 'tsol') ? 1 : 0;
    981 	}
    982 	$option .= " (format: $format)" if $format;
    983 
    984 	my $dataType = $extEntry->getAttr('type');
    985 	unless (defined $dataType) {
    986 	  print STDERR "no type defined for external tag for $eventId\n";
    987 	  $dataType = "error";
    988 	}
    989 
    990 	my $comment = $entry->getContent();
    991 
    992 	if (($dataType =~ /,/) || ($entryId =~ /,/)) {
    993 	  my @type = split(/\s*,\s*/, $dataType);
    994 	  my @id   = split(/\s*,\s*/, $entryId);
    995 	  if ($#type != $#id) {
    996 	    print STDERR
    997 	      "number of data types ($dataType) does not match number of ids ($entryId)",
    998 	      " for event $eventId\n";
    999 	    if ($#type < $#id) {
   1000 	      $#id = $#type;
   1001 	    }
   1002 	    else {
   1003 	      $#type = $#id;
   1004 	    }
   1005 	  }
   1006 
   1007 	  my $i;
   1008 	  my $line = '';
   1009 	  $line = "/* $comment */\n\t" if defined $comment;
   1010 	  for ($i = 0; $i <= $#type; $i++) {
   1011 	    my ($primitive, $dereference) =
   1012 	        ($type[$i] =~ /([^\*]+)\s*(\**)/);
   1013 	    $id[$i] .= $1 if ($primitive =~ s/(\[\d+\])//);
   1014 	    $line .= "$primitive\t$dereference$id[$i];\t/*  $option  */";
   1015 	    push (@entryList, $line);
   1016 	    $line = '';
   1017 	  }
   1018 	}
   1019 	else {
   1020 	  my $line = '';
   1021 	  $line = "/* $comment */\n\t" if defined $comment;
   1022 	  if ($dataType =~ /^msg/i) {
   1023 	      $dataType =~ s/^msg\s*//i;
   1024 	      $line .= "enum ${pfx_adt}_$dataType" . "\t$entryId;\t/*  $option  */";
   1025 	  }
   1026 	  elsif ($dataType =~ /time_t/i) {
   1027 	      $line .= "time_t\t$entryId;\t/* $option */";
   1028 	  }
   1029 	  else {
   1030 	    my ($primitive, $dereference) =
   1031 	        ($dataType =~ /([^\*]+)\s*(\**)/);
   1032 	    $entryId .= $1 if ($primitive =~ s/(\[\d+\])//);
   1033 	    $line .= "$primitive\t$dereference$entryId;\t/* $option */";
   1034 	  }
   1035 	  push (@entryList, $line);
   1036 	}
   1037     }
   1038     $eventExtra{$eventId} = [$eventHeader, $idNo];
   1039     $eventAPI{$eventId} = \@entryList;
   1040 }
   1041 
   1042 sub generateMsgLists {
   1043     my $textList = shift;
   1044 
   1045     my $textName = $textList->getId();
   1046     my $header = $textList->getHeader();
   1047     my $start = $textList->getMsgStart();
   1048     my $public = $textList->getMsgPublic();
   1049     my $deprecated = $textList->getDeprecated();
   1050 
   1051     addHeader($header);
   1052     print "$textName starts at $start\n" if $debug;
   1053 
   1054     my $entry;
   1055     my @entry;
   1056     while ($entry = $textList->getNextMsg()) {
   1057         if ($debug) {
   1058 	    my ($id, $text) = split(/\s*::\s*/, $entry);
   1059 	    print "   $id = $text\n";
   1060 	}
   1061 	unshift (@entry, $entry);
   1062     }
   1063     $msg_list{$textName} =
   1064 	[\@entry, [$header, $start, $public, $deprecated]];
   1065 }
   1066 
   1067 sub addHeader {
   1068     my $header_index = shift;
   1069 
   1070     die "invalid adt_event_N.h index: $header_index\n"
   1071         unless ($header_index =~ /^\d+$/);
   1072 
   1073     $headers{$header_index} = $header_index;
   1074 }
   1075 
   1076 # $header = 0 is a special case; it is for adt_event.h
   1077 # $header > 0 creates adt_event_N.h, where N = $header
   1078 
   1079 sub openHeaderFiles {
   1080     my $outfile = shift;	# path to an adt_event_N.h file
   1081 
   1082     my $header;
   1083     my @Hfile = (); # potentially sparse array of file handles
   1084     my @HfileName = (); # parallel array to Hfile, file name (not path)
   1085     foreach $header (sort keys %headers) {
   1086         my $file = $outfile;
   1087 	if ($header > 0) {
   1088 	    $file =~ s/_N/_$header/;
   1089 	} else {
   1090 	    $file =~ s/_N//;
   1091 	}
   1092 	unless (open($Hfile[$header], ">$file")) {
   1093 	    print STDERR "can't open output ($file): $!\n";
   1094 	    $HfileName[$header] = '';
   1095 	    $Hfile[$header] = '';
   1096 	} else {
   1097 	    my @tmp = split(/\//, $file);
   1098 	    $HfileName[$header] = $tmp[$#tmp];
   1099 	}
   1100     }
   1101     return (@Hfile);
   1102 }
   1103 
   1104 sub closeHeaderFiles {
   1105     my @Hfile = @_;
   1106 
   1107     my $header;
   1108     foreach $header (sort keys %headers) {
   1109 	close $Hfile[$header] if $Hfile[$header];
   1110     }
   1111 }
   1112