Skip to content

Commit

Permalink
add LookupId, LookupGroupId
Browse files Browse the repository at this point in the history
Signed-off-by: leongross <[email protected]>
  • Loading branch information
leongross committed May 10, 2024
1 parent 344dba8 commit d9ef0e1
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 2 deletions.
77 changes: 76 additions & 1 deletion src/os/user/lookup_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ package user
import (
"bufio"
"os"
"strconv"
"strings"
)

const (
userFile = "/etc/passwd"
groupFile = "/etc/group"
)

func lookupUser(username string) (*User, error) {
f, err := os.Open("/etc/passwd")
f, err := os.Open(userFile)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -43,3 +49,72 @@ func lookupUser(username string) (*User, error) {

return nil, UnknownUserError(username)
}

func lookupUserId(uid string) (*User, error) {
f, err := os.Open(userFile)
if err != nil {
return nil, err
}
defer f.Close()

// parse file format <username>:<password>:<uid>:<gid>:<gecos>:<home>:<shell>
lines := bufio.NewScanner(f)
for lines.Scan() {
line := lines.Text()
fragments := strings.Split(line, ":")

if len(fragments) < 7 {
continue
}

if fragments[2] == uid {
return &User{
Uid: fragments[2],
Gid: fragments[3],
Username: fragments[0],
Name: fragments[4],
HomeDir: fragments[5],
}, nil
}
}

id, err := strconv.Atoi(uid)
if err != nil {
return nil, err
}

return nil, UnknownUserIdError(id)
}

func lookupGroupId(gid string) (*Group, error) {
f, err := os.Open(groupFile)
if err != nil {
return nil, err
}
defer f.Close()

// parse file format group_name:password:GID:user_list
// group_name: the name of the group.
// password: the (encrypted) group password. If this field is empty, no password is needed.
// GID: the numeric group ID.
// user_list: a list of the usernames that are members of this group, separated by commas.

lines := bufio.NewScanner(f)
for lines.Scan() {
line := lines.Text()
fragments := strings.Split(line, ":")

if len(fragments) < 4 {
continue
}

if fragments[2] == gid {
return &Group{
Gid: fragments[2],
Name: fragments[0],
}, nil
}
}

return nil, UnknownGroupIdError(gid)
}
43 changes: 42 additions & 1 deletion src/os/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

package user

import "errors"
import (
"errors"
"strconv"
)

// User represents a user account.
type User struct {
Expand Down Expand Up @@ -38,6 +41,29 @@ func (e UnknownUserError) Error() string {
return "user: unknown user " + string(e)
}

// Group represents a grouping of users.
//
// On POSIX systems Gid contains a decimal number representing the group ID.
type Group struct {
Gid string // group ID
Name string // group name
}

// UnknownGroupIdError is returned by [LookupGroupId] when
// a group cannot be found.
type UnknownGroupIdError string

func (e UnknownGroupIdError) Error() string {
return "group: unknown groupid " + string(e)
}

// UnknownUserIdError is returned by [LookupId] when a user cannot be found.
type UnknownUserIdError int

func (e UnknownUserIdError) Error() string {
return "user: unknown userid " + strconv.Itoa(int(e))
}

// Current returns the current user.
//
// The first call will cache the current user information.
Expand All @@ -55,3 +81,18 @@ func Current() (*User, error) {
func Lookup(username string) (*User, error) {
return lookupUser(username)
}

// LookupId looks up a user by userid. If the user cannot be found, the
// returned error is of type UnknownUserIdError.
func LookupId(uid string) (*User, error) {
if u, err := Current(); err == nil && u.Uid == uid {
return u, err
}
return lookupUserId(uid)
}

// LookupGroupId looks up a group by groupid. If the group cannot be found, the
// returned error is of type [UnknownGroupIdError].
func LookupGroupId(gid string) (*Group, error) {
return lookupGroupId(gid)
}
10 changes: 10 additions & 0 deletions src/os/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ func TestUserLookup(t *testing.T) {
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
t.Fatalf("Lookup(%q) = %v; want %v, got error %v", tc.user.Username, user, tc.user, err)
}

userId, err := LookupId(tc.user.Uid)
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
t.Fatalf("LookupId(%q) = %v; want %v, got error %v", tc.user.Username, userId, tc.user, err)
}

group, err := LookupGroupId(tc.user.Gid)
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
t.Fatalf("LookupGroupId(%q) = %v; want %v, got error %v", tc.user.Gid, group, tc.user.Gid, err)
}
})
}
}

0 comments on commit d9ef0e1

Please sign in to comment.