[インデックス 13116] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおいて、NetBSDシステムコールに対するnametomib()
関数の実装を追加するものです。nametomib()
は、人間が読める形式のシステム制御情報(sysctl)の名前(例: "kern.ostype")を、システムが内部的に使用する整数配列(MIB: Management Information Base)に変換する役割を担います。この機能は、NetBSDのCTL_QUERY
ノード発見メカニズムを利用して実現されています。
コミット
commit 495a9dc2b3ac76004e1324ca38761efad848ad96
Author: Joel Sing <jsing@google.com>
Date: Wed May 23 01:33:48 2012 +1000
syscall: implement nametomib() on netbsd
Implement nametomib() on NetBSD using the CTL_QUERY node discovery
mechanism.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6211071
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/495a9dc2b3ac76004e1324ca38761efad848ad96
元コミット内容
syscall: implement nametomib() on netbsd
Implement nametomib() on NetBSD using the CTL_QUERY node discovery
mechanism.
変更の背景
Go言語のsyscall
パッケージは、オペレーティングシステムが提供する低レベルのシステムコールへのインターフェースを提供します。これにより、GoプログラムからOS固有の機能にアクセスできるようになります。nametomib()
関数は、sysctlインターフェースを通じてシステム情報を取得する際に不可欠な機能です。
NetBSDでは、sysctl変数は階層的な名前空間で管理されており、例えばカーネルのOSタイプはkern.ostype
のように表現されます。しかし、sysctl
システムコール自体は、これらの名前を直接受け取るのではなく、対応する整数値の配列(MIB)を必要とします。他のUnix系OS(例: FreeBSD)では、sysctlbyname()
のような関数がこの名前からMIBへの変換をOS側で提供していますが、NetBSDには直接的な同等の関数がありませんでした。
このコミットの背景には、Goのsyscall
パッケージがNetBSD上でsysctlをより完全にサポートするために、この名前からMIBへの変換ロジックをGo側で実装する必要があったという経緯があります。特に、NetBSDが提供するCTL_QUERY
という特殊なsysctlノードを利用して、名前空間を動的に探索し、対応するMIBを構築するアプローチが採用されました。
前提知識の解説
Sysctl (System Control)
Sysctlは、Unix系オペレーティングシステムにおいて、カーネルの実行時パラメータを照会したり設定したりするためのメカニズムです。これにより、システム管理者は、カーネルの動作を動的に調整したり、システムの状態に関する情報を取得したりできます。
Sysctl変数は通常、ドットで区切られた階層的な名前(例: kern.maxfiles
, net.inet.ip.forwarding
)で識別されます。これらの名前は、内部的には整数値の配列(MIB: Management Information Base)にマッピングされます。例えば、kern.ostype
は{1, 1}
のようなMIBに変換されるかもしれません。
NetBSDのSysctlとCTL_QUERY
NetBSDのsysctlシステムは、他のBSD系OSと多くの共通点がありますが、特定の機能において独自の実装を持っています。このコミットで重要なのは、NetBSDが提供するCTL_QUERY
という特殊なsysctlノードです。
CTL_QUERY
は、特定のMIBパスの下にある利用可能なsysctlノードの情報を動的に取得するために使用されます。これは、名前からMIBへの変換を行う際に、名前空間を探索するために利用できる強力なメカニズムです。具体的には、あるMIBパスにCTL_QUERY
を追加してsysctl
システムコールを呼び出すと、そのパスの直下にあるノードのメタデータ(名前、番号、型など)のリストが返されます。この情報を使って、名前の各コンポーネントに対応するMIB番号を段階的に解決していくことができます。
MIB (Management Information Base)
MIBは、システムやネットワークデバイスの管理情報を階層的に構造化したデータベースのようなものです。sysctlの文脈では、カーネルパラメータやシステム状態を表す整数値の配列を指します。各整数は、階層内の特定のノード(カテゴリや具体的なパラメータ)を識別します。
unsafe.Pointer
と_C_int
Go言語のunsafe
パッケージは、型安全性をバイパスしてメモリを直接操作するための機能を提供します。unsafe.Pointer
は、任意の型のポインタを表現でき、異なる型のポインタ間で変換を行うことができます。これは、C言語の構造体やシステムコールインターフェースとGoのデータ構造をマッピングする際にしばしば使用されます。
_C_int
は、Goのsyscall
パッケージでC言語のint
型に対応するために定義される型です。システムコールは通常、C言語のデータ型を期待するため、Goの型をCの型に正確にマッピングする必要があります。
技術的詳細
このコミットの主要な技術的詳細は、NetBSDのCTL_QUERY
メカニズムを利用してnametomib()
を実装する点にあります。
-
sysctlNodes
関数の導入:- この新しい関数は、与えられたMIBパスの直下にあるsysctlノードのリストを取得します。
- 内部的には、与えられた
mib
配列にCTL_QUERY
を付加し、そのMIBを使ってsysctl
システムコールを呼び出します。 - 最初の呼び出しでは、返されるデータのサイズ(
olen
)を取得し、そのサイズに基づいてSysctlnode
構造体のスライスを割り当てます。 - 2回目の呼び出しで、実際にノードのデータを取得します。
Sysctlnode
構造体は、各sysctlノードのメタデータ(名前、番号、フラグなど)を保持します。
-
nametomib
関数の実装:- 入力されたsysctl名(例: "kern.ostype")をドットで分割し、各コンポーネント("kern", "ostype")を抽出します。
- 空のMIB配列から開始し、名前の各コンポーネントを順に解決していきます。
- 各コンポーネントについて、現在のMIBパス(最初は空、その後は解決済みの部分)に
CTL_QUERY
を付加してsysctlNodes
を呼び出し、そのパスの直下にあるノードのリストを取得します。 - 取得したノードリストをイテレートし、現在のコンポーネント名と一致するノードを探します。
- 一致するノードが見つかった場合、そのノードの番号(
node.Num
)をMIB配列に追加します。 - このプロセスを名前のすべてのコンポーネントに対して繰り返すことで、最終的なMIB配列を構築します。
- もし途中でコンポーネントが見つからなかった場合、
EINVAL
(無効な引数)エラーを返します。
-
mkerrors.sh
の変更:mkerrors.sh
スクリプトは、Goのsyscall
パッケージで使用される定数や構造体を、Cヘッダーファイルから自動生成するために使われます。- このコミットでは、
CTL_MAXNAME
,CTL_NET
,CTL_QUERY
,SYSCTL_VERS
といった新しい定数がGoのコードに正しく取り込まれるように、スクリプトのパターンマッチングが更新されています。 - また、
schedppq
がconst int
から#define
に変更されています。これは、Cヘッダーファイルでの定義方法に合わせた変更で、Goのバインディング生成に影響します。
-
types_netbsd.go
の変更:sys/sysctl.h
ヘッダーがインクルードされるようになりました。これにより、sysctl
関連のC構造体や定数がGoのコードから利用可能になります。Sysctlnode
型がC.struct_sysctlnode
として定義されました。これは、C言語のstruct sysctlnode
に対応するGoの構造体です。
-
zerrors_netbsd_*.go
とztypes_netbsd_*.go
の変更:- これらは
mkerrors.sh
スクリプトによって自動生成されるファイルです。 zerrors_netbsd_386.go
とzerrors_netbsd_amd64.go
には、CTL_QUERY
、SYSCTL_VERSION
、SYSCTL_VERS_0
、SYSCTL_VERS_1
、SYSCTL_VERS_MASK
といった新しい定数が追加されています。ztypes_netbsd_386.go
とztypes_netbsd_amd64.go
には、Sysctlnode
構造体のGoでの定義が追加されています。この構造体は、Flags
,Num
,Name
などのフィールドを持ち、sysctlノードのメタデータを表現します。
- これらは
この実装により、GoプログラムはNetBSD上でsysctl
の名前ベースのクエリを効率的に実行できるようになり、より柔軟なシステム情報の取得が可能になります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、src/pkg/syscall/syscall_netbsd.go
におけるnametomib()
関数の実装と、それに付随するsysctlNodes()
関数の追加です。
// src/pkg/syscall/syscall_netbsd.go
// sysctlNodes retrieves a list of sysctl nodes below the given MIB.
// It uses the CTL_QUERY mechanism to discover nodes.
func sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error) {
var olen uintptr
// Get a list of all sysctl nodes below the given MIB by performing
// a sysctl for the given MIB with CTL_QUERY appended.
mib = append(mib, CTL_QUERY)
qnode := Sysctlnode{Flags: SYSCTL_VERS_1}
qp := (*byte)(unsafe.Pointer(&qnode))
sz := unsafe.Sizeof(qnode)
if err = sysctl(mib, nil, &olen, qp, sz); err != nil {
return nil, err
}
// Now that we know the size, get the actual nodes.
nodes = make([]Sysctlnode, olen/sz)
np := (*byte)(unsafe.Pointer(&nodes[0]))
if err = sysctl(mib, np, &olen, qp, sz); err != nil {
return nil, err
}
return nodes, nil
}
// nametomib converts a sysctl name (e.g., "kern.ostype") to its MIB array.
func nametomib(name string) (mib []_C_int, err error) {
// Split name into components.
var parts []string
last := 0
for i := 0; i < len(name); i++ {
if name[i] == '.' {
parts = append(parts, name[last:i])
last = i + 1
}
}
parts = append(parts, name[last:])
// Discover the nodes and construct the MIB OID.
for partno, part := range parts {
nodes, err := sysctlNodes(mib) // Get nodes for the current MIB path
if err != nil {
return nil, err
}
for _, node := range nodes {
n := make([]byte, 0)
for i := range node.Name {
if node.Name[i] != 0 { // Node names are null-terminated C strings
n = append(n, byte(node.Name[i]))
}
}
if string(n) == part { // Match component name
mib = append(mib, _C_int(node.Num)) // Add node number to MIB
break
}
}
if len(mib) != partno+1 { // If component not found
return nil, EINVAL
}
}
return mib, nil
}
コアとなるコードの解説
sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error)
この関数は、与えられたmib
(Management Information Base)パスの下にあるsysctlノードのリストを取得します。
mib = append(mib, CTL_QUERY)
: 既存のmib
パスの最後にCTL_QUERY
という特別な定数を追加します。NetBSDのsysctl
システムでは、このCTL_QUERY
をパスの最後に指定することで、そのパスの直下にあるノードのメタデータ(名前、番号など)を問い合わせることができます。qnode := Sysctlnode{Flags: SYSCTL_VERS_1}
:Sysctlnode
構造体のインスタンスを作成し、Flags
フィールドにSYSCTL_VERS_1
を設定します。これは、問い合わせるsysctlノードのバージョンを指定するためのものです。qp := (*byte)(unsafe.Pointer(&qnode))
:qnode
のアドレスをunsafe.Pointer
を介して*byte
型にキャストします。これは、sysctl
システムコールがバイトポインタを期待するためです。sz := unsafe.Sizeof(qnode)
:Sysctlnode
構造体のサイズを取得します。if err = sysctl(mib, nil, &olen, qp, sz); err != nil
: 最初のsysctl
呼び出しを行います。mib
:CTL_QUERY
が追加されたMIBパス。nil
: データを格納するバッファ。ここでは、返されるデータのサイズを知るためにnil
を渡します。&olen
: 返されるデータのサイズが格納されるポインタ。qp
: 問い合わせるノードの情報を渡すためのポインタ(ここではqnode
)。sz
:qnode
のサイズ。- この呼び出しにより、
olen
に、指定されたMIBパスの下にあるすべてのノードのメタデータを格納するために必要なバイト数が設定されます。
nodes = make([]Sysctlnode, olen/sz)
:olen
とsz
を使って、取得するSysctlnode
の数に合わせたスライスをnodes
として作成します。np := (*byte)(unsafe.Pointer(&nodes[0]))
:nodes
スライスの最初の要素のアドレスを*byte
型にキャストします。if err = sysctl(mib, np, &olen, qp, sz); err != nil
: 2回目のsysctl
呼び出しを行います。- 今回は、
np
にnodes
スライスのバッファを渡し、実際のノードデータを取得します。 - これにより、
nodes
スライスに、指定されたMIBパスの直下にあるすべてのsysctlノードのメタデータが格納されます。
- 今回は、
nametomib(name string) (mib []_C_int, err error)
この関数は、人間が読めるsysctl名(例: "kern.ostype")を、システムが使用するMIB配列に変換します。
parts := []string
: 入力されたname
をドット(.
)で分割し、各コンポーネント(例: "kern", "ostype")をparts
スライスに格納します。for partno, part := range parts
: 分割された各コンポーネントについてループします。nodes, err := sysctlNodes(mib)
: 現在までに解決されたMIBパス(最初は空、その後は部分的に解決されたMIB)を使ってsysctlNodes
を呼び出し、そのパスの直下にあるノードのリストを取得します。for _, node := range nodes
: 取得した各node
についてループします。n := make([]byte, 0)
...if string(n) == part
:node.Name
はC言語のヌル終端文字列として格納されているため、それをGoの文字列に変換し、現在のpart
(コンポーネント名)と比較します。mib = append(mib, _C_int(node.Num))
: もし名前が一致するノードが見つかった場合、そのノードの番号(node.Num
)を現在のmib
配列に追加します。これにより、MIBパスが1段階深くなります。break
: 一致するノードが見つかったら、現在のコンポーネントの探索を終了し、次のコンポーネントに移ります。if len(mib) != partno+1
: 各ループの終わりに、mib
の長さが現在のpartno
(0から始まるインデックス)に1を加えたものと等しいかを確認します。これは、現在のコンポーネントに対応するノードがmib
に追加されたことを意味します。もし長さが一致しない場合(つまり、現在のコンポーネントが見つからなかった場合)、EINVAL
エラーを返します。- すべてのコンポーネントが正常に解決されると、最終的な
mib
配列が返されます。
この二つの関数が連携することで、GoプログラムはNetBSDのsysctl名前空間を動的に探索し、指定された名前のsysctl変数に対応するMIBを正確に取得できるようになります。
関連リンク
- Go CL 6211071: https://golang.org/cl/6211071
- NetBSD sysctl(3) man page: https://man.netbsd.org/sysctl.3 (一般的なsysctlの概念とNetBSDでの使用法について)
参考にした情報源リンク
- NetBSDの
sysctl(3)
マニュアルページ:CTL_QUERY
の動作とsysctlnode
構造体に関する詳細な情報を提供しています。 - Go言語の
syscall
パッケージのドキュメントとソースコード: GoがどのようにOSのシステムコールをラップしているか、特に異なるOS間での実装の違いを理解する上で参照しました。 - BSD系OSのsysctlに関する一般的なドキュメントや記事:
nametomib
のような機能の必要性や、名前からMIBへの変換の一般的なアプローチを理解するのに役立ちました。
[インデックス 13116] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおいて、NetBSDシステムコールに対するnametomib()
関数の実装を追加するものです。nametomib()
は、人間が読める形式のシステム制御情報(sysctl)の名前(例: "kern.ostype")を、システムが内部的に使用する整数配列(MIB: Management Information Base)に変換する役割を担います。この機能は、NetBSDのCTL_QUERY
ノード発見メカニズムを利用して実現されています。
コミット
commit 495a9dc2b3ac76004e1324ca38761efad848ad96
Author: Joel Sing <jsing@google.com>
Date: Wed May 23 01:33:48 2012 +1000
syscall: implement nametomib() on netbsd
Implement nametomib() on NetBSD using the CTL_QUERY node discovery
mechanism.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6211071
GitHub上でのコミットページへのリンク
https://github.com/golang.org/go/commit/495a9dc2b3ac76004e1324ca38761efad848ad96
元コミット内容
syscall: implement nametomib() on netbsd
Implement nametomib() on NetBSD using the CTL_QUERY node discovery
mechanism.
変更の背景
Go言語のsyscall
パッケージは、オペレーティングシステムが提供する低レベルのシステムコールへのインターフェースを提供します。これにより、GoプログラムからOS固有の機能にアクセスできるようになります。nametomib()
関数は、sysctlインターフェースを通じてシステム情報を取得する際に不可欠な機能です。
NetBSDでは、sysctl変数は階層的な名前空間で管理されており、例えばカーネルのOSタイプはkern.ostype
のように表現されます。しかし、sysctl
システムコール自体は、これらの名前を直接受け取るのではなく、対応する整数値の配列(MIB)を必要とします。他のUnix系OS(例: FreeBSD)では、sysctlbyname()
のような関数がこの名前からMIBへの変換をOS側で提供していますが、NetBSDには直接的な同等の関数がありませんでした。
このコミットの背景には、Goのsyscall
パッケージがNetBSD上でsysctlをより完全にサポートするために、この名前からMIBへの変換ロジックをGo側で実装する必要があったという経緯があります。特に、NetBSDが提供するCTL_QUERY
という特殊なsysctlノードを利用して、名前空間を動的に探索し、対応するMIBを構築するアプローチが採用されました。
前提知識の解説
Sysctl (System Control)
Sysctlは、Unix系オペレーティングシステムにおいて、カーネルの実行時パラメータを照会したり設定したりするためのメカニズムです。これにより、システム管理者は、カーネルの動作を動的に調整したり、システムの状態に関する情報を取得したりできます。
Sysctl変数は通常、ドットで区切られた階層的な名前(例: kern.maxfiles
, net.inet.ip.forwarding
)で識別されます。これらの名前は、内部的には整数値の配列(MIB: Management Information Base)にマッピングされます。各整数は、階層内の特定のノード(カテゴリや具体的なパラメータ)を識別します。
NetBSDのSysctlとCTL_QUERY
NetBSDのsysctlシステムは、他のBSD系OSと多くの共通点がありますが、特定の機能において独自の実装を持っています。このコミットで重要なのは、NetBSDが提供するCTL_QUERY
という特殊なsysctlノードです。
CTL_QUERY
は、特定のMIBパスの下にある利用可能なsysctlノードの情報を動的に取得するために使用されます。これは、名前からMIBへの変換を行う際に、名前空間を探索するために利用できる強力なメカニズムです。具体的には、あるMIBパスにCTL_QUERY
を追加してsysctl
システムコールを呼び出すと、そのパスの直下にあるノードのメタデータ(名前、番号、型など)のリストが返されます。この情報を使って、名前の各コンポーネントに対応するMIB番号を段階的に解決していくことができます。CTL_QUERY
は通常、<sys/sysctl.h>
で-2
として定義されるプリプロセッサマクロです。
MIB (Management Information Base)
MIBは、システムやネットワークデバイスの管理情報を階層的に構造化したデータベースのようなものです。sysctlの文脈では、カーネルパラメータやシステム状態を表す整数値の配列を指します。各整数は、階層内の特定のノード(カテゴリや具体的なパラメータ)を識別します。
unsafe.Pointer
と_C_int
Go言語のunsafe
パッケージは、型安全性をバイパスしてメモリを直接操作するための機能を提供します。unsafe.Pointer
は、任意の型のポインタを表現でき、異なる型のポインタ間で変換を行うことができます。これは、C言語の構造体やシステムコールインターフェースとGoのデータ構造をマッピングする際にしばしば使用されます。
_C_int
は、Goのsyscall
パッケージでC言語のint
型に対応するために定義される型です。システムコールは通常、C言語のデータ型を期待するため、Goの型をCの型に正確にマッピングする必要があります。
技術的詳細
このコミットの主要な技術的詳細は、NetBSDのCTL_QUERY
メカニズムを利用してnametomib()
を実装する点にあります。
-
sysctlNodes
関数の導入:- この新しい関数は、与えられたMIBパスの直下にあるsysctlノードのリストを取得します。
- 内部的には、与えられた
mib
配列にCTL_QUERY
を付加し、そのMIBを使ってsysctl
システムコールを呼び出します。 - 最初の呼び出しでは、返されるデータのサイズ(
olen
)を取得し、そのサイズに基づいてSysctlnode
構造体のスライスを割り当てます。 - 2回目の呼び出しで、実際にノードのデータを取得します。
Sysctlnode
構造体は、各sysctlノードのメタデータ(名前、番号、フラグなど)を保持します。CTL_QUERY
や他の動的な操作を使用する際には、プログラムのバージョンを示すためにSYSCTL_VERSION
を明示的に指定することが重要です。
-
nametomib
関数の実装:- 入力されたsysctl名(例: "kern.ostype")をドットで分割し、各コンポーネント("kern", "ostype")を抽出します。
- 空のMIB配列から開始し、名前の各コンポーネントを順に解決していきます。
- 各コンポーネントについて、現在のMIBパス(最初は空、その後は解決済みの部分)に
CTL_QUERY
を付加してsysctlNodes
を呼び出し、そのパスの直下にあるノードのリストを取得します。 - 取得したノードリストをイテレートし、現在のコンポーネント名と一致するノードを探します。
- 一致するノードが見つかった場合、そのノードの番号(
node.Num
)をMIB配列に追加します。 - このプロセスを名前のすべてのコンポーネントに対して繰り返すことで、最終的なMIB配列を構築します。
- もし途中でコンポーネントが見つからなかった場合、
EINVAL
(無効な引数)エラーを返します。
-
mkerrors.sh
の変更:mkerrors.sh
スクリプトは、Goのsyscall
パッケージで使用される定数や構造体を、Cヘッダーファイルから自動生成するために使われます。- このコミットでは、
CTL_MAXNAME
,CTL_NET
,CTL_QUERY
,SYSCTL_VERS
といった新しい定数がGoのコードに正しく取り込まれるように、スクリプトのパターンマッチングが更新されています。 - また、
schedppq
がconst int
から#define
に変更されています。これは、Cヘッダーファイルでの定義方法に合わせた変更で、Goのバインディング生成に影響します。
-
types_netbsd.go
の変更:sys/sysctl.h
ヘッダーがインクルードされるようになりました。これにより、sysctl
関連のC構造体や定数がGoのコードから利用可能になります。Sysctlnode
型がC.struct_sysctlnode
として定義されました。これは、C言語のstruct sysctlnode
に対応するGoの構造体です。
-
zerrors_netbsd_*.go
とztypes_netbsd_*.go
の変更:- これらは
mkerrors.sh
スクリプトによって自動生成されるファイルです。 zerrors_netbsd_386.go
とzerrors_netbsd_amd64.go
には、CTL_QUERY
、SYSCTL_VERSION
、SYSCTL_VERS_0
、SYSCTL_VERS_1
、SYSCTL_VERS_MASK
といった新しい定数が追加されています。ztypes_netbsd_386.go
とztypes_netbsd_amd64.go
には、Sysctlnode
構造体のGoでの定義が追加されています。この構造体は、Flags
,Num
,Name
などのフィールドを持ち、sysctlノードのメタデータを表現します。
- これらは
この実装により、GoプログラムはNetBSD上でsysctl
の名前ベースのクエリを効率的に実行できるようになり、より柔軟なシステム情報の取得が可能になります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、src/pkg/syscall/syscall_netbsd.go
におけるnametomib()
関数の実装と、それに付随するsysctlNodes()
関数の追加です。
// src/pkg/syscall/syscall_netbsd.go
// sysctlNodes retrieves a list of sysctl nodes below the given MIB.
// It uses the CTL_QUERY mechanism to discover nodes.
func sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error) {
var olen uintptr
// Get a list of all sysctl nodes below the given MIB by performing
// a sysctl for the given MIB with CTL_QUERY appended.
mib = append(mib, CTL_QUERY)
qnode := Sysctlnode{Flags: SYSCTL_VERS_1}
qp := (*byte)(unsafe.Pointer(&qnode))
sz := unsafe.Sizeof(qnode)
if err = sysctl(mib, nil, &olen, qp, sz); err != nil {
return nil, err
}
// Now that we know the size, get the actual nodes.
nodes = make([]Sysctlnode, olen/sz)
np := (*byte)(unsafe.Pointer(&nodes[0]))
if err = sysctl(mib, np, &olen, qp, sz); err != nil {
return nil, err
}
return nodes, nil
}
// nametomib converts a sysctl name (e.g., "kern.ostype") to its MIB array.
func nametomib(name string) (mib []_C_int, err error) {
// Split name into components.
var parts []string
last := 0
for i := 0; i < len(name); i++ {
if name[i] == '.' {
parts = append(parts, name[last:i])
last = i + 1
}
}
parts = append(parts, name[last:])
// Discover the nodes and construct the MIB OID.
for partno, part := range parts {
nodes, err := sysctlNodes(mib) // Get nodes for the current MIB path
if err != nil {
return nil, err
}
for _, node := range nodes {
n := make([]byte, 0)
for i := range node.Name {
if node.Name[i] != 0 { // Node names are null-terminated C strings
n = append(n, byte(node.Name[i]))
}
}
if string(n) == part { // Match component name
mib = append(mib, _C_int(node.Num)) // Add node number to MIB
break
}
}
if len(mib) != partno+1 { // If component not found
return nil, EINVAL
}
}
return mib, nil
}
コアとなるコードの解説
sysctlNodes(mib []_C_int) (nodes []Sysctlnode, err error)
この関数は、与えられたmib
(Management Information Base)パスの下にあるsysctlノードのリストを取得します。
mib = append(mib, CTL_QUERY)
: 既存のmib
パスの最後にCTL_QUERY
という特別な定数を追加します。NetBSDのsysctl
システムでは、このCTL_QUERY
をパスの最後に指定することで、そのパスの直下にあるノードのメタデータ(名前、番号など)を問い合わせることができます。qnode := Sysctlnode{Flags: SYSCTL_VERS_1}
:Sysctlnode
構造体のインスタンスを作成し、Flags
フィールドにSYSCTL_VERS_1
を設定します。これは、問い合わせるsysctlノードのバージョンを指定するためのものです。qp := (*byte)(unsafe.Pointer(&qnode))
:qnode
のアドレスをunsafe.Pointer
を介して*byte
型にキャストします。これは、sysctl
システムコールがバイトポインタを期待するためです。sz := unsafe.Sizeof(qnode)
:Sysctlnode
構造体のサイズを取得します。if err = sysctl(mib, nil, &olen, qp, sz); err != nil
: 最初のsysctl
呼び出しを行います。mib
:CTL_QUERY
が追加されたMIBパス。nil
: データを格納するバッファ。ここでは、返されるデータのサイズを知るためにnil
を渡します。&olen
: 返されるデータのサイズが格納されるポインタ。qp
: 問い合わせるノードの情報を渡すためのポインタ(ここではqnode
)。sz
:qnode
のサイズ。- この呼び出しにより、
olen
に、指定されたMIBパスの下にあるすべてのノードのメタデータを格納するために必要なバイト数が設定されます。
nodes = make([]Sysctlnode, olen/sz)
:olen
とsz
を使って、取得するSysctlnode
の数に合わせたスライスをnodes
として作成します。np := (*byte)(unsafe.Pointer(&nodes[0]))
:nodes
スライスの最初の要素のアドレスを*byte
型にキャストします。if err = sysctl(mib, np, &olen, qp, sz); err != nil
: 2回目のsysctl
呼び出しを行います。- 今回は、
np
にnodes
スライスのバッファを渡し、実際のノードデータを取得します。 - これにより、
nodes
スライスに、指定されたMIBパスの直下にあるすべてのsysctlノードのメタデータが格納されます。
- 今回は、
nametomib(name string) (mib []_C_int, err error)
この関数は、人間が読めるsysctl名(例: "kern.ostype")を、システムが使用するMIB配列に変換します。
parts := []string
: 入力されたname
をドット(.
)で分割し、各コンポーネント(例: "kern", "ostype")をparts
スライスに格納します。for partno, part := range parts
: 分割された各コンポーネントについてループします。nodes, err := sysctlNodes(mib)
: 現在までに解決されたMIBパス(最初は空、その後は部分的に解決されたMIB)を使ってsysctlNodes
を呼び出し、そのパスの直下にあるノードのリストを取得します。for _, node := range nodes
: 取得した各node
についてループします。n := make([]byte, 0)
...if string(n) == part
:node.Name
はC言語のヌル終端文字列として格納されているため、それをGoの文字列に変換し、現在のpart
(コンポーネント名)と比較します。mib = append(mib, _C_int(node.Num))
: もし名前が一致するノードが見つかった場合、そのノードの番号(node.Num
)を現在のmib
配列に追加します。これにより、MIBパスが1段階深くなります。break
: 一致するノードが見つかったら、現在のコンポーネントの探索を終了し、次のコンポーネントに移ります。if len(mib) != partno+1
: 各ループの終わりに、mib
の長さが現在のpartno
(0から始まるインデックス)に1を加えたものと等しいかを確認します。これは、現在のコンポーネントに対応するノードがmib
に追加されたことを意味します。もし長さが一致しない場合(つまり、現在のコンポーネントが見つからなかった場合)、EINVAL
エラーを返します。- すべてのコンポーネントが正常に解決されると、最終的な
mib
配列が返されます。
この二つの関数が連携することで、GoプログラムはNetBSDのsysctl名前空間を動的に探索し、指定された名前のsysctl変数に対応するMIBを正確に取得できるようになります。
関連リンク
- Go CL 6211071: https://golang.org/cl/6211071
- NetBSD sysctl(3) man page: https://man.netbsd.org/sysctl.3 (一般的なsysctlの概念とNetBSDでの使用法について)
参考にした情報源リンク
- NetBSDの
sysctl(3)
マニュアルページ:CTL_QUERY
の動作とsysctlnode
構造体に関する詳細な情報を提供しています。 - Go言語の
syscall
パッケージのドキュメントとソースコード: GoがどのようにOSのシステムコールをラップしているか、特に異なるOS間での実装の違いを理解する上で参照しました。 - BSD系OSのsysctlに関する一般的なドキュメントや記事:
nametomib
のような機能の必要性や、名前からMIBへの変換の一般的なアプローチを理解するのに役立ちました。