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 2010 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         #         'ipc'                 => 'AUT_IPC',                # not defined
 601         #         'ipc_perm'            => 'AUT_IPC_PERM',   # not defined
 602                   'iport'               => 'AUT_IPORT',
 603                   'label'               => 'AUT_LABEL',
 604                   'newgroups'           => 'AUT_NEWGROUPS',
 605         #         'opaque'              => 'AUT_OPAQUE',     # not defined
 606                   'path'                => 'AUT_PATH',
 607                   'path_list'           => '-AUT_PATH',              # dummy token id
 608                   'process'             => 'AUT_PROCESS',
 609                   'priv_effective'      => 'ADT_AUT_PRIV_E', # dummy token id
 610                   'priv_limit'          => 'ADT_AUT_PRIV_L',         # dummy token id
 611                   'priv_inherit'        => 'ADT_AUT_PRIV_I', # dummy token id
 612                   'return'              => 'AUT_RETURN',
 613         #         'seq'                 => 'AUT_SEQ',                # not defined
 614         #         'socket'              => 'AUT_SOCKET',     # not defined
 615         #         'socket-inet'         => 'AUT_SOCKET_INET',
 616                   'subject'             => 'AUT_SUBJECT',
 617                   'text'                => 'AUT_TEXT',
 618                   'tid'                 => 'AUT_TID',
 619         #         'trailer'             => 'AUT_TRAILER',    # not defined
 620                   'uauth'               => 'AUT_UAUTH',
 621                   'user'                => 'AUT_USER',
 622                   'zonename'            => 'AUT_ZONENAME'
 623                  );
 624 
 625     my @xlateEntryList = ();
 626 
 627     my $external = $event->getExternal();
 628     my $internal = $event->getInternal();
 629 
 630     unless ($external) {
 631         print STDERR "No external object captured for event $eventId\n";
 632         return;
 633     }
 634     if ($eventType) {
 635         $nameTranslation{$eventId} = $eventId;
 636     } else {
 637         $nameTranslation{$eventId} = $external->getInternalName();
 638     }
 639     unless ($internal) {
 640         print STDERR "No internal object captured for event $eventId\n";
 641         return;
 642     }
 643     my @entryRef = $internal->getEntries();
 644     my $entryRef;
 645     my @tokenOrder = ();
 646     my $firstTokenIndex = 0; # djdj not used yet, djdj BUG!
 647                              # needs to be used by translate table
 648 
 649     if ($internal->isReorder()) { # prescan the entry list to get the token order
 650       my @inputOrder;
 651       foreach $entryRef (@entryRef) {
 652         my ($intEntry, $entry) = @$entryRef;
 653         push (@inputOrder, $intEntry->getAttr('order'));
 654       }
 655 
 656       my $i; # walk down the inputOrder list once
 657       my $k = 1; # discover next in line
 658       my $l = 0; # who should point to next in line
 659       for ($i = 0; $i <= $#inputOrder; $i++) {
 660         my $j;
 661         for ($j = 0; $j <= $#inputOrder; $j++) {
 662           if ($k == $inputOrder[$j]) {
 663             if ($k == 1) {
 664                 $firstTokenIndex = $j;
 665             } else {
 666                 $tokenOrder[$l] = "&(selfReference[$j])";
 667             }
 668             $l = $j;
 669             last;
 670           }
 671         }
 672         $k++;
 673       }
 674       $tokenOrder[$l] = 'NULL';
 675     }
 676     else { # default order -- input order same as output
 677       my $i;
 678       my $j;
 679       for ($i = 0; $i < $#entryRef; $i++) {
 680         my $j = $i + 1;
 681         $tokenOrder[$i] = "&(selfReference[$j])";
 682       }
 683       $tokenOrder[$#entryRef] = 'NULL';
 684     }
 685 
 686     my $sequence = 0;
 687     foreach $entryRef (@entryRef) {
 688       my ($intEntry, $entry) = @$entryRef;
 689       my $entryId = $entry->getAttr('id');
 690 
 691       my ($extEntry, $unusedEntry, $tokenId) =
 692         $external->getEntry($entryId);
 693       my $opt = $extEntry->getAttr('opt');
 694 
 695       if ($opt eq 'none') {
 696         if (defined ($doc->getToken($tokenId))) {
 697           if (defined ($tokenType{$tokenId})) {
 698             $tokenId = $tokenType{$tokenId};
 699           }
 700           else {
 701             print STDERR "token id $tokenId not implemented\n";
 702           }
 703         }
 704         else {
 705           print STDERR "token = $tokenId is undefined\n";
 706           $tokenId = 'error';
 707         }
 708         my ($xlate, $jni) =
 709           formatTableEntry ('', $tokenId, $eventId, '', 0, 0,
 710                             $tokenOrder[$sequence], 'NULL', $omit);
 711         push (@xlateEntryList, $xlate);
 712       }
 713       else {
 714         my $dataType = $extEntry->getAttr('type');
 715         $dataType =~ s/\s+//g;   # remove blanks (char * => char*)
 716 
 717         my $enumGroup = '';
 718         if ($dataType =~ /^msg/i) {
 719             $enumGroup = $dataType;
 720             $enumGroup =~ s/^msg\s*//i;
 721             $enumGroup = "${pfx_adt}_" . $enumGroup;
 722         }
 723         my $required = ($opt eq 'required') ? 1 : 0;
 724         my $tsol = 0;
 725         my $tokenId = $intEntry->getAttr('token');
 726         my $token;
 727         my $tokenName;
 728         my $tokenFormat = $intEntry->getAttr('format');
 729         if (defined ($tokenFormat)) {
 730           $tokenFormat = "\"$tokenFormat\"";
 731         }
 732         else {
 733           $tokenFormat = 'NULL';
 734         }
 735         
 736         if (defined ($token = $doc->getToken($tokenId))) {
 737           $tsol = (lc $token->getUsage() eq 'tsol') ? 1 : 0;
 738           if (defined ($tokenType{$tokenId})) {
 739             $tokenName = $tokenType{$tokenId};
 740           }
 741           else {
 742             print STDERR "token id $tokenId not implemented\n";
 743           }
 744         }
 745         else {
 746           print STDERR 
 747             "$tokenId is an unimplemented token ($entryId in $eventId)\n";
 748           $tokenName = 'AUT_TEXT';
 749         }
 750         my ($xlate, $jni) =
 751           formatTableEntry($entryId, $tokenName, $eventId, $dataType, $required,
 752                            $tsol, $tokenOrder[$sequence], $tokenFormat,
 753                            $enumGroup, $omit);
 754         push (@xlateEntryList, $xlate);
 755       }
 756       $sequence++;
 757     }
 758     $xlateEventTable{$eventId} = [\@xlateEntryList, $eventType, $firstTokenIndex,
 759                                  $eventHeader];
 760 }
 761 
 762 sub formatTableEntry {
 763     my ($id, $token, $eventId, $type, $required, $tsol, $sequence, $format,
 764         $enumGroup, $omitEntry) = @_;
 765 
 766 
 767     # does this map belong in the xml source?  (at least the defaults?)
 768     # fill in the default value only if it is other than zero.
 769     #                 base type             adt name,   default value
 770     my %entryDef = ( 'au_asid_t'        => ['ADT_UINT32',    ''],
 771                      'uint_t'           => ['ADT_UINT32',            ''],
 772                      'int'              => ['ADT_INT',               ''],
 773                      'int32_t'          => ['ADT_INT32',     ''],
 774                      'uid_t'            => ['ADT_UID',               'AU_NOAUDITID'],
 775                      'gid_t'            => ['ADT_GID',               'AU_NOAUDITID'],
 776                      'uid_t*'           => ['ADT_UIDSTAR',   ''],
 777                      'gid_t*'           => ['ADT_GIDSTAR',   ''],
 778                      'char'             => ['ADT_CHAR',              ''],
 779                      'char*'            => ['ADT_CHARSTAR',  ''],
 780                      'char**'           => ['ADT_CHAR2STAR', ''],
 781                      'long'             => ['ADT_LONG',              ''],
 782                      'pid_t'            => ['ADT_PID',               ''],
 783                      'priv_set_t*'      => ['ADT_PRIVSTAR',  ''],
 784                      'ulong_t'          => ['ADT_ULONG',     ''],
 785                      'uint16_t',        => ['ADT_UINT16',    ''],
 786                      'uint32_t'         => ['ADT_UINT32',    ''],
 787                      'uint32_t*'        => ['ADT_UINT32STAR',        ''],
 788                      'uint32_t[]'       => ['ADT_UINT32ARRAY',  ''],
 789                      'uint64_t'         => ['ADT_UINT64',    ''],
 790                      'uint64_t*'        => ['ADT_UINT64STAR',        ''],
 791                      'm_label_t*'       => ['ADT_MLABELSTAR',        ''],
 792                      'fd_t'             => ['ADT_FD',                '-1'],
 793                     );
 794     my $xlateLabel = $uniLabel.$xlateUniLabelInc;
 795     my $xlateLabelInc = 0;
 796     my $xlateLine = '';
 797     my @jniLine = ();
 798 
 799         # the list handling should be a simple loop with a loop of one
 800         # falling out naturally.
 801 
 802     unless ($type =~ /,/) {     # if list, then generate sequence of entries
 803       my $dataType;
 804       my $dataSize;
 805       my $xlateLabelRef = '';
 806 
 807       my $arraySize = '';
 808       $arraySize = $1 if ($type =~ s/\[(\d+)\]/[]/);
 809 
 810       my $entryType = ${$entryDef{$type}}[0];
 811 
 812       my @xlateType = ();       # for adt_xlate.c
 813       my $typeCount = 1;
 814 
 815       if ($entryType) {
 816         $dataType = $entryType;
 817         $type =~ s/([^*]+)\s*(\*+)/$1 $2/;
 818         $type =~ s/\[\]//;
 819         $dataSize = "sizeof ($type)";
 820         if ($arraySize) {
 821                 $dataSize = "$arraySize * " . $dataSize;
 822         }
 823         $xlateLine = "{{$dataType, $dataSize}}";
 824         push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
 825       } elsif ($type eq '') {
 826           $xlateLabelRef = 'NULL';
 827       } elsif ($type =~ /^msg/i) {
 828         $type =~ s/^msg//i;
 829         $dataType = 'ADT_MSG';
 830         my $dataEnum = 'ADT_LIST_' . uc $type;
 831         $xlateLine = "{{$dataType, $dataEnum}}";
 832         push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
 833       } elsif ($type =~ /time_t/i) {
 834         $dataType = 'ADT_DATE';
 835         $dataSize = "sizeof (time_t)";
 836         $xlateLine = "{{$dataType, $dataSize}}";
 837         push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
 838       } elsif ($type =~ /termid/i) {
 839         $dataType = 'ADT_TERMIDSTAR';
 840         $dataSize = "sizeof (au_tid_addr_t *)";
 841         $xlateLine = "{{$dataType, $dataSize}}";
 842         push (@jniLine, [$id, $dataType, $format, $enumGroup, $required]);
 843       } elsif (uc $omitEntry eq 'JNI') {
 844         $xlateLabelRef = 'NULL';
 845       } else {
 846         print STDERR "$type is not an implemented data type\n";
 847         $xlateLabelRef = 'NULL';
 848       }
 849       if ($xlateLine && !($xlateTypeList{$xlateLine})) {
 850         $xlateTypeList{$xlateLine} = $xlateLabel;
 851         push (@xlateTypeList, "datadef\t$xlateLabel\[1\] =\t$xlateLine;");
 852         $xlateLabelInc = 1;
 853       } else {
 854         $xlateLabel = $xlateTypeList{$xlateLine};
 855       }
 856       $xlateLabelRef = '&' . $xlateLabel . '[0]'
 857         unless $xlateLabelRef eq 'NULL';
 858 
 859       # "EOL" is where a comma should go unless end of list
 860       $xlateLine = "{$token,\t1,\t$xlateLabelRef,\t$sequence,\n" .
 861           "\t\t0,\t$required,\t$tsol,\t$format}EOL";
 862       
 863       if (uc $omitEntry ne 'ALWAYS' && ${$entryDef{$type}}[1]) {
 864           my @list = ();
 865           if ($xlateDefault{$eventId}) {
 866               @list = @{$xlateDefault{$eventId}};
 867           } else {
 868               push (@xlateDefaults, $eventId);
 869           }
 870           push (@list, $id, ${$entryDef{$type}}[1]);
 871           $xlateDefault{$eventId} = \@list;
 872       }
 873     } else {    # is a list
 874       my @type = split(/,/, $type);
 875       my @arraySize = ();
 876       my @id   = split(/,/, $id);
 877       my @jniId  = @id;
 878       my $dataType;
 879       my $typeCount = ($#type + 1);
 880       my @xlateType = ();
 881       my @default = ();
 882 
 883       foreach my $dtype (@type) {
 884         my $jniId = shift @jniId;
 885         my $id = shift @id;
 886         my $arraySize = '';
 887         $arraySize = $1 if ($dtype =~ s/\[(\d+)\]/[]/);
 888 
 889         my $entryType = ${$entryDef{$dtype}}[0];
 890         if ($entryType) {
 891           my $type = $dtype;
 892           $type =~ s/([^*]+)\s*(\*+)/$1 $2/;
 893           $type =~ s/\[\]//;
 894 
 895           my $sizeString = "sizeof";
 896           $sizeString = "$arraySize * " . $sizeString if $arraySize;
 897           push (@xlateType, "\{$entryType, $sizeString ($type)\}");
 898           push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
 899         } elsif ($type =~ /^msg/i) {
 900           $type =~ s/^msg//i;
 901           $dataType = 'ADT_MSG';
 902           my $dataEnum = 'ADT_LIST_' . uc $type;
 903           push (@xlateType, "\{$dataType, $dataEnum\}};");
 904           push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
 905         } elsif ($type =~ /time_t/i) {
 906           $dataType = 'ADT_DATE';
 907           push (@xlateType, "\{$entryType, sizeof ($type)\}");
 908           push (@jniLine, [$jniId, $entryType, $format, $enumGroup, $required]);
 909         } elsif ($type =~ /termid/i) {
 910           $dataType = 'ADT_TERMIDSTAR';
 911           push (@xlateType, "\{$dataType, sizeof (au_tid_addr_t *)\}");
 912           push (@jniLine, [$jniId, $dataType, $format, $enumGroup, $required]);
 913         } elsif (uc $omitEntry eq 'JNI') {
 914           # nothing to do.
 915         } else {
 916           print STDERR "$dtype is not an implemented data type\n";
 917         }
 918         if (uc $omitEntry ne 'ALWAYS' && ${$entryDef{$dtype}}[1]) {
 919           push (@default, $id, ${$entryDef{$dtype}}[1]);
 920         }
 921       }
 922       my $xlateArray = "\[$typeCount\] =\t{" . join(",\n\t\t\t\t", @xlateType) . "};";
 923       
 924       unless ($xlateTypeList{$xlateArray}) {
 925         $xlateTypeList{$xlateArray} = $xlateLabel;
 926         $xlateArray = "datadef\t$xlateLabel" . $xlateArray;
 927         push (@xlateTypeList, $xlateArray);
 928         $xlateLabelInc = 1;
 929       } else {
 930         $xlateLabel = $xlateTypeList{$xlateArray};
 931       }
 932       $xlateLine =
 933         "{$token,\t$typeCount,\t&$xlateLabel\[0\],\t$sequence,\n" .
 934         "\t\t0,\t$required,\t$tsol,\t$format}EOL";
 935       if (@default) {
 936           my @list = ();
 937           if ($xlateDefault{$eventId}) {
 938               @list = @{$xlateDefault{$eventId}};
 939           } else {
 940               push (@xlateDefaults, $eventId);
 941           }
 942           push (@list, @default);
 943           $xlateDefault{$eventId} = \@list;
 944       }
 945     }
 946     $xlateUniLabelInc++ if $xlateLabelInc;
 947     return ($xlateLine, \@jniLine);
 948 }
 949 
 950 sub generateAPIFile {
 951     my $event = shift;
 952     my $eventId = shift;
 953     my $eventType = shift;
 954     my $eventHeader = shift;
 955     my $idNo = shift;
 956 
 957     my @entryList = ();
 958 
 959     my $external = $event->getExternal();
 960 
 961     if ($eventType && $debug) {
 962         print STDERR "event $eventId is of type $eventType\n";
 963     }
 964 
 965     return unless $external;
 966 
 967     my ($extEntry, $entry, $tokenId, $format);
 968     while (($extEntry, $entry, $tokenId, $format) = $external->getNextEntry()) {
 969         last unless $entry;
 970         my $entryId = $entry->getAttr('id');
 971 
 972         unless (defined $entryId) {
 973             print STDERR "undefined entry id for external $eventId\n";
 974             next;
 975         }
 976         my $option = $extEntry->getAttr('opt');
 977         next if ($option eq 'none');
 978 
 979         if (defined (my $token = $doc->getToken($tokenId))) {
 980           $option = 'Trusted Solaris only'
 981             if (lc $token->getUsage() eq 'tsol') ? 1 : 0;
 982         }
 983         $option .= " (format: $format)" if $format;
 984 
 985         my $dataType = $extEntry->getAttr('type');
 986         unless (defined $dataType) {
 987           print STDERR "no type defined for external tag for $eventId\n";
 988           $dataType = "error";
 989         }
 990 
 991         my $comment = $entry->getContent();
 992 
 993         if (($dataType =~ /,/) || ($entryId =~ /,/)) {
 994           my @type = split(/\s*,\s*/, $dataType);
 995           my @id   = split(/\s*,\s*/, $entryId);
 996           if ($#type != $#id) {
 997             print STDERR
 998               "number of data types ($dataType) does not match number of ids ($entryId)",
 999               " for event $eventId\n";
1000             if ($#type < $#id) {
1001               $#id = $#type;
1002             }
1003             else {
1004               $#type = $#id;
1005             }
1006           }
1007 
1008           my $i;
1009           my $line = '';
1010           $line = "/* $comment */\n\t" if defined $comment;
1011           for ($i = 0; $i <= $#type; $i++) {
1012             my ($primitive, $dereference) =
1013                 ($type[$i] =~ /([^\*]+)\s*(\**)/);
1014             $id[$i] .= $1 if ($primitive =~ s/(\[\d+\])//);
1015             $line .= "$primitive\t$dereference$id[$i];\t/*  $option  */";
1016             push (@entryList, $line);
1017             $line = '';
1018           }
1019         }
1020         else {
1021           my $line = '';
1022           $line = "/* $comment */\n\t" if defined $comment;
1023           if ($dataType =~ /^msg/i) {
1024               $dataType =~ s/^msg\s*//i;
1025               $line .= "enum ${pfx_adt}_$dataType" . "\t$entryId;\t/*  $option  */";
1026           }
1027           elsif ($dataType =~ /time_t/i) {
1028               $line .= "time_t\t$entryId;\t/* $option */";
1029           }
1030           else {
1031             my ($primitive, $dereference) =
1032                 ($dataType =~ /([^\*]+)\s*(\**)/);
1033             $entryId .= $1 if ($primitive =~ s/(\[\d+\])//);
1034             $line .= "$primitive\t$dereference$entryId;\t/* $option */";
1035           }
1036           push (@entryList, $line);
1037         }
1038     }
1039     $eventExtra{$eventId} = [$eventHeader, $idNo];
1040     $eventAPI{$eventId} = \@entryList;
1041 }
1042 
1043 sub generateMsgLists {
1044     my $textList = shift;
1045 
1046     my $textName = $textList->getId();
1047     my $header = $textList->getHeader();
1048     my $start = $textList->getMsgStart();
1049     my $public = $textList->getMsgPublic();
1050     my $deprecated = $textList->getDeprecated();
1051 
1052     addHeader($header);
1053     print "$textName starts at $start\n" if $debug;
1054 
1055     my $entry;
1056     my @entry;
1057     while ($entry = $textList->getNextMsg()) {
1058         if ($debug) {
1059             my ($id, $text) = split(/\s*::\s*/, $entry);
1060             print "   $id = $text\n";
1061         }
1062         unshift (@entry, $entry);
1063     }
1064     $msg_list{$textName} =
1065         [\@entry, [$header, $start, $public, $deprecated]];
1066 }
1067 
1068 sub addHeader {
1069     my $header_index = shift;
1070 
1071     die "invalid adt_event_N.h index: $header_index\n"
1072         unless ($header_index =~ /^\d+$/);
1073 
1074     $headers{$header_index} = $header_index;
1075 }
1076 
1077 # $header = 0 is a special case; it is for adt_event.h
1078 # $header > 0 creates adt_event_N.h, where N = $header
1079 
1080 sub openHeaderFiles {
1081     my $outfile = shift;        # path to an adt_event_N.h file
1082 
1083     my $header;
1084     my @Hfile = (); # potentially sparse array of file handles
1085     my @HfileName = (); # parallel array to Hfile, file name (not path)
1086     foreach $header (sort keys %headers) {
1087         my $file = $outfile;
1088         if ($header > 0) {
1089             $file =~ s/_N/_$header/;
1090         } else {
1091             $file =~ s/_N//;
1092         }
1093         unless (open($Hfile[$header], ">$file")) {
1094             print STDERR "can't open output ($file): $!\n";
1095             $HfileName[$header] = '';
1096             $Hfile[$header] = '';
1097         } else {
1098             my @tmp = split(/\//, $file);
1099             $HfileName[$header] = $tmp[$#tmp];
1100         }
1101     }
1102     return (@Hfile);
1103 }
1104 
1105 sub closeHeaderFiles {
1106     my @Hfile = @_;
1107 
1108     my $header;
1109     foreach $header (sort keys %headers) {
1110         close $Hfile[$header] if $Hfile[$header];
1111     }
1112 }