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