1#!/usr/bin/env perl
2
3# Copyright 2011 The Go Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file.
6
7#
8# Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
9#
10# Build a MIB with each entry being an array containing the level, type and
11# a hash that will contain additional entries if the current entry is a node.
12# We then walk this MIB and create a flattened sysctl name to OID hash.
13#
14
15use strict;
16
17my $debug = 0;
18my %ctls = ();
19
20my @headers = qw (
21	sys/sysctl.h
22	sys/socket.h
23	sys/tty.h
24	sys/malloc.h
25	sys/mount.h
26	sys/namei.h
27	sys/sem.h
28	sys/shm.h
29	sys/vmmeter.h
30	uvm/uvm_param.h
31	uvm/uvm_swap_encrypt.h
32	ddb/db_var.h
33	net/if.h
34	net/if_pfsync.h
35	net/pipex.h
36	netinet/in.h
37	netinet/icmp_var.h
38	netinet/igmp_var.h
39	netinet/ip_ah.h
40	netinet/ip_carp.h
41	netinet/ip_divert.h
42	netinet/ip_esp.h
43	netinet/ip_ether.h
44	netinet/ip_gre.h
45	netinet/ip_ipcomp.h
46	netinet/ip_ipip.h
47	netinet/pim_var.h
48	netinet/tcp_var.h
49	netinet/udp_var.h
50	netinet6/in6.h
51	netinet6/ip6_divert.h
52	netinet6/pim6_var.h
53	netinet/icmp6.h
54	netmpls/mpls.h
55);
56
57my @ctls = qw (
58	kern
59	vm
60	fs
61	net
62	#debug				# Special handling required
63	hw
64	#machdep			# Arch specific
65	user
66	ddb
67	#vfs				# Special handling required
68	fs.posix
69	kern.forkstat
70	kern.intrcnt
71	kern.malloc
72	kern.nchstats
73	kern.seminfo
74	kern.shminfo
75	kern.timecounter
76	kern.tty
77	kern.watchdog
78	net.bpf
79	net.ifq
80	net.inet
81	net.inet.ah
82	net.inet.carp
83	net.inet.divert
84	net.inet.esp
85	net.inet.etherip
86	net.inet.gre
87	net.inet.icmp
88	net.inet.igmp
89	net.inet.ip
90	net.inet.ip.ifq
91	net.inet.ipcomp
92	net.inet.ipip
93	net.inet.mobileip
94	net.inet.pfsync
95	net.inet.pim
96	net.inet.tcp
97	net.inet.udp
98	net.inet6
99	net.inet6.divert
100	net.inet6.ip6
101	net.inet6.icmp6
102	net.inet6.pim6
103	net.inet6.tcp6
104	net.inet6.udp6
105	net.mpls
106	net.mpls.ifq
107	net.key
108	net.pflow
109	net.pfsync
110	net.pipex
111	net.rt
112	vm.swapencrypt
113	#vfsgenctl			# Special handling required
114);
115
116# Node name "fixups"
117my %ctl_map = (
118	"ipproto" => "net.inet",
119	"net.inet.ipproto" => "net.inet",
120	"net.inet6.ipv6proto" => "net.inet6",
121	"net.inet6.ipv6" => "net.inet6.ip6",
122	"net.inet.icmpv6" => "net.inet6.icmp6",
123	"net.inet6.divert6" => "net.inet6.divert",
124	"net.inet6.tcp6" => "net.inet.tcp",
125	"net.inet6.udp6" => "net.inet.udp",
126	"mpls" => "net.mpls",
127	"swpenc" => "vm.swapencrypt"
128);
129
130# Node mappings
131my %node_map = (
132	"net.inet.ip.ifq" => "net.ifq",
133	"net.inet.pfsync" => "net.pfsync",
134	"net.mpls.ifq" => "net.ifq"
135);
136
137my $ctlname;
138my %mib = ();
139my %sysctl = ();
140my $node;
141
142sub debug() {
143	print STDERR "$_[0]\n" if $debug;
144}
145
146# Walk the MIB and build a sysctl name to OID mapping.
147sub build_sysctl() {
148	my ($node, $name, $oid) = @_;
149	my %node = %{$node};
150	my @oid = @{$oid};
151
152	foreach my $key (sort keys %node) {
153		my @node = @{$node{$key}};
154		my $nodename = $name.($name ne '' ? '.' : '').$key;
155		my @nodeoid = (@oid, $node[0]);
156		if ($node[1] eq 'CTLTYPE_NODE') {
157			if (exists $node_map{$nodename}) {
158				$node = \%mib;
159				$ctlname = $node_map{$nodename};
160				foreach my $part (split /\./, $ctlname) {
161					$node = \%{@{$$node{$part}}[2]};
162				}
163			} else {
164				$node = $node[2];
165			}
166			&build_sysctl($node, $nodename, \@nodeoid);
167		} elsif ($node[1] ne '') {
168			$sysctl{$nodename} = \@nodeoid;
169		}
170	}
171}
172
173foreach my $ctl (@ctls) {
174	$ctls{$ctl} = $ctl;
175}
176
177# Build MIB
178foreach my $header (@headers) {
179	&debug("Processing $header...");
180	open HEADER, "/usr/include/$header" ||
181	    print STDERR "Failed to open $header\n";
182	while (<HEADER>) {
183		if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ ||
184		    $_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ ||
185		    $_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) {
186			if ($1 eq 'CTL_NAMES') {
187				# Top level.
188				$node = \%mib;
189			} else {
190				# Node.
191				my $nodename = lc($2);
192				if ($header =~ /^netinet\//) {
193					$ctlname = "net.inet.$nodename";
194				} elsif ($header =~ /^netinet6\//) {
195					$ctlname = "net.inet6.$nodename";
196				} elsif ($header =~ /^net\//) {
197					$ctlname = "net.$nodename";
198				} else {
199					$ctlname = "$nodename";
200					$ctlname =~ s/^(fs|net|kern)_/$1\./;
201				}
202				if (exists $ctl_map{$ctlname}) {
203					$ctlname = $ctl_map{$ctlname};
204				}
205				if (not exists $ctls{$ctlname}) {
206					&debug("Ignoring $ctlname...");
207					next;
208				}
209
210				# Walk down from the top of the MIB.
211				$node = \%mib;
212				foreach my $part (split /\./, $ctlname) {
213					if (not exists $$node{$part}) {
214						&debug("Missing node $part");
215						$$node{$part} = [ 0, '', {} ];
216					}
217					$node = \%{@{$$node{$part}}[2]};
218				}
219			}
220
221			# Populate current node with entries.
222			my $i = -1;
223			while (defined($_) && $_ !~ /^}/) {
224				$_ = <HEADER>;
225				$i++ if $_ =~ /{.*}/;
226				next if $_ !~ /{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}/;
227				$$node{$1} = [ $i, $2, {} ];
228			}
229		}
230	}
231	close HEADER;
232}
233
234&build_sysctl(\%mib, "", []);
235
236print <<EOF;
237// mksysctl_openbsd.pl
238// Code generated by the command above; DO NOT EDIT.
239
240package syscall;
241
242type mibentry struct {
243	ctlname string
244	ctloid []_C_int
245}
246
247var sysctlMib = []mibentry {
248EOF
249
250foreach my $name (sort keys %sysctl) {
251	my @oid = @{$sysctl{$name}};
252	print "\t{ \"$name\", []_C_int{ ", join(', ', @oid), " } }, \n";
253}
254
255print <<EOF;
256}
257EOF
258