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