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