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  }