Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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システムでは UidGid は10進数の数値ですが、Windowsではセキュリティ識別子 (SID) の文字列形式になります。このコミットにより、Plan 9では /dev/user の内容がこれらのフィールドに設定されるようになります。

ビルドタグ (+build)

Go言語では、ファイルの先頭に +build ディレクティブを記述することで、特定の環境でのみファイルをコンパイルするように制御できます。これはクロスプラットフォーム開発において非常に重要な機能です。例えば、+build plan9 と記述されたファイルはPlan 9環境でのみコンパイルされ、+build !plan9 と記述されたファイルはPlan 9以外の環境でコンパイルされます。

技術的詳細

このコミットは、Plan 9環境における os/user パッケージの Current() 関数の実装に焦点を当てています。

  1. 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 のパースを必要とし、その実装がこのコミットのスコープ外であるためです。
  2. lookup_stubs.go の変更:

    • このファイルは、os/user パッケージのデフォルトのスタブ実装を提供します。
    • +build !cgo,!windows から +build !cgo,!windows,!plan9 へとビルドタグが変更されました。これにより、Plan 9環境ではこのスタブファイルではなく、新しく追加された lookup_plan9.go が使用されるようになります。
  3. user.go の変更:

    • User 構造体のコメントに、Plan 9における Uid, Gid, Username, Name フィールドの挙動に関する説明が追加されました。具体的には、これらのフィールドが /dev/user の内容になることが明記されています。
  4. user_test.go の変更:

    • check() 関数内の runtime.GOOSswitch 文に "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() 関数です。

  1. 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 エラーを返します。
  2. lookup()lookupId() の未実装:

    • これらの関数は、return nil, syscall.EPLAN9 を返します。syscall.EPLAN9 はPlan 9固有のエラーコードで、この機能が実装されていないことを示します。コミットメッセージにもあるように、これらの関数を実装するには /adm/users ファイルをパースする必要があり、その複雑さと必要性がこの時点では不明確であったため、見送られました。
  3. ビルドタグの調整:

    • lookup_stubs.go のビルドタグに !plan9 が追加されたことで、Plan 9環境ではこの汎用スタブファイルがコンパイルされなくなり、代わりに lookup_plan9.go が使用されるようになります。これにより、OS固有の実装が適切に選択されるようになります。
  4. User 構造体コメントの更新:

    • user.goUser 構造体に関するコメントが更新され、Plan 9における Uid, Gid, Username, Name フィールドの具体的な内容が明記されました。これは、異なるOS間での User 構造体のフィールドの意味合いの違いを明確にするための重要なドキュメントの改善です。
  5. テストの調整:

    • user_test.go では、runtime.GOOS"plan9" の場合にもテストが実行されるように check() 関数が更新されました。しかし、Lookup()LookupId() がPlan 9で未実装であるため、これらのテストはPlan 9環境ではスキップされるように修正されています。これにより、未実装の機能に対するテストが失敗するのを防ぎつつ、将来的な実装のためのプレースホルダーが提供されます。

これらの変更により、Goの os/user パッケージはPlan 9環境で現在のユーザー情報を正しく取得できるようになり、GoプログラムのPlan 9における互換性と機能性が向上しました。

関連リンク

参考にした情報源リンク