| // Copyright 2019 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // +build ignore | |
| // Parse the header files for OpenBSD and generate a Go usable sysctl MIB. | |
| // | |
| // Build a MIB with each entry being an array containing the level, type and | |
| // a hash that will contain additional entries if the current entry is a node. | |
| // We then walk this MIB and create a flattened sysctl name to OID hash. | |
| package main | |
| import ( | |
| "bufio" | |
| "fmt" | |
| "os" | |
| "path/filepath" | |
| "regexp" | |
| "sort" | |
| "strings" | |
| ) | |
| var ( | |
| goos, goarch string | |
| ) | |
| // cmdLine returns this programs's commandline arguments. | |
| func cmdLine() string { | |
| return "go run mksysctl_openbsd.go " + strings.Join(os.Args[1:], " ") | |
| } | |
| // buildTags returns build tags. | |
| func buildTags() string { | |
| return fmt.Sprintf("%s,%s", goarch, goos) | |
| } | |
| // reMatch performs regular expression match and stores the substring slice to value pointed by m. | |
| func reMatch(re *regexp.Regexp, str string, m *[]string) bool { | |
| *m = re.FindStringSubmatch(str) | |
| if *m != nil { | |
| return true | |
| } | |
| return false | |
| } | |
| type nodeElement struct { | |
| n int | |
| t string | |
| pE *map[string]nodeElement | |
| } | |
| var ( | |
| debugEnabled bool | |
| mib map[string]nodeElement | |
| node *map[string]nodeElement | |
| nodeMap map[string]string | |
| sysCtl []string | |
| ) | |
| var ( | |
| ctlNames1RE = regexp.MustCompile(`^#define\s+(CTL_NAMES)\s+{`) | |
| ctlNames2RE = regexp.MustCompile(`^#define\s+(CTL_(.*)_NAMES)\s+{`) | |
| ctlNames3RE = regexp.MustCompile(`^#define\s+((.*)CTL_NAMES)\s+{`) | |
| netInetRE = regexp.MustCompile(`^netinet/`) | |
| netInet6RE = regexp.MustCompile(`^netinet6/`) | |
| netRE = regexp.MustCompile(`^net/`) | |
| bracesRE = regexp.MustCompile(`{.*}`) | |
| ctlTypeRE = regexp.MustCompile(`{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}`) | |
| fsNetKernRE = regexp.MustCompile(`^(fs|net|kern)_`) | |
| ) | |
| func debug(s string) { | |
| if debugEnabled { | |
| fmt.Fprintln(os.Stderr, s) | |
| } | |
| } | |
| // Walk the MIB and build a sysctl name to OID mapping. | |
| func buildSysctl(pNode *map[string]nodeElement, name string, oid []int) { | |
| lNode := pNode // local copy of pointer to node | |
| var keys []string | |
| for k := range *lNode { | |
| keys = append(keys, k) | |
| } | |
| sort.Strings(keys) | |
| for _, key := range keys { | |
| nodename := name | |
| if name != "" { | |
| nodename += "." | |
| } | |
| nodename += key | |
| nodeoid := append(oid, (*pNode)[key].n) | |
| if (*pNode)[key].t == `CTLTYPE_NODE` { | |
| if _, ok := nodeMap[nodename]; ok { | |
| lNode = &mib | |
| ctlName := nodeMap[nodename] | |
| for _, part := range strings.Split(ctlName, ".") { | |
| lNode = ((*lNode)[part]).pE | |
| } | |
| } else { | |
| lNode = (*pNode)[key].pE | |
| } | |
| buildSysctl(lNode, nodename, nodeoid) | |
| } else if (*pNode)[key].t != "" { | |
| oidStr := []string{} | |
| for j := range nodeoid { | |
| oidStr = append(oidStr, fmt.Sprintf("%d", nodeoid[j])) | |
| } | |
| text := "\t{ \"" + nodename + "\", []_C_int{ " + strings.Join(oidStr, ", ") + " } }, \n" | |
| sysCtl = append(sysCtl, text) | |
| } | |
| } | |
| } | |
| func main() { | |
| // Get the OS (using GOOS_TARGET if it exist) | |
| goos = os.Getenv("GOOS_TARGET") | |
| if goos == "" { | |
| goos = os.Getenv("GOOS") | |
| } | |
| // Get the architecture (using GOARCH_TARGET if it exists) | |
| goarch = os.Getenv("GOARCH_TARGET") | |
| if goarch == "" { | |
| goarch = os.Getenv("GOARCH") | |
| } | |
| // Check if GOOS and GOARCH environment variables are defined | |
| if goarch == "" || goos == "" { | |
| fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n") | |
| os.Exit(1) | |
| } | |
| mib = make(map[string]nodeElement) | |
| headers := [...]string{ | |
| `sys/sysctl.h`, | |
| `sys/socket.h`, | |
| `sys/tty.h`, | |
| `sys/malloc.h`, | |
| `sys/mount.h`, | |
| `sys/namei.h`, | |
| `sys/sem.h`, | |
| `sys/shm.h`, | |
| `sys/vmmeter.h`, | |
| `uvm/uvmexp.h`, | |
| `uvm/uvm_param.h`, | |
| `uvm/uvm_swap_encrypt.h`, | |
| `ddb/db_var.h`, | |
| `net/if.h`, | |
| `net/if_pfsync.h`, | |
| `net/pipex.h`, | |
| `netinet/in.h`, | |
| `netinet/icmp_var.h`, | |
| `netinet/igmp_var.h`, | |
| `netinet/ip_ah.h`, | |
| `netinet/ip_carp.h`, | |
| `netinet/ip_divert.h`, | |
| `netinet/ip_esp.h`, | |
| `netinet/ip_ether.h`, | |
| `netinet/ip_gre.h`, | |
| `netinet/ip_ipcomp.h`, | |
| `netinet/ip_ipip.h`, | |
| `netinet/pim_var.h`, | |
| `netinet/tcp_var.h`, | |
| `netinet/udp_var.h`, | |
| `netinet6/in6.h`, | |
| `netinet6/ip6_divert.h`, | |
| `netinet6/pim6_var.h`, | |
| `netinet/icmp6.h`, | |
| `netmpls/mpls.h`, | |
| } | |
| ctls := [...]string{ | |
| `kern`, | |
| `vm`, | |
| `fs`, | |
| `net`, | |
| //debug /* Special handling required */ | |
| `hw`, | |
| //machdep /* Arch specific */ | |
| `user`, | |
| `ddb`, | |
| //vfs /* Special handling required */ | |
| `fs.posix`, | |
| `kern.forkstat`, | |
| `kern.intrcnt`, | |
| `kern.malloc`, | |
| `kern.nchstats`, | |
| `kern.seminfo`, | |
| `kern.shminfo`, | |
| `kern.timecounter`, | |
| `kern.tty`, | |
| `kern.watchdog`, | |
| `net.bpf`, | |
| `net.ifq`, | |
| `net.inet`, | |
| `net.inet.ah`, | |
| `net.inet.carp`, | |
| `net.inet.divert`, | |
| `net.inet.esp`, | |
| `net.inet.etherip`, | |
| `net.inet.gre`, | |
| `net.inet.icmp`, | |
| `net.inet.igmp`, | |
| `net.inet.ip`, | |
| `net.inet.ip.ifq`, | |
| `net.inet.ipcomp`, | |
| `net.inet.ipip`, | |
| `net.inet.mobileip`, | |
| `net.inet.pfsync`, | |
| `net.inet.pim`, | |
| `net.inet.tcp`, | |
| `net.inet.udp`, | |
| `net.inet6`, | |
| `net.inet6.divert`, | |
| `net.inet6.ip6`, | |
| `net.inet6.icmp6`, | |
| `net.inet6.pim6`, | |
| `net.inet6.tcp6`, | |
| `net.inet6.udp6`, | |
| `net.mpls`, | |
| `net.mpls.ifq`, | |
| `net.key`, | |
| `net.pflow`, | |
| `net.pfsync`, | |
| `net.pipex`, | |
| `net.rt`, | |
| `vm.swapencrypt`, | |
| //vfsgenctl /* Special handling required */ | |
| } | |
| // Node name "fixups" | |
| ctlMap := map[string]string{ | |
| "ipproto": "net.inet", | |
| "net.inet.ipproto": "net.inet", | |
| "net.inet6.ipv6proto": "net.inet6", | |
| "net.inet6.ipv6": "net.inet6.ip6", | |
| "net.inet.icmpv6": "net.inet6.icmp6", | |
| "net.inet6.divert6": "net.inet6.divert", | |
| "net.inet6.tcp6": "net.inet.tcp", | |
| "net.inet6.udp6": "net.inet.udp", | |
| "mpls": "net.mpls", | |
| "swpenc": "vm.swapencrypt", | |
| } | |
| // Node mappings | |
| nodeMap = map[string]string{ | |
| "net.inet.ip.ifq": "net.ifq", | |
| "net.inet.pfsync": "net.pfsync", | |
| "net.mpls.ifq": "net.ifq", | |
| } | |
| mCtls := make(map[string]bool) | |
| for _, ctl := range ctls { | |
| mCtls[ctl] = true | |
| } | |
| for _, header := range headers { | |
| debug("Processing " + header) | |
| file, err := os.Open(filepath.Join("/usr/include", header)) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "%v\n", err) | |
| os.Exit(1) | |
| } | |
| s := bufio.NewScanner(file) | |
| for s.Scan() { | |
| var sub []string | |
| if reMatch(ctlNames1RE, s.Text(), &sub) || | |
| reMatch(ctlNames2RE, s.Text(), &sub) || | |
| reMatch(ctlNames3RE, s.Text(), &sub) { | |
| if sub[1] == `CTL_NAMES` { | |
| // Top level. | |
| node = &mib | |
| } else { | |
| // Node. | |
| nodename := strings.ToLower(sub[2]) | |
| ctlName := "" | |
| if reMatch(netInetRE, header, &sub) { | |
| ctlName = "net.inet." + nodename | |
| } else if reMatch(netInet6RE, header, &sub) { | |
| ctlName = "net.inet6." + nodename | |
| } else if reMatch(netRE, header, &sub) { | |
| ctlName = "net." + nodename | |
| } else { | |
| ctlName = nodename | |
| ctlName = fsNetKernRE.ReplaceAllString(ctlName, `$1.`) | |
| } | |
| if val, ok := ctlMap[ctlName]; ok { | |
| ctlName = val | |
| } | |
| if _, ok := mCtls[ctlName]; !ok { | |
| debug("Ignoring " + ctlName + "...") | |
| continue | |
| } | |
| // Walk down from the top of the MIB. | |
| node = &mib | |
| for _, part := range strings.Split(ctlName, ".") { | |
| if _, ok := (*node)[part]; !ok { | |
| debug("Missing node " + part) | |
| (*node)[part] = nodeElement{n: 0, t: "", pE: &map[string]nodeElement{}} | |
| } | |
| node = (*node)[part].pE | |
| } | |
| } | |
| // Populate current node with entries. | |
| i := -1 | |
| for !strings.HasPrefix(s.Text(), "}") { | |
| s.Scan() | |
| if reMatch(bracesRE, s.Text(), &sub) { | |
| i++ | |
| } | |
| if !reMatch(ctlTypeRE, s.Text(), &sub) { | |
| continue | |
| } | |
| (*node)[sub[1]] = nodeElement{n: i, t: sub[2], pE: &map[string]nodeElement{}} | |
| } | |
| } | |
| } | |
| err = s.Err() | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "%v\n", err) | |
| os.Exit(1) | |
| } | |
| file.Close() | |
| } | |
| buildSysctl(&mib, "", []int{}) | |
| sort.Strings(sysCtl) | |
| text := strings.Join(sysCtl, "") | |
| fmt.Printf(srcTemplate, cmdLine(), buildTags(), text) | |
| } | |
| const srcTemplate = `// %s | |
| // Code generated by the command above; DO NOT EDIT. | |
| // +build %s | |
| package unix | |
| type mibentry struct { | |
| ctlname string | |
| ctloid []_C_int | |
| } | |
| var sysctlMib = []mibentry { | |
| %s | |
| } | |
| ` |