github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/id/id.go (about) 1 // Copyright 2017-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !plan9 6 // +build !plan9 7 8 // id displays the user id, group id, and groups of the calling process. 9 // 10 // Synopsis: 11 // 12 // id [-gGnu] 13 // 14 // Description: 15 // 16 // id displays the uid, gid and groups of the calling process 17 // 18 // Options: 19 // 20 // -g, --group print only the effective group ID 21 // -G, --groups print all group IDs 22 // -n, --name print a name instead of a number, for -ugG 23 // -u, --user print only the effective user ID 24 // -r, --user print real ID instead of effective ID 25 package main 26 27 import ( 28 "errors" 29 "flag" 30 "fmt" 31 "io" 32 "log" 33 "os" 34 "strconv" 35 "strings" 36 "syscall" 37 ) 38 39 type flags struct { 40 group bool 41 groups bool 42 name bool 43 user bool 44 real bool 45 } 46 47 var ( 48 errOnlyOneChoice = errors.New("id: cannot print \"only\" of more than one choice") 49 errNotOnlyNames = errors.New("id: cannot print only names in default format") 50 errNotOnlyNamesOrIDs = errors.New("id: cannot print only names or real IDs in default format") 51 ) 52 53 func correctFlags(flags ...bool) bool { 54 n := 0 55 for _, v := range flags { 56 if v { 57 n++ 58 } 59 } 60 return !(n > 1) 61 } 62 63 // User contains user information, as from /etc/passwd 64 type User struct { 65 name string 66 uid int 67 gid int 68 groups map[int]string 69 } 70 71 // UID returns the integer UID for a user 72 func (u *User) UID() int { 73 return u.uid 74 } 75 76 // GID returns the integer GID for a user 77 func (u *User) GID() int { 78 return u.gid 79 } 80 81 // Name returns the name for a user 82 func (u *User) Name() string { 83 return u.name 84 } 85 86 // Groups returns all the groups for a user in a map 87 func (u *User) Groups() map[int]string { 88 return u.groups 89 } 90 91 // GIDName returns the group name for a user's UID 92 func (u *User) GIDName() string { 93 val := u.Groups()[u.UID()] 94 return val 95 } 96 97 // NewUser is a factory method for the User type. 98 func NewUser(flags *flags, username string, users *Users, groups *Groups) (*User, error) { 99 var groupsNumbers []int 100 101 u := &User{groups: make(map[int]string)} 102 if len(username) == 0 { // no username provided, get current 103 if flags.real { 104 u.uid = syscall.Geteuid() 105 u.gid = syscall.Getegid() 106 } else { 107 u.uid = syscall.Getuid() 108 u.gid = syscall.Getgid() 109 } 110 groupsNumbers, _ = syscall.Getgroups() 111 if v, err := users.GetUser(u.uid); err == nil { 112 u.name = v 113 } else { 114 u.name = strconv.Itoa(u.uid) 115 } 116 } else { 117 if v, err := users.GetUID(username); err == nil { // user is username 118 u.name = username 119 u.uid = v 120 } else { 121 if uid, err := strconv.Atoi(username); err == nil { // user is valid int 122 if v, err := users.GetUser(uid); err == nil { // user is valid uid 123 u.name = v 124 u.uid = uid 125 } 126 } else { 127 return nil, fmt.Errorf("id: no such user or uid: %s", username) 128 } 129 } 130 u.gid, _ = users.GetGID(u.uid) 131 groupsNumbers = append([]int{u.gid}, groups.UserGetGIDs(u.name)...) 132 // FIXME: not yet implemented group listing lookups 133 } 134 135 for _, groupNum := range groupsNumbers { 136 if groupName, err := groups.GetGroup(groupNum); err == nil { 137 u.groups[groupNum] = groupName 138 } else { 139 u.groups[groupNum] = strconv.Itoa(groupNum) 140 } 141 } 142 143 return u, nil 144 } 145 146 // IDCommand runs the "id" with the current user's information. 147 func IDCommand(w io.Writer, flags *flags, u User) { 148 if !flags.groups { 149 if flags.user { 150 if flags.name { 151 fmt.Fprintln(w, u.Name()) 152 return 153 } 154 fmt.Fprintln(w, u.UID()) 155 return 156 } else if flags.group { 157 if flags.name { 158 fmt.Fprintln(w, u.GIDName()) 159 return 160 } 161 fmt.Fprintln(w, u.GID()) 162 return 163 } 164 165 fmt.Printf("uid=%d(%s) ", u.UID(), u.Name()) 166 fmt.Printf("gid=%d(%s) ", u.GID(), u.GIDName()) 167 } 168 169 if !flags.groups { 170 fmt.Print("groups=") 171 } 172 173 var groupOutput []string 174 175 for gid, name := range u.Groups() { 176 if !flags.groups { 177 groupOutput = append(groupOutput, fmt.Sprintf("%d(%s)", gid, name)) 178 } else { 179 if flags.name { 180 groupOutput = append(groupOutput, fmt.Sprintf("%s ", name)) 181 } else { 182 groupOutput = append(groupOutput, fmt.Sprintf("%d ", gid)) 183 } 184 } 185 } 186 187 sep := "," 188 if flags.groups { 189 sep = "" 190 } 191 192 fmt.Fprintln(w, strings.Join(groupOutput, sep)) 193 } 194 195 func run(w io.Writer, name string, f *flags, passwd, group string) error { 196 if !correctFlags(f.groups, f.group, f.user) { 197 return errOnlyOneChoice 198 } 199 if f.name && !(f.groups || f.group || f.user) { 200 return errNotOnlyNames 201 } 202 if len(name) != 0 && f.real { 203 return errNotOnlyNamesOrIDs 204 } 205 206 users, err := NewUsers(passwd) 207 if err != nil { 208 return fmt.Errorf("id: %w", err) 209 } 210 groups, err := NewGroups(group) 211 if err != nil { 212 return fmt.Errorf("id: %w", err) 213 } 214 215 user, err := NewUser(f, name, users, groups) 216 if err != nil { 217 return fmt.Errorf("id: %w", err) 218 } 219 220 IDCommand(w, f, *user) 221 return nil 222 } 223 224 func main() { 225 const ( 226 GroupFile = "/etc/group" 227 PasswdFile = "/etc/passwd" 228 ) 229 var flags = &flags{} 230 flag.BoolVar(&flags.group, "g", false, "print only the effective group ID") 231 flag.BoolVar(&flags.groups, "G", false, "print all group IDs") 232 flag.BoolVar(&flags.name, "n", false, "print a name instead of a number, for -ugG") 233 flag.BoolVar(&flags.user, "u", false, "print only the effective user ID") 234 flag.BoolVar(&flags.real, "r", false, "print real ID instead of effective ID") 235 236 flag.Parse() 237 if err := run(os.Stdout, flag.Arg(0), flags, PasswdFile, GroupFile); err != nil { 238 log.Fatalf("%v", err) 239 } 240 241 }