github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/osutil/group.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil 21 22 import ( 23 "bytes" 24 "fmt" 25 "os/exec" 26 "os/user" 27 "strconv" 28 ) 29 30 // FindUid returns the identifier of the given UNIX user name. It will 31 // automatically fallback to use "getent" if needed. 32 func FindUid(username string) (uint64, error) { 33 return findUid(username) 34 } 35 36 // FindGid returns the identifier of the given UNIX group name. It will 37 // automatically fallback to use "getent" if needed. 38 func FindGid(groupname string) (uint64, error) { 39 return findGid(groupname) 40 } 41 42 // getent returns the identifier of the given UNIX user or group name as 43 // determined by the specified database 44 func getent(database, name string) (uint64, error) { 45 if database != "passwd" && database != "group" { 46 return 0, fmt.Errorf(`unsupported getent database "%q"`, database) 47 } 48 49 cmdStr := []string{ 50 "getent", 51 database, 52 name, 53 } 54 cmd := exec.Command(cmdStr[0], cmdStr[1:]...) 55 output, err := cmd.CombinedOutput() 56 if err != nil { 57 // according to getent(1) the exit value of "2" means: 58 // "One or more supplied key could not be found in the 59 // database." 60 exitCode, _ := ExitCode(err) 61 if exitCode == 2 { 62 if database == "passwd" { 63 return 0, user.UnknownUserError(name) 64 } 65 return 0, user.UnknownGroupError(name) 66 } 67 return 0, fmt.Errorf("getent failed with: %v", OutputErr(output, err)) 68 } 69 70 // passwd has 7 entries and group 4. In both cases, parts[2] is the id 71 parts := bytes.Split(output, []byte(":")) 72 if len(parts) < 4 { 73 return 0, fmt.Errorf("malformed entry: %q", output) 74 } 75 76 return strconv.ParseUint(string(parts[2]), 10, 64) 77 } 78 79 var findUidNoGetentFallback = func(username string) (uint64, error) { 80 myuser, err := user.Lookup(username) 81 if err != nil { 82 return 0, err 83 } 84 85 return strconv.ParseUint(myuser.Uid, 10, 64) 86 } 87 88 var findGidNoGetentFallback = func(groupname string) (uint64, error) { 89 group, err := user.LookupGroup(groupname) 90 if err != nil { 91 return 0, err 92 } 93 94 return strconv.ParseUint(group.Gid, 10, 64) 95 } 96 97 // findUidWithGetentFallback returns the identifier of the given UNIX user name with 98 // getent fallback 99 func findUidWithGetentFallback(username string) (uint64, error) { 100 // first do the cheap os/user lookup 101 myuser, err := findUidNoGetentFallback(username) 102 switch err.(type) { 103 case nil: 104 // found it! 105 return myuser, nil 106 case user.UnknownUserError: 107 // user unknown, let's try getent 108 return getent("passwd", username) 109 default: 110 // something weird happened with the lookup, just report it 111 return 0, err 112 } 113 } 114 115 // findGidWithGetentFallback returns the identifier of the given UNIX group name with 116 // getent fallback 117 func findGidWithGetentFallback(groupname string) (uint64, error) { 118 // first do the cheap os/user lookup 119 group, err := findGidNoGetentFallback(groupname) 120 switch err.(type) { 121 case nil: 122 // found it! 123 return group, nil 124 case user.UnknownGroupError: 125 // group unknown, let's try getent 126 return getent("group", groupname) 127 default: 128 // something weird happened with the lookup, just report it 129 return 0, err 130 } 131 } 132 133 func IsUnknownUser(err error) bool { 134 _, ok := err.(user.UnknownUserError) 135 return ok 136 } 137 138 func IsUnknownGroup(err error) bool { 139 _, ok := err.(user.UnknownGroupError) 140 return ok 141 }