xref: /illumos-gate/usr/src/lib/libbsm/auditxml (revision 689b9301078f0c35c7f198fcee8032a0d30eff3a)
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
30my $prog = $0; $prog =~ s|.*/||g;
31my $usage = <<EOF;
32
33Usage: $prog [options] <xml-input-file>
34Options:
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
41EOF
42
43use auditxml;
44use Getopt::Std;
45use strict;
46
47our $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.
52my $appDebug = 0; # used after return from "new auditxml";
53
54# Process command-line options
55our ($opt_d, $opt_e, $opt_i, $opt_o);
56$opt_e = "";
57$opt_i = "";
58$opt_o = "";
59if (!getopts('de:i:o:') || $#ARGV != 0) {
60    die $usage;
61}
62my $outdir = $opt_o || ".";
63my $pfx_adt = lc($opt_i) || "adt";
64my $pfx_ADT = uc($pfx_adt);
65my $pfx_AUE = uc($opt_e) || "AUE";
66
67$appDebug = $opt_d;
68
69my $uniLabel = "adr";
70my $xlateUniLabelInc = 0;
71
72
73# where everything comes from and where it goes:
74
75my $xlateFile = "$outdir/${pfx_adt}_xlate.c";
76my $headerFile = "$outdir/${pfx_adt}_event_N.h";
77
78my $filename = $ARGV[0];  # input XML file
79my $doc = new auditxml ($filename);
80$filename =~ s|.*/||g;
81
82$debug = $appDebug;
83
84my $genNotice = "
85DO NOT EDIT. This file is auto generated by the Solaris Audit
86system from $filename.
87
88See http://opensolaris.org/os/project/audit/
89";
90
91# trim leading/trailing newlines
92$genNotice =~ s/^\n//s;
93$genNotice =~ s/\n$//s;
94
95my %xlateEventTable = ();
96my @xlateTypeList = ();
97my %xlateTypeList = ();
98my %eventAPI = ();
99my %eventExtra = ();
100my %headers = ();
101my %externalIdNo = ();
102my @outputState = ();
103my %nameTranslation = ();
104my @xlateDefaults = ();
105my %xlateDefault = ();
106my %msg_list = ();
107
108my $event;
109while ($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
133my $textList;
134while ($textList = $doc->getNextMsgId()) {
135    generateMsgLists($textList);  # enum -> text mappings
136}
137
138printTableC($xlateFile);
139printAPIFile($headerFile, $doc);
140
141exit 0;
142
143
144sub 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
164EOF
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
243void
244${pfx_adt}_preload(au_event_t event_id, adt_event_data_t *event_data)
245{
246	switch (event_id) {
247EOF
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:
255EOF
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;
264EOF
265		}
266
267		print Cfile <<EOF;
268		break;
269EOF
270	}
271
272    print Cfile <<EOF;
273	default:
274		break;
275	}
276}
277#endif
278
279EOF
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
334sub 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
365extern "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
376EOF
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
543extern	void	adt_get_auid(const adt_session_data_t *, au_id_t *);
544extern	void	adt_set_auid(const adt_session_data_t *, const au_id_t);
545
546extern	void	adt_get_mask(const adt_session_data_t *, au_mask_t *);
547extern	void	adt_set_mask(const adt_session_data_t *, const au_mask_t *);
548
549extern	void	adt_get_termid(const adt_session_data_t *, au_tid_addr_t *);
550extern	void	adt_set_termid(const adt_session_data_t *,
551    const au_tid_addr_t *);
552
553extern	void	adt_get_asid(const adt_session_data_t *, au_asid_t *);
554extern	void	adt_set_asid(const adt_session_data_t *, const au_asid_t);
555extern	au_asid_t adt_get_unique_id(au_id_t);
556extern	void	adt_load_table(const adt_session_data_t *, adt_translation_t **,
557    void (*preload)(au_event_t, adt_event_data_t *));
558
559extern	void	${pfx_adt}_preload(au_event_t, adt_event_data_t *);
560
561extern adt_translation_t *${pfx_adt}_xlate_table[];
562
563#endif
564
565#ifdef	__cplusplus
566}
567#endif
568
569#endif	/* $adt_event_n */
570EOF
571    }
572    closeHeaderFiles(@Hfile);
573}
574
575sub 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
766sub 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
954sub 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
1047sub 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
1072sub 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
1084sub 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
1109sub closeHeaderFiles {
1110    my @Hfile = @_;
1111
1112    my $header;
1113    foreach $header (sort keys %headers) {
1114	close $Hfile[$header] if $Hfile[$header];
1115    }
1116}
1117