1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build ignore
6
7// Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
8//
9// Build a MIB with each entry being an array containing the level, type and
10// a hash that will contain additional entries if the current entry is a node.
11// We then walk this MIB and create a flattened sysctl name to OID hash.
12
13package main
14
15import (
16	"bufio"
17	"fmt"
18	"os"
19	"path/filepath"
20	"regexp"
21	"sort"
22	"strings"
23)
24
25var (
26	goos, goarch string
27)
28
29// cmdLine returns this programs's commandline arguments.
30func cmdLine() string {
31	return "go run mksysctl_openbsd.go " + strings.Join(os.Args[1:], " ")
32}
33
34// buildTags returns build tags.
35func buildTags() string {
36	return fmt.Sprintf("%s,%s", goarch, goos)
37}
38
39// reMatch performs regular expression match and stores the substring slice to value pointed by m.
40func reMatch(re *regexp.Regexp, str string, m *[]string) bool {
41	*m = re.FindStringSubmatch(str)
42	if *m != nil {
43		return true
44	}
45	return false
46}
47
48type nodeElement struct {
49	n  int
50	t  string
51	pE *map[string]nodeElement
52}
53
54var (
55	debugEnabled bool
56	mib          map[string]nodeElement
57	node         *map[string]nodeElement
58	nodeMap      map[string]string
59	sysCtl       []string
60)
61
62var (
63	ctlNames1RE = regexp.MustCompile(`^#define\s+(CTL_NAMES)\s+{`)
64	ctlNames2RE = regexp.MustCompile(`^#define\s+(CTL_(.*)_NAMES)\s+{`)
65	ctlNames3RE = regexp.MustCompile(`^#define\s+((.*)CTL_NAMES)\s+{`)
66	netInetRE   = regexp.MustCompile(`^netinet/`)
67	netInet6RE  = regexp.MustCompile(`^netinet6/`)
68	netRE       = regexp.MustCompile(`^net/`)
69	bracesRE    = regexp.MustCompile(`{.*}`)
70	ctlTypeRE   = regexp.MustCompile(`{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}`)
71	fsNetKernRE = regexp.MustCompile(`^(fs|net|kern)_`)
72)
73
74func debug(s string) {
75	if debugEnabled {
76		fmt.Fprintln(os.Stderr, s)
77	}
78}
79
80// Walk the MIB and build a sysctl name to OID mapping.
81func buildSysctl(pNode *map[string]nodeElement, name string, oid []int) {
82	lNode := pNode // local copy of pointer to node
83	var keys []string
84	for k := range *lNode {
85		keys = append(keys, k)
86	}
87	sort.Strings(keys)
88
89	for _, key := range keys {
90		nodename := name
91		if name != "" {
92			nodename += "."
93		}
94		nodename += key
95
96		nodeoid := append(oid, (*pNode)[key].n)
97
98		if (*pNode)[key].t == `CTLTYPE_NODE` {
99			if _, ok := nodeMap[nodename]; ok {
100				lNode = &mib
101				ctlName := nodeMap[nodename]
102				for _, part := range strings.Split(ctlName, ".") {
103					lNode = ((*lNode)[part]).pE
104				}
105			} else {
106				lNode = (*pNode)[key].pE
107			}
108			buildSysctl(lNode, nodename, nodeoid)
109		} else if (*pNode)[key].t != "" {
110			oidStr := []string{}
111			for j := range nodeoid {
112				oidStr = append(oidStr, fmt.Sprintf("%d", nodeoid[j]))
113			}
114			text := "\t{ \"" + nodename + "\", []_C_int{ " + strings.Join(oidStr, ", ") + " } }, \n"
115			sysCtl = append(sysCtl, text)
116		}
117	}
118}
119
120func main() {
121	// Get the OS (using GOOS_TARGET if it exist)
122	goos = os.Getenv("GOOS_TARGET")
123	if goos == "" {
124		goos = os.Getenv("GOOS")
125	}
126	// Get the architecture (using GOARCH_TARGET if it exists)
127	goarch = os.Getenv("GOARCH_TARGET")
128	if goarch == "" {
129		goarch = os.Getenv("GOARCH")
130	}
131	// Check if GOOS and GOARCH environment variables are defined
132	if goarch == "" || goos == "" {
133		fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n")
134		os.Exit(1)
135	}
136
137	mib = make(map[string]nodeElement)
138	headers := [...]string{
139		`sys/sysctl.h`,
140		`sys/socket.h`,
141		`sys/tty.h`,
142		`sys/malloc.h`,
143		`sys/mount.h`,
144		`sys/namei.h`,
145		`sys/sem.h`,
146		`sys/shm.h`,
147		`sys/vmmeter.h`,
148		`uvm/uvmexp.h`,
149		`uvm/uvm_param.h`,
150		`uvm/uvm_swap_encrypt.h`,
151		`ddb/db_var.h`,
152		`net/if.h`,
153		`net/if_pfsync.h`,
154		`net/pipex.h`,
155		`netinet/in.h`,
156		`netinet/icmp_var.h`,
157		`netinet/igmp_var.h`,
158		`netinet/ip_ah.h`,
159		`netinet/ip_carp.h`,
160		`netinet/ip_divert.h`,
161		`netinet/ip_esp.h`,
162		`netinet/ip_ether.h`,
163		`netinet/ip_gre.h`,
164		`netinet/ip_ipcomp.h`,
165		`netinet/ip_ipip.h`,
166		`netinet/pim_var.h`,
167		`netinet/tcp_var.h`,
168		`netinet/udp_var.h`,
169		`netinet6/in6.h`,
170		`netinet6/ip6_divert.h`,
171		`netinet6/pim6_var.h`,
172		`netinet/icmp6.h`,
173		`netmpls/mpls.h`,
174	}
175
176	ctls := [...]string{
177		`kern`,
178		`vm`,
179		`fs`,
180		`net`,
181		//debug			/* Special handling required */
182		`hw`,
183		//machdep		/* Arch specific */
184		`user`,
185		`ddb`,
186		//vfs			/* Special handling required */
187		`fs.posix`,
188		`kern.forkstat`,
189		`kern.intrcnt`,
190		`kern.malloc`,
191		`kern.nchstats`,
192		`kern.seminfo`,
193		`kern.shminfo`,
194		`kern.timecounter`,
195		`kern.tty`,
196		`kern.watchdog`,
197		`net.bpf`,
198		`net.ifq`,
199		`net.inet`,
200		`net.inet.ah`,
201		`net.inet.carp`,
202		`net.inet.divert`,
203		`net.inet.esp`,
204		`net.inet.etherip`,
205		`net.inet.gre`,
206		`net.inet.icmp`,
207		`net.inet.igmp`,
208		`net.inet.ip`,
209		`net.inet.ip.ifq`,
210		`net.inet.ipcomp`,
211		`net.inet.ipip`,
212		`net.inet.mobileip`,
213		`net.inet.pfsync`,
214		`net.inet.pim`,
215		`net.inet.tcp`,
216		`net.inet.udp`,
217		`net.inet6`,
218		`net.inet6.divert`,
219		`net.inet6.ip6`,
220		`net.inet6.icmp6`,
221		`net.inet6.pim6`,
222		`net.inet6.tcp6`,
223		`net.inet6.udp6`,
224		`net.mpls`,
225		`net.mpls.ifq`,
226		`net.key`,
227		`net.pflow`,
228		`net.pfsync`,
229		`net.pipex`,
230		`net.rt`,
231		`vm.swapencrypt`,
232		//vfsgenctl		/* Special handling required */
233	}
234
235	// Node name "fixups"
236	ctlMap := map[string]string{
237		"ipproto":             "net.inet",
238		"net.inet.ipproto":    "net.inet",
239		"net.inet6.ipv6proto": "net.inet6",
240		"net.inet6.ipv6":      "net.inet6.ip6",
241		"net.inet.icmpv6":     "net.inet6.icmp6",
242		"net.inet6.divert6":   "net.inet6.divert",
243		"net.inet6.tcp6":      "net.inet.tcp",
244		"net.inet6.udp6":      "net.inet.udp",
245		"mpls":                "net.mpls",
246		"swpenc":              "vm.swapencrypt",
247	}
248
249	// Node mappings
250	nodeMap = map[string]string{
251		"net.inet.ip.ifq": "net.ifq",
252		"net.inet.pfsync": "net.pfsync",
253		"net.mpls.ifq":    "net.ifq",
254	}
255
256	mCtls := make(map[string]bool)
257	for _, ctl := range ctls {
258		mCtls[ctl] = true
259	}
260
261	for _, header := range headers {
262		debug("Processing " + header)
263		file, err := os.Open(filepath.Join("/usr/include", header))
264		if err != nil {
265			fmt.Fprintf(os.Stderr, "%v\n", err)
266			os.Exit(1)
267		}
268		s := bufio.NewScanner(file)
269		for s.Scan() {
270			var sub []string
271			if reMatch(ctlNames1RE, s.Text(), &sub) ||
272				reMatch(ctlNames2RE, s.Text(), &sub) ||
273				reMatch(ctlNames3RE, s.Text(), &sub) {
274				if sub[1] == `CTL_NAMES` {
275					// Top level.
276					node = &mib
277				} else {
278					// Node.
279					nodename := strings.ToLower(sub[2])
280					ctlName := ""
281					if reMatch(netInetRE, header, &sub) {
282						ctlName = "net.inet." + nodename
283					} else if reMatch(netInet6RE, header, &sub) {
284						ctlName = "net.inet6." + nodename
285					} else if reMatch(netRE, header, &sub) {
286						ctlName = "net." + nodename
287					} else {
288						ctlName = nodename
289						ctlName = fsNetKernRE.ReplaceAllString(ctlName, `$1.`)
290					}
291
292					if val, ok := ctlMap[ctlName]; ok {
293						ctlName = val
294					}
295					if _, ok := mCtls[ctlName]; !ok {
296						debug("Ignoring " + ctlName + "...")
297						continue
298					}
299
300					// Walk down from the top of the MIB.
301					node = &mib
302					for _, part := range strings.Split(ctlName, ".") {
303						if _, ok := (*node)[part]; !ok {
304							debug("Missing node " + part)
305							(*node)[part] = nodeElement{n: 0, t: "", pE: &map[string]nodeElement{}}
306						}
307						node = (*node)[part].pE
308					}
309				}
310
311				// Populate current node with entries.
312				i := -1
313				for !strings.HasPrefix(s.Text(), "}") {
314					s.Scan()
315					if reMatch(bracesRE, s.Text(), &sub) {
316						i++
317					}
318					if !reMatch(ctlTypeRE, s.Text(), &sub) {
319						continue
320					}
321					(*node)[sub[1]] = nodeElement{n: i, t: sub[2], pE: &map[string]nodeElement{}}
322				}
323			}
324		}
325		err = s.Err()
326		if err != nil {
327			fmt.Fprintf(os.Stderr, "%v\n", err)
328			os.Exit(1)
329		}
330		file.Close()
331	}
332	buildSysctl(&mib, "", []int{})
333
334	sort.Strings(sysCtl)
335	text := strings.Join(sysCtl, "")
336
337	fmt.Printf(srcTemplate, cmdLine(), buildTags(), text)
338}
339
340const srcTemplate = `// %s
341// Code generated by the command above; DO NOT EDIT.
342
343// +build %s
344
345package unix
346
347type mibentry struct {
348	ctlname string
349	ctloid []_C_int
350}
351
352var sysctlMib = []mibentry {
353%s
354}
355`
356