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