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