LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package user | 5 package user |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 "os" | 9 "os" |
10 "runtime" | 10 "runtime" |
11 "strings" | 11 "strings" |
12 "unsafe" | 12 "unsafe" |
13 ) | 13 ) |
14 | 14 |
15 /* | 15 /* |
16 #include <unistd.h> | 16 #include <unistd.h> |
17 #include <sys/types.h> | 17 #include <sys/types.h> |
18 #include <pwd.h> | 18 #include <pwd.h> |
| 19 #include <grp.h> |
19 #include <stdlib.h> | 20 #include <stdlib.h> |
20 | 21 |
21 static int mygetpwuid_r(int uid, struct passwd *pwd, | 22 static int mygetpwuid_r(int uid, struct passwd *pwd, |
22 char *buf, size_t buflen, struct passwd **result) { | 23 char *buf, size_t buflen, struct passwd **result) { |
23 return getpwuid_r(uid, pwd, buf, buflen, result); | 24 return getpwuid_r(uid, pwd, buf, buflen, result); |
24 } | 25 } |
| 26 |
| 27 static int mygetgrgid_r(int gid, struct group *grp, |
| 28 char *buf, size_t buflen, struct group **result) { |
| 29 return getgrgid_r(gid, grp, buf, buflen, result); |
| 30 } |
| 31 |
| 32 static int mygetgrouplist(const char *user, gid_t group, gid_t *groups, |
| 33 int *ngroups) { |
| 34 return getgrouplist(user, group, (gid_t *)groups, ngroups); |
| 35 } |
| 36 |
| 37 static inline gid_t group_at(int i, gid_t *groups) { |
| 38 return groups[i]; |
| 39 } |
| 40 static inline char **next_member(char **members) { return members + 1; } |
25 */ | 41 */ |
26 import "C" | 42 import "C" |
27 | 43 |
28 // Lookup looks up a user by username. If the user cannot be found, | 44 // Lookup looks up a user by username. If the user cannot be found, |
29 // the returned error is of type UnknownUserError. | 45 // the returned error is of type UnknownUserError. |
30 func Lookup(username string) (*User, os.Error) { | 46 func Lookup(username string) (*User, os.Error) { |
31 » return lookup(-1, username, true) | 47 » u, err := lookupUser(username, buildUser) |
| 48 » if err != nil { |
| 49 » » return nil, err |
| 50 » } |
| 51 » return u, nil |
32 } | 52 } |
33 | 53 |
34 // LookupId looks up a user by userid. If the user cannot be found, | 54 // LookupId looks up a user by userid. If the user cannot be found, |
35 // the returned error is of type UnknownUserIdError. | 55 // the returned error is of type UnknownUserIdError. |
36 func LookupId(uid int) (*User, os.Error) { | 56 func LookupId(uid int) (*User, os.Error) { |
37 » return lookup(uid, "", false) | 57 » u, err := lookupUserId(uid, buildUser) |
38 } | 58 » if err != nil { |
39 | 59 » » return nil, err |
40 func lookup(uid int, username string, lookupByName bool) (*User, os.Error) { | 60 » } |
| 61 » return u, nil |
| 62 } |
| 63 |
| 64 // In indicates whether the user is a member of the given group. |
| 65 func (u *User) In(g *Group) (bool, os.Error) { |
| 66 » if u.Gid == g.Gid { |
| 67 » » return true, nil |
| 68 » } |
| 69 |
| 70 » nameC := C.CString(u.Username) |
| 71 » defer C.free(unsafe.Pointer(nameC)) |
| 72 » groupC := C.gid_t(u.Gid) |
| 73 » ngroupsC := C.int(0) |
| 74 |
| 75 » C.mygetgrouplist(nameC, groupC, nil, &ngroupsC) |
| 76 » ngroups := int(ngroupsC) |
| 77 |
| 78 » groups := C.malloc(C.size_t(int(unsafe.Sizeof(groupC)) * ngroups)) |
| 79 » defer C.free(groups) |
| 80 |
| 81 » rv := C.mygetgrouplist(nameC, groupC, (*C.gid_t)(groups), &ngroupsC) |
| 82 » if rv == -1 { |
| 83 » » return false, fmt.Errorf("user: membership of %s in %s: %s", u.U
sername, g.Name, os.Errno(rv)) |
| 84 » } |
| 85 |
| 86 » ngroups = int(ngroupsC) |
| 87 » for i := 0; i < ngroups; i++ { |
| 88 » » gid := C.group_at(C.int(i), (*C.gid_t)(groups)) |
| 89 » » if g.Gid == int(gid) { |
| 90 » » » return true, nil |
| 91 » » } |
| 92 » } |
| 93 » return false, nil |
| 94 } |
| 95 |
| 96 // LookupGroup looks up a group by groupname. If the group cannot |
| 97 // be found, the returned error is of type UnknownGroupError. |
| 98 func LookupGroup(groupname string) (*Group, os.Error) { |
| 99 » g, err := lookupGroup(groupname, buildGroup) |
| 100 » if err != nil { |
| 101 » » return nil, err |
| 102 » } |
| 103 » return g, nil |
| 104 } |
| 105 |
| 106 // LookupGroupId looks up a group by groupid. If the group cannot |
| 107 // be found, the returned error is of type UnknownGroupIdError. |
| 108 func LookupGroupId(gid int) (*Group, os.Error) { |
| 109 » g, err := lookupGroupId(gid, buildGroup) |
| 110 » if err != nil { |
| 111 » » return nil, err |
| 112 » } |
| 113 » return g, nil |
| 114 } |
| 115 |
| 116 // Members returns the list of members of the group. |
| 117 func (g *Group) Members() ([]string, os.Error) { |
| 118 » var members []string |
| 119 |
| 120 » _, err := lookupGroupId(g.Gid, func(grp *C.struct_group) *Group { |
| 121 » » cmem := grp.gr_mem |
| 122 » » for *cmem != nil { |
| 123 » » » members = append(members, C.GoString(*cmem)) |
| 124 » » » cmem = C.next_member(cmem) |
| 125 » » } |
| 126 » » return g |
| 127 » }) |
| 128 » if err != nil { |
| 129 » » return nil, err |
| 130 » } |
| 131 |
| 132 » return members, nil |
| 133 } |
| 134 |
| 135 const ( |
| 136 » userBuffer = iota |
| 137 » groupBuffer |
| 138 ) |
| 139 |
| 140 func lookupUser(username string, f func(*C.struct_passwd) *User) (*User, os.Erro
r) { |
41 var pwd C.struct_passwd | 141 var pwd C.struct_passwd |
42 var result *C.struct_passwd | 142 var result *C.struct_passwd |
43 | 143 |
44 » var bufSize C.long | 144 » buf, bufSize, err := allocBuffer(userBuffer) |
45 » if runtime.GOOS == "freebsd" { | 145 » if err != nil { |
46 » » // FreeBSD doesn't have _SC_GETPW_R_SIZE_MAX | 146 » » return nil, err |
47 » » // and just returns -1. So just use the same | 147 » } |
48 » » // size that Linux returns | |
49 » » bufSize = 1024 | |
50 » } else { | |
51 » » bufSize = C.sysconf(C._SC_GETPW_R_SIZE_MAX) | |
52 » » if bufSize <= 0 || bufSize > 1<<20 { | |
53 » » » return nil, fmt.Errorf("user: unreasonable _SC_GETPW_R_S
IZE_MAX of %d", bufSize) | |
54 » » } | |
55 » } | |
56 » buf := C.malloc(C.size_t(bufSize)) | |
57 defer C.free(buf) | 148 defer C.free(buf) |
58 » var rv C.int | 149 |
59 » if lookupByName { | 150 » nameC := C.CString(username) |
60 » » nameC := C.CString(username) | 151 » defer C.free(unsafe.Pointer(nameC)) |
61 » » defer C.free(unsafe.Pointer(nameC)) | 152 » rv := C.getpwnam_r(nameC, |
62 » » rv = C.getpwnam_r(nameC, | 153 » » &pwd, |
63 » » » &pwd, | 154 » » (*C.char)(buf), |
64 » » » (*C.char)(buf), | 155 » » C.size_t(bufSize), |
65 » » » C.size_t(bufSize), | 156 » » &result) |
66 » » » &result) | 157 » if rv != 0 { |
67 » » if rv != 0 { | 158 » » return nil, fmt.Errorf("user: lookup username %s: %s", username,
os.Errno(rv)) |
68 » » » return nil, fmt.Errorf("user: lookup username %s: %s", u
sername, os.Errno(rv)) | 159 » } |
69 » » } | 160 » if result == nil { |
70 » » if result == nil { | 161 » » return nil, UnknownUserError(username) |
71 » » » return nil, UnknownUserError(username) | 162 » } |
72 » » } | 163 » u := f(&pwd) |
73 » } else { | 164 » return u, nil |
74 » » // mygetpwuid_r is a wrapper around getpwuid_r to | 165 } |
75 » » // to avoid using uid_t because C.uid_t(uid) for | 166 |
76 » » // unknown reasons doesn't work on linux. | 167 func lookupUserId(uid int, f func(*C.struct_passwd) *User) (*User, os.Error) { |
77 » » rv = C.mygetpwuid_r(C.int(uid), | 168 » var pwd C.struct_passwd |
78 » » » &pwd, | 169 » var result *C.struct_passwd |
79 » » » (*C.char)(buf), | 170 |
80 » » » C.size_t(bufSize), | 171 » buf, bufSize, err := allocBuffer(userBuffer) |
81 » » » &result) | 172 » if err != nil { |
82 » » if rv != 0 { | 173 » » return nil, err |
83 » » » return nil, fmt.Errorf("user: lookup userid %d: %s", uid
, os.Errno(rv)) | 174 » } |
84 » » } | 175 » defer C.free(buf) |
85 » » if result == nil { | 176 |
86 » » » return nil, UnknownUserIdError(uid) | 177 » // mygetpwuid_r is a wrapper around getpwuid_r to |
87 » » } | 178 » // to avoid using uid_t because C.uid_t(uid) for |
88 » } | 179 » // unknown reasons doesn't work on linux. |
| 180 » rv := C.mygetpwuid_r(C.int(uid), |
| 181 » » &pwd, |
| 182 » » (*C.char)(buf), |
| 183 » » C.size_t(bufSize), |
| 184 » » &result) |
| 185 » if rv != 0 { |
| 186 » » return nil, fmt.Errorf("user: lookup userid %d: %s", uid, os.Err
no(rv)) |
| 187 » } |
| 188 » if result == nil { |
| 189 » » return nil, UnknownUserIdError(uid) |
| 190 » } |
| 191 » u := f(&pwd) |
| 192 » return u, nil |
| 193 } |
| 194 |
| 195 func buildUser(pwd *C.struct_passwd) *User { |
89 u := &User{ | 196 u := &User{ |
90 Uid: int(pwd.pw_uid), | 197 Uid: int(pwd.pw_uid), |
91 Gid: int(pwd.pw_gid), | 198 Gid: int(pwd.pw_gid), |
92 Username: C.GoString(pwd.pw_name), | 199 Username: C.GoString(pwd.pw_name), |
93 Name: C.GoString(pwd.pw_gecos), | 200 Name: C.GoString(pwd.pw_gecos), |
94 HomeDir: C.GoString(pwd.pw_dir), | 201 HomeDir: C.GoString(pwd.pw_dir), |
95 } | 202 } |
96 // The pw_gecos field isn't quite standardized. Some docs | 203 // The pw_gecos field isn't quite standardized. Some docs |
97 // say: "It is expected to be a comma separated list of | 204 // say: "It is expected to be a comma separated list of |
98 // personal data where the first item is the full name of the | 205 // personal data where the first item is the full name of the |
99 // user." | 206 // user." |
100 if i := strings.Index(u.Name, ","); i >= 0 { | 207 if i := strings.Index(u.Name, ","); i >= 0 { |
101 u.Name = u.Name[:i] | 208 u.Name = u.Name[:i] |
102 } | 209 } |
103 » return u, nil | 210 » return u |
104 } | 211 } |
| 212 |
| 213 func lookupGroup(groupname string, f func(*C.struct_group) *Group) (*Group, os.E
rror) { |
| 214 » var grp C.struct_group |
| 215 » var result *C.struct_group |
| 216 |
| 217 » buf, bufSize, err := allocBuffer(groupBuffer) |
| 218 » if err != nil { |
| 219 » » return nil, err |
| 220 » } |
| 221 » defer C.free(buf) |
| 222 |
| 223 » nameC := C.CString(groupname) |
| 224 » defer C.free(unsafe.Pointer(nameC)) |
| 225 » rv := C.getgrnam_r(nameC, |
| 226 » » &grp, |
| 227 » » (*C.char)(buf), |
| 228 » » C.size_t(bufSize), |
| 229 » » &result) |
| 230 » if rv != 0 { |
| 231 » » return nil, fmt.Errorf("group: lookup groupname %s: %s", groupna
me, os.Errno(rv)) |
| 232 » } |
| 233 » if result == nil { |
| 234 » » return nil, UnknownGroupError(groupname) |
| 235 » } |
| 236 » g := f(&grp) |
| 237 » return g, nil |
| 238 } |
| 239 |
| 240 func lookupGroupId(gid int, f func(*C.struct_group) *Group) (*Group, os.Error) { |
| 241 » var grp C.struct_group |
| 242 » var result *C.struct_group |
| 243 |
| 244 » buf, bufSize, err := allocBuffer(groupBuffer) |
| 245 » if err != nil { |
| 246 » » return nil, err |
| 247 » } |
| 248 » defer C.free(buf) |
| 249 |
| 250 » // mygetgrgid_r is a wrapper around getgrgid_r to |
| 251 » // to avoid using gid_t because C.gid_t(gid) for |
| 252 » // unknown reasons doesn't work on linux. |
| 253 » rv := C.mygetgrgid_r(C.int(gid), |
| 254 » » &grp, |
| 255 » » (*C.char)(buf), |
| 256 » » C.size_t(bufSize), |
| 257 » » &result) |
| 258 » if rv != 0 { |
| 259 » » return nil, fmt.Errorf("group: lookup groupid %d: %s", gid, os.E
rrno(rv)) |
| 260 » } |
| 261 » if result == nil { |
| 262 » » return nil, UnknownGroupIdError(gid) |
| 263 » } |
| 264 » g := f(&grp) |
| 265 » return g, nil |
| 266 } |
| 267 |
| 268 func buildGroup(grp *C.struct_group) *Group { |
| 269 » g := &Group{ |
| 270 » » Gid: int(grp.gr_gid), |
| 271 » » Name: C.GoString(grp.gr_name), |
| 272 » } |
| 273 » return g |
| 274 } |
| 275 |
| 276 func allocBuffer(bufType int) (unsafe.Pointer, C.long, os.Error) { |
| 277 » var bufSize C.long |
| 278 » if runtime.GOOS == "freebsd" { |
| 279 » » // FreeBSD doesn't have _SC_GETPW_R_SIZE_MAX |
| 280 » » // or SC_GETGR_R_SIZE_MAX and just returns -1. |
| 281 » » // So just use the same size that Linux returns |
| 282 » » bufSize = 1024 |
| 283 » } else { |
| 284 » » var size C.int |
| 285 » » var constName string |
| 286 » » switch bufType { |
| 287 » » case userBuffer: |
| 288 » » » size = C._SC_GETPW_R_SIZE_MAX |
| 289 » » » constName = "_SC_GETPW_R_SIZE_MAX" |
| 290 » » case groupBuffer: |
| 291 » » » size = C._SC_GETGR_R_SIZE_MAX |
| 292 » » » constName = "_SC_GETGR_R_SIZE_MAX" |
| 293 » » } |
| 294 » » bufSize = C.sysconf(size) |
| 295 » » if bufSize <= 0 || bufSize > 1<<20 { |
| 296 » » » return nil, bufSize, fmt.Errorf("user: unreasonable %s o
f %d", constName, bufSize) |
| 297 » » } |
| 298 » } |
| 299 » return C.malloc(C.size_t(bufSize)), bufSize, nil |
| 300 } |
LEFT | RIGHT |