[インデックス 17383] ファイルの概要
このコミットは、Go言語の標準ライブラリ os/user パッケージに、Plan 9オペレーティングシステム向けのユーザー情報取得機能を追加するものです。具体的には、現在のユーザー情報を取得する Current() 関数がPlan 9で動作するように実装されました。
コミット
commit de8de8912efea014ceda6819ddf8da09b2a0d056
Author: Nicolas Owens <mischief@offblast.org>
Date: Fri Aug 23 21:05:49 2013 -0500
os/user: Current support on Plan 9
Current for Plan 9 is implemented with /dev/user for
Uid/Gid/Username/Name, and $home environment variable for
HomeDir.
Implementing Lookup/LookupId is not done, which would
require parsing /adm/users. It is unclear of how much benefit
this would be.
R=golang-dev
CC=bradfitz, golang-dev, r
https://golang.org/cl/13203043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/de8de8912efea014ceda6819ddf8da09b2a0d056
元コミット内容
このコミットの目的は、Goの os/user パッケージにおいて、Plan 9オペレーティングシステムでの Current() 関数のサポートを実装することです。Plan 9では、ユーザーID (Uid)、グループID (Gid)、ユーザー名 (Username)、および名前 (Name) の取得に /dev/user ファイルが使用され、ホームディレクトリ (HomeDir) の取得には $home 環境変数が使用されます。
Lookup() および LookupId() 関数については、/adm/users ファイルのパースが必要となるため、このコミットでは実装されていません。コミットメッセージでは、これらの機能の実装がどれほどの利益をもたらすか不明確であると述べられています。
変更の背景
Go言語はクロスプラットフォーム対応を重視しており、様々なオペレーティングシステムで動作するように設計されています。os/user パッケージは、実行中のシステムにおけるユーザーアカウント情報を抽象化して提供する役割を担っています。しかし、初期のGo言語の実装では、Plan 9のような特定のニッチなOSに対するユーザー情報取得のサポートが不完全でした。
このコミットは、Plan 9環境でGoプログラムが現在のユーザー情報を取得できるようにすることで、os/user パッケージのクロスプラットフォーム互換性を向上させることを目的としています。特に、Plan 9のユニークなファイルシステムとユーザー管理の仕組みに対応するための具体的な実装が求められていました。
前提知識の解説
Plan 9 From Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムを通じてアクセスするという「すべてはファイルである」という原則を徹底しています。
Plan 9のユーザー管理
Plan 9におけるユーザー管理は、従来のUnix系システムとは異なる特徴を持っています。
/dev/user: この特殊なファイルは、現在のユーザーのユーザー名(またはユーザーID)を格納しています。Goのos/userパッケージが現在のユーザー情報を取得する際に利用されます。/adm/users: このファイルは、システム上の全ユーザーアカウント情報を格納しています。Unixにおける/etc/passwdに相当しますが、そのフォーマットやアクセス方法は異なります。ユーザー名やUID/GIDからユーザー情報を検索するLookup()やLookupId()関数を実装するには、このファイルをパースする必要があります。$home環境変数: ユーザーのホームディレクトリは、通常$home環境変数によって指定されます。
Go言語の os/user パッケージ
os/user パッケージは、Goプログラムが現在のユーザーや指定されたユーザーに関する情報を取得するための機能を提供します。主要な関数は以下の通りです。
user.Current(): 現在のユーザーの情報を*User構造体として返します。user.Lookup(username string): 指定されたユーザー名のユーザー情報を*User構造体として返します。user.LookupId(uid string): 指定されたユーザーIDのユーザー情報を*User構造体として返します。
*User 構造体には、Uid (ユーザーID)、Gid (プライマリグループID)、Username (ユーザー名)、Name (フルネーム)、HomeDir (ホームディレクトリ) などのフィールドが含まれます。これらのフィールドの具体的な内容は、OSによって異なります。例えば、POSIXシステムでは Uid と Gid は10進数の数値ですが、Windowsではセキュリティ識別子 (SID) の文字列形式になります。このコミットにより、Plan 9では /dev/user の内容がこれらのフィールドに設定されるようになります。
ビルドタグ (+build)
Go言語では、ファイルの先頭に +build ディレクティブを記述することで、特定の環境でのみファイルをコンパイルするように制御できます。これはクロスプラットフォーム開発において非常に重要な機能です。例えば、+build plan9 と記述されたファイルはPlan 9環境でのみコンパイルされ、+build !plan9 と記述されたファイルはPlan 9以外の環境でコンパイルされます。
技術的詳細
このコミットは、Plan 9環境における os/user パッケージの Current() 関数の実装に焦点を当てています。
-
lookup_plan9.goの新規追加:- このファイルは、
+build plan9タグを持つため、Plan 9環境でのみコンパイルされます。 userFile定数として"/dev/user"が定義されています。current()関数が実装されており、ioutil.ReadFile(userFile)を使用して/dev/userの内容を読み込みます。- 読み込んだ内容 (
uname) をUser構造体のUid,Gid,Username,Nameフィールドに設定します。 HomeDirフィールドはos.Getenv("home")を呼び出して$home環境変数の値を取得します。lookup()およびlookupId()関数は、Plan 9では実装されておらず、syscall.EPLAN9エラーを返します。これは、これらの機能が/adm/usersのパースを必要とし、その実装がこのコミットのスコープ外であるためです。
- このファイルは、
-
lookup_stubs.goの変更:- このファイルは、
os/userパッケージのデフォルトのスタブ実装を提供します。 +build !cgo,!windowsから+build !cgo,!windows,!plan9へとビルドタグが変更されました。これにより、Plan 9環境ではこのスタブファイルではなく、新しく追加されたlookup_plan9.goが使用されるようになります。
- このファイルは、
-
user.goの変更:User構造体のコメントに、Plan 9におけるUid,Gid,Username,Nameフィールドの挙動に関する説明が追加されました。具体的には、これらのフィールドが/dev/userの内容になることが明記されています。
-
user_test.goの変更:check()関数内のruntime.GOOSのswitch文に"plan9"が追加され、Plan 9環境でもテストがサポートされるようになりました。TestLookup()およびTestLookupId()関数に、runtime.GOOS == "plan9"の場合にテストをスキップするロジックが追加されました。これは、これらの関数がPlan 9ではまだ実装されていないためです。
このコミットは、Plan 9のユーザー情報取得メカニズムに直接対応することで、Goの os/user パッケージのPlan 9サポートを強化しています。特に、/dev/user と $home 環境変数を活用することで、Plan 9の設計思想に沿った形で Current() 関数が機能するようにしています。
コアとなるコードの変更箇所
src/pkg/os/user/lookup_plan9.go (新規追加)
// Copyright 2013 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.
package user
import (
"fmt"
"io/ioutil"
"os"
"syscall"
)
// Partial os/user support on Plan 9.
// Supports Current(), but not Lookup()/LookupId().
// The latter two would require parsing /adm/users.
const (
userFile = "/dev/user"
)
func current() (*User, error) {
ubytes, err := ioutil.ReadFile(userFile)
if err != nil {
return nil, fmt.Errorf("user: %s", err)
}
uname := string(ubytes)
u := &User{
Uid: uname,
Gid: uname,
Username: uname,
Name: uname,
HomeDir: os.Getenv("home"),
}
return u, nil
}
func lookup(username string) (*User, error) {
return nil, syscall.EPLAN9
}
func lookupId(uid string) (*User, error) {
return nil, syscall.EPLAN9
}
src/pkg/os/user/lookup_stubs.go
--- a/src/pkg/os/user/lookup_stubs.go
+++ b/src/pkg/os/user/lookup_stubs.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !cgo,!windows
+// +build !cgo,!windows,!plan9
package user
src/pkg/os/user/user.go
--- a/src/pkg/os/user/user.go
+++ b/src/pkg/os/user/user.go
@@ -16,6 +16,8 @@ var implemented = true // set to false by lookup_stubs.go's init
// On posix systems Uid and Gid contain a decimal number
// representing uid and gid. On windows Uid and Gid
// contain security identifier (SID) in a string format.
+// On Plan 9, Uid, Gid, Username, and Name will be the
+// contents of /dev/user.
type User struct {
Uid string // user id
Gid string // primary group id
src/pkg/os/user/user_test.go
--- a/src/pkg/os/user/user_test.go
+++ b/src/pkg/os/user/user_test.go
@@ -14,7 +14,7 @@ func check(t *testing.T) {
t.Skip("user: not implemented; skipping tests")
}
switch runtime.GOOS {
- case "linux", "freebsd", "darwin", "windows":
+ case "linux", "freebsd", "darwin", "windows", "plan9":
// test supported
default:
t.Skipf("user: Lookup not implemented on %q; skipping test", runtime.GOOS)
@@ -61,6 +61,10 @@ func compare(t *testing.T, want, got *User) {
func TestLookup(t *testing.T) {
check(t)
+ if runtime.GOOS == "plan9" {
+ t.Skipf("Lookup not implemented on %q", runtime.GOOS)
+ }
+
want, err := Current()
if err != nil {
t.Fatalf("Current: %v", err)
@@ -75,6 +79,10 @@ func TestLookupId(t *testing.T) {
func TestLookupId(t *testing.T) {
check(t)
+ if runtime.GOOS == "plan9" {
+ t.Skipf("LookupId not implemented on %q", runtime.GOOS)
+ }
+
want, err := Current()
if err != nil {
t.Fatalf("Current: %v", err)
コアとなるコードの解説
このコミットの核となるのは、src/pkg/os/user/lookup_plan9.go ファイルに実装された current() 関数です。
-
current()関数の実装:ioutil.ReadFile(userFile): Plan 9の特殊ファイル/dev/userから現在のユーザー情報をバイト列として読み込みます。このファイルには通常、現在のユーザー名が格納されています。uname := string(ubytes): 読み込んだバイト列を文字列に変換します。これがユーザー名として扱われます。u := &User{...}:User構造体のインスタンスを作成し、各フィールドに値を設定します。Uid: uname,Gid: uname,Username: uname,Name: uname: Plan 9では、ユーザーID、グループID、ユーザー名、フルネームのすべてに/dev/userから読み込んだunameの値が設定されます。これはPlan 9のユーザー管理の簡素さを反映しています。HomeDir: os.Getenv("home"): ホームディレクトリは、Unix系システムのように/etc/passwdから取得するのではなく、Plan 9の慣習に従い$home環境変数の値から取得されます。
return u, nil: 構築されたUser構造体とnilエラーを返します。
-
lookup()とlookupId()の未実装:- これらの関数は、
return nil, syscall.EPLAN9を返します。syscall.EPLAN9はPlan 9固有のエラーコードで、この機能が実装されていないことを示します。コミットメッセージにもあるように、これらの関数を実装するには/adm/usersファイルをパースする必要があり、その複雑さと必要性がこの時点では不明確であったため、見送られました。
- これらの関数は、
-
ビルドタグの調整:
lookup_stubs.goのビルドタグに!plan9が追加されたことで、Plan 9環境ではこの汎用スタブファイルがコンパイルされなくなり、代わりにlookup_plan9.goが使用されるようになります。これにより、OS固有の実装が適切に選択されるようになります。
-
User構造体コメントの更新:user.goのUser構造体に関するコメントが更新され、Plan 9におけるUid,Gid,Username,Nameフィールドの具体的な内容が明記されました。これは、異なるOS間でのUser構造体のフィールドの意味合いの違いを明確にするための重要なドキュメントの改善です。
-
テストの調整:
user_test.goでは、runtime.GOOSが"plan9"の場合にもテストが実行されるようにcheck()関数が更新されました。しかし、Lookup()とLookupId()がPlan 9で未実装であるため、これらのテストはPlan 9環境ではスキップされるように修正されています。これにより、未実装の機能に対するテストが失敗するのを防ぎつつ、将来的な実装のためのプレースホルダーが提供されます。
これらの変更により、Goの os/user パッケージはPlan 9環境で現在のユーザー情報を正しく取得できるようになり、GoプログラムのPlan 9における互換性と機能性が向上しました。
関連リンク
- Go言語の
os/userパッケージのドキュメント: https://pkg.go.dev/os/user - Plan 9 From Bell Labs 公式サイト: https://9p.io/plan9/
- Go言語のクロスコンパイルとビルドタグに関するドキュメント: https://go.dev/doc/go1.4#go-command (Go 1.4のリリースノートですが、ビルドタグの概念について触れられています)
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go
- Plan 9のファイルシステムに関する情報 (Wikipediaなど): https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs
- Go言語の
syscallパッケージのドキュメント: https://pkg.go.dev/syscall - Go言語の
io/ioutilパッケージのドキュメント: https://pkg.go.dev/io/ioutil (Go 1.16以降はioとosパッケージに統合されていますが、当時のコードでは使用されていました) - Go言語の
osパッケージのドキュメント: https://pkg.go.dev/os - Go言語の
runtimeパッケージのドキュメント: https://pkg.go.dev/runtime - Go言語の
testingパッケージのドキュメント: https://pkg.go.dev/testing