Skip to content

Commit 4ef7dc1

Browse files
committed
add LookupId, LookupGroupId
Signed-off-by: leongross <[email protected]>
1 parent 1d6e8b1 commit 4ef7dc1

File tree

4 files changed

+129
-2
lines changed

4 files changed

+129
-2
lines changed

GNUmakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ TEST_PACKAGES_FAST = \
335335
unicode \
336336
unicode/utf16 \
337337
unicode/utf8 \
338+
user \
338339
$(nil)
339340

340341
# Assume this will go away before Go2, so only check minor version.

src/os/user/lookup_unix.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ package user
1010
import (
1111
"bufio"
1212
"os"
13+
"strconv"
1314
"strings"
1415
)
1516

17+
const (
18+
userFile = "/etc/passwd"
19+
groupFile = "/etc/group"
20+
)
21+
1622
func lookupUser(username string) (*User, error) {
17-
f, err := os.Open("/etc/passwd")
23+
f, err := os.Open(userFile)
1824
if err != nil {
1925
return nil, err
2026
}
@@ -43,3 +49,72 @@ func lookupUser(username string) (*User, error) {
4349

4450
return nil, UnknownUserError(username)
4551
}
52+
53+
func lookupUserId(uid string) (*User, error) {
54+
f, err := os.Open(userFile)
55+
if err != nil {
56+
return nil, err
57+
}
58+
defer f.Close()
59+
60+
// parse file format <username>:<password>:<uid>:<gid>:<gecos>:<home>:<shell>
61+
lines := bufio.NewScanner(f)
62+
for lines.Scan() {
63+
line := lines.Text()
64+
fragments := strings.Split(line, ":")
65+
66+
if len(fragments) < 7 {
67+
continue
68+
}
69+
70+
if fragments[2] == uid {
71+
return &User{
72+
Uid: fragments[2],
73+
Gid: fragments[3],
74+
Username: fragments[0],
75+
Name: fragments[4],
76+
HomeDir: fragments[5],
77+
}, nil
78+
}
79+
}
80+
81+
id, err := strconv.Atoi(uid)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
return nil, UnknownUserIdError(id)
87+
}
88+
89+
func lookupGroupId(gid string) (*Group, error) {
90+
f, err := os.Open(groupFile)
91+
if err != nil {
92+
return nil, err
93+
}
94+
defer f.Close()
95+
96+
// parse file format group_name:password:GID:user_list
97+
// group_name: the name of the group.
98+
// password: the (encrypted) group password. If this field is empty, no password is needed.
99+
// GID: the numeric group ID.
100+
// user_list: a list of the usernames that are members of this group, separated by commas.
101+
102+
lines := bufio.NewScanner(f)
103+
for lines.Scan() {
104+
line := lines.Text()
105+
fragments := strings.Split(line, ":")
106+
107+
if len(fragments) < 4 {
108+
continue
109+
}
110+
111+
if fragments[2] == gid {
112+
return &Group{
113+
Gid: fragments[2],
114+
Name: fragments[0],
115+
}, nil
116+
}
117+
}
118+
119+
return nil, UnknownGroupIdError(gid)
120+
}

src/os/user/user.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
package user
66

7-
import "errors"
7+
import (
8+
"errors"
9+
"strconv"
10+
)
811

912
// User represents a user account.
1013
type User struct {
@@ -38,6 +41,29 @@ func (e UnknownUserError) Error() string {
3841
return "user: unknown user " + string(e)
3942
}
4043

44+
// Group represents a grouping of users.
45+
//
46+
// On POSIX systems Gid contains a decimal number representing the group ID.
47+
type Group struct {
48+
Gid string // group ID
49+
Name string // group name
50+
}
51+
52+
// UnknownGroupIdError is returned by [LookupGroupId] when
53+
// a group cannot be found.
54+
type UnknownGroupIdError string
55+
56+
func (e UnknownGroupIdError) Error() string {
57+
return "group: unknown groupid " + string(e)
58+
}
59+
60+
// UnknownUserIdError is returned by [LookupId] when a user cannot be found.
61+
type UnknownUserIdError int
62+
63+
func (e UnknownUserIdError) Error() string {
64+
return "user: unknown userid " + strconv.Itoa(int(e))
65+
}
66+
4167
// Current returns the current user.
4268
//
4369
// The first call will cache the current user information.
@@ -55,3 +81,18 @@ func Current() (*User, error) {
5581
func Lookup(username string) (*User, error) {
5682
return lookupUser(username)
5783
}
84+
85+
// LookupId looks up a user by userid. If the user cannot be found, the
86+
// returned error is of type UnknownUserIdError.
87+
func LookupId(uid string) (*User, error) {
88+
if u, err := Current(); err == nil && u.Uid == uid {
89+
return u, err
90+
}
91+
return lookupUserId(uid)
92+
}
93+
94+
// LookupGroupId looks up a group by groupid. If the group cannot be found, the
95+
// returned error is of type [UnknownGroupIdError].
96+
func LookupGroupId(gid string) (*Group, error) {
97+
return lookupGroupId(gid)
98+
}

src/os/user_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ func TestUserLookup(t *testing.T) {
5454
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
5555
t.Fatalf("Lookup(%q) = %v; want %v, got error %v", tc.user.Username, user, tc.user, err)
5656
}
57+
58+
userId, err := LookupId(tc.user.Uid)
59+
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
60+
t.Fatalf("LookupId(%q) = %v; want %v, got error %v", tc.user.Username, userId, tc.user, err)
61+
}
62+
63+
group, err := LookupGroupId(tc.user.Gid)
64+
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
65+
t.Fatalf("LookupGroupId(%q) = %v; want %v, got error %v", tc.user.Gid, group, tc.user.Gid, err)
66+
}
5767
})
5868
}
5969
}

0 commit comments

Comments
 (0)