gopkg.in/hugelgupf/u-root.v2@v2.0.0-20180831055005-3f8fdb0ce09d/cmds/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  // Synopsis:
     6  //      id [-gGnu]
     7  //
     8  // Description:
     9  //      id displays the uid, guid and groups of the calling process
    10  // Options:
    11  //  		-g, --group     print only the effective group ID
    12  //		  -G, --groups    print all group IDs
    13  //		  -n, --name      print a name instead of a number, for -ugG
    14  //		  -u, --user      print only the effective user ID
    15  package main
    16  
    17  import (
    18  	"bufio"
    19  	"flag"
    20  	"fmt"
    21  	"log"
    22  	"os"
    23  	"strconv"
    24  	"strings"
    25  	"syscall"
    26  )
    27  
    28  var (
    29  	GroupFile  = "/etc/group"
    30  	PasswdFile = "/etc/passwd"
    31  
    32  	flags struct {
    33  		group  bool
    34  		groups bool
    35  		name   bool
    36  		user   bool
    37  	}
    38  )
    39  
    40  func correctFlags(flags ...bool) bool {
    41  	n := 0
    42  	for _, v := range flags {
    43  		if v {
    44  			n++
    45  		}
    46  	}
    47  	return !(n > 1)
    48  }
    49  
    50  func init() {
    51  	flag.BoolVar(&flags.group, "g", false, "print only the effective group ID")
    52  	flag.BoolVar(&flags.groups, "G", false, "print all group IDs")
    53  	flag.BoolVar(&flags.name, "n", false, "print a name instead of a number, for -ugG")
    54  	flag.BoolVar(&flags.user, "u", false, "print only the effective user ID")
    55  }
    56  
    57  type User struct {
    58  	name   string
    59  	uid    int
    60  	euid   int
    61  	gid    int
    62  	groups map[int]string
    63  }
    64  
    65  // readGroups reads the GroupFile for groups.
    66  // It assumes the format "name:passwd:number:groupList".
    67  func readGroups() (map[int]string, error) {
    68  	groupFile, err := os.Open(GroupFile)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	var groupInfo []string
    74  
    75  	groupsMap := make(map[int]string)
    76  	groupScanner := bufio.NewScanner(groupFile)
    77  
    78  	for groupScanner.Scan() {
    79  		groupInfo = strings.Split(groupScanner.Text(), ":")
    80  		groupNum, err := strconv.Atoi(groupInfo[2])
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  
    85  		groupsMap[groupNum] = groupInfo[0]
    86  	}
    87  
    88  	return groupsMap, nil
    89  }
    90  
    91  func (u *User) UID() int {
    92  	return u.uid
    93  }
    94  
    95  func (u *User) GetEuid() int {
    96  	return u.euid
    97  }
    98  
    99  func (u *User) GID() int {
   100  	return u.gid
   101  }
   102  
   103  func (u *User) Name() string {
   104  	return u.name
   105  }
   106  
   107  func (u *User) Groups() map[int]string {
   108  	return u.groups
   109  }
   110  
   111  func (u *User) GIDName() string {
   112  	val := u.Groups()[u.UID()]
   113  	return val
   114  }
   115  
   116  // NewUser is a factory method for the User type.
   117  func NewUser() (*User, error) {
   118  	u := &User{"", syscall.Getuid(), syscall.Geteuid(), syscall.Getgid(), make(map[int]string)}
   119  
   120  	groupsNumbers, err := syscall.Getgroups()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	// Read from groupFile for names and numbers
   126  	groupsMap, err := readGroups()
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	for _, groupNum := range groupsNumbers {
   132  		if groupName, ok := groupsMap[groupNum]; ok {
   133  			u.groups[groupNum] = groupName
   134  		} else {
   135  			return nil, fmt.Errorf("Inconsistent %s file", GroupFile)
   136  		}
   137  	}
   138  
   139  	passwdFile, err := os.Open(PasswdFile)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	// Read from passwdFile for the users name
   145  	var passwdInfo []string
   146  
   147  	passwdScanner := bufio.NewScanner(passwdFile)
   148  
   149  	for passwdScanner.Scan() {
   150  		passwdInfo = strings.Split(passwdScanner.Text(), ":")
   151  		if val, err := strconv.Atoi(passwdInfo[2]); err != nil {
   152  			return nil, err
   153  		} else if val == u.UID() {
   154  			u.name = passwdInfo[0]
   155  			break
   156  		}
   157  	}
   158  
   159  	if u.name == "" {
   160  		return nil, fmt.Errorf("User is not in %s", PasswdFile)
   161  	}
   162  
   163  	return u, nil
   164  }
   165  
   166  // IDCommand runs the "id" with the current user's information.
   167  func IDCommand(u User) {
   168  	if !flags.groups {
   169  		if flags.user {
   170  			if flags.name {
   171  				fmt.Println(u.Name())
   172  				return
   173  			}
   174  			fmt.Println(u.UID())
   175  			return
   176  		} else if flags.group {
   177  			if flags.name {
   178  				fmt.Println(u.GIDName())
   179  				return
   180  			}
   181  			fmt.Println(u.GID())
   182  			return
   183  		}
   184  
   185  		fmt.Printf("uid=%d(%s) ", u.UID(), u.Name())
   186  		fmt.Printf("gid=%d(%s) ", u.GID(), u.GIDName())
   187  	}
   188  
   189  	if !flags.groups {
   190  		fmt.Print("groups=")
   191  	}
   192  
   193  	var groupOutput []string
   194  
   195  	for gid, name := range u.Groups() {
   196  
   197  		if !flags.groups {
   198  			groupOutput = append(groupOutput, fmt.Sprintf("%d(%s)", gid, name))
   199  
   200  		} else {
   201  			if flags.name {
   202  				groupOutput = append(groupOutput, fmt.Sprintf("%s ", name))
   203  			} else {
   204  				groupOutput = append(groupOutput, fmt.Sprintf("%d ", gid))
   205  			}
   206  		}
   207  	}
   208  
   209  	sep := ","
   210  	if flags.groups {
   211  		sep = ""
   212  	}
   213  
   214  	fmt.Println(strings.Join(groupOutput, sep))
   215  }
   216  
   217  func main() {
   218  	flag.Parse()
   219  	if !correctFlags(flags.groups, flags.group, flags.user) {
   220  		log.Fatalf("id: cannot print \"only\" of more than one choice")
   221  
   222  	}
   223  	if flags.name && !(flags.groups || flags.group || flags.user) {
   224  		log.Fatalf("id: cannot print only names in default format")
   225  	}
   226  
   227  	currentUser, err := NewUser()
   228  	if err != nil {
   229  		log.Fatalf("id: %s", err)
   230  	}
   231  
   232  	IDCommand(*currentUser)
   233  }