356 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			356 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // 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 | ||
|  | } | ||
|  | ` |