1package internal
2
3import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "slices"
8 "strconv"
9)
10
11// PasswdEntry holds information about the user, such as their username, home directory, or shell.
12type PasswdEntry struct {
13 Username string
14 Password string
15 UID int
16 GID int
17 GECOS string
18 Home string
19 Shell string
20}
21
22// A list of passwd entries, mirroring what you would find in /etc/passwd.
23type Passwd []PasswdEntry
24
25func (passwd *PasswdEntry) UnmarshalText(text []byte) error {
26 line := bytes.Split(text, []byte{':'})
27 passwd.Username = string(line[0])
28 passwd.Password = string(line[1])
29 uid, err := strconv.Atoi(string(line[2]))
30 if err != nil {
31 return fmt.Errorf("failed to convert UID to int: %v", err)
32 }
33 gid, err := strconv.Atoi(string(line[3]))
34 if err != nil {
35 return fmt.Errorf("failed to convert GID to int: %v", err)
36 }
37 passwd.UID = uid
38 passwd.GID = gid
39 passwd.GECOS = string(line[4])
40 passwd.Home = string(line[5])
41 passwd.Shell = string(line[6])
42 return nil
43}
44
45func (passwd *Passwd) UnmarshalText(text []byte) error {
46 var b bytes.Buffer
47 if _, err := b.Write(text); err != nil {
48 return fmt.Errorf("failed to write passwd file to buffer: %v", err)
49 }
50 scanner := bufio.NewScanner(&b)
51 line := 1
52 for scanner.Scan() {
53 var entry PasswdEntry
54 if err := entry.UnmarshalText(scanner.Bytes()); err != nil {
55 return fmt.Errorf("failed to parse passwd file, line %d: %v", line, err)
56 }
57 *passwd = append(*passwd, entry)
58 line++
59 }
60 return nil
61}
62
63// Get an entry from a list of passwd entries.
64func (passwd Passwd) Entry(user string) (PasswdEntry, error) {
65 if len(passwd) == 0 {
66 return PasswdEntry{}, fmt.Errorf("passwd has no entries")
67 }
68 i := slices.IndexFunc(passwd, func(pwent PasswdEntry) bool {
69 return pwent.Username == user
70 })
71 if i == -1 {
72 return PasswdEntry{}, fmt.Errorf("failed to find user %s", user)
73 }
74 return passwd[i], nil
75}