1 // Copyright 2011 The Go 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 // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris 6 // +build cgo 7 8 package user 9 10 import ( 11 "fmt" 12 "strconv" 13 "strings" 14 "syscall" 15 "unsafe" 16 ) 17 18 /* 19 #cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS 20 #include <unistd.h> 21 #include <sys/types.h> 22 #include <pwd.h> 23 #include <grp.h> 24 #include <stdlib.h> 25 26 static int mygetpwuid_r(int uid, struct passwd *pwd, 27 char *buf, size_t buflen, struct passwd **result) { 28 return getpwuid_r(uid, pwd, buf, buflen, result); 29 } 30 31 static int mygetpwnam_r(const char *name, struct passwd *pwd, 32 char *buf, size_t buflen, struct passwd **result) { 33 return getpwnam_r(name, pwd, buf, buflen, result); 34 } 35 36 static int mygetgrgid_r(int gid, struct group *grp, 37 char *buf, size_t buflen, struct group **result) { 38 return getgrgid_r(gid, grp, buf, buflen, result); 39 } 40 41 static int mygetgrnam_r(const char *name, struct group *grp, 42 char *buf, size_t buflen, struct group **result) { 43 return getgrnam_r(name, grp, buf, buflen, result); 44 } 45 */ 46 import "C" 47 48 func current() (*User, error) { 49 return lookupUnixUid(syscall.Getuid()) 50 } 51 52 func lookupUser(username string) (*User, error) { 53 var pwd C.struct_passwd 54 var result *C.struct_passwd 55 nameC := C.CString(username) 56 defer C.free(unsafe.Pointer(nameC)) 57 58 buf := alloc(userBuffer) 59 defer buf.free() 60 61 err := retryWithBuffer(buf, func() syscall.Errno { 62 // mygetpwnam_r is a wrapper around getpwnam_r to avoid 63 // passing a size_t to getpwnam_r, because for unknown 64 // reasons passing a size_t to getpwnam_r doesn't work on 65 // Solaris. 66 return syscall.Errno(C.mygetpwnam_r(nameC, 67 &pwd, 68 (*C.char)(buf.ptr), 69 C.size_t(buf.size), 70 &result)) 71 }) 72 if err != nil { 73 return nil, fmt.Errorf("user: lookup username %s: %v", username, err) 74 } 75 if result == nil { 76 return nil, UnknownUserError(username) 77 } 78 return buildUser(&pwd), err 79 } 80 81 func lookupUserId(uid string) (*User, error) { 82 i, e := strconv.Atoi(uid) 83 if e != nil { 84 return nil, e 85 } 86 return lookupUnixUid(i) 87 } 88 89 func lookupUnixUid(uid int) (*User, error) { 90 var pwd C.struct_passwd 91 var result *C.struct_passwd 92 93 buf := alloc(userBuffer) 94 defer buf.free() 95 96 err := retryWithBuffer(buf, func() syscall.Errno { 97 // mygetpwuid_r is a wrapper around getpwuid_r to 98 // to avoid using uid_t because C.uid_t(uid) for 99 // unknown reasons doesn't work on linux. 100 return syscall.Errno(C.mygetpwuid_r(C.int(uid), 101 &pwd, 102 (*C.char)(buf.ptr), 103 C.size_t(buf.size), 104 &result)) 105 }) 106 if err != nil { 107 return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err) 108 } 109 if result == nil { 110 return nil, UnknownUserIdError(uid) 111 } 112 return buildUser(&pwd), nil 113 } 114 115 func buildUser(pwd *C.struct_passwd) *User { 116 u := &User{ 117 Uid: strconv.Itoa(int(pwd.pw_uid)), 118 Gid: strconv.Itoa(int(pwd.pw_gid)), 119 Username: C.GoString(pwd.pw_name), 120 Name: C.GoString(pwd.pw_gecos), 121 HomeDir: C.GoString(pwd.pw_dir), 122 } 123 // The pw_gecos field isn't quite standardized. Some docs 124 // say: "It is expected to be a comma separated list of 125 // personal data where the first item is the full name of the 126 // user." 127 if i := strings.Index(u.Name, ","); i >= 0 { 128 u.Name = u.Name[:i] 129 } 130 return u 131 } 132 133 func currentGroup() (*Group, error) { 134 return lookupUnixGid(syscall.Getgid()) 135 } 136 137 func lookupGroup(groupname string) (*Group, error) { 138 var grp C.struct_group 139 var result *C.struct_group 140 141 buf := alloc(groupBuffer) 142 defer buf.free() 143 cname := C.CString(groupname) 144 defer C.free(unsafe.Pointer(cname)) 145 146 err := retryWithBuffer(buf, func() syscall.Errno { 147 return syscall.Errno(C.mygetgrnam_r(cname, 148 &grp, 149 (*C.char)(buf.ptr), 150 C.size_t(buf.size), 151 &result)) 152 }) 153 if err != nil { 154 return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err) 155 } 156 if result == nil { 157 return nil, UnknownGroupError(groupname) 158 } 159 return buildGroup(&grp), nil 160 } 161 162 func lookupGroupId(gid string) (*Group, error) { 163 i, e := strconv.Atoi(gid) 164 if e != nil { 165 return nil, e 166 } 167 return lookupUnixGid(i) 168 } 169 170 func lookupUnixGid(gid int) (*Group, error) { 171 var grp C.struct_group 172 var result *C.struct_group 173 174 buf := alloc(groupBuffer) 175 defer buf.free() 176 177 err := retryWithBuffer(buf, func() syscall.Errno { 178 // mygetgrgid_r is a wrapper around getgrgid_r to 179 // to avoid using gid_t because C.gid_t(gid) for 180 // unknown reasons doesn't work on linux. 181 return syscall.Errno(C.mygetgrgid_r(C.int(gid), 182 &grp, 183 (*C.char)(buf.ptr), 184 C.size_t(buf.size), 185 &result)) 186 }) 187 if err != nil { 188 return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err) 189 } 190 if result == nil { 191 return nil, UnknownGroupIdError(strconv.Itoa(gid)) 192 } 193 return buildGroup(&grp), nil 194 } 195 196 func buildGroup(grp *C.struct_group) *Group { 197 g := &Group{ 198 Gid: strconv.Itoa(int(grp.gr_gid)), 199 Name: C.GoString(grp.gr_name), 200 } 201 return g 202 } 203 204 type bufferKind C.int 205 206 const ( 207 userBuffer = bufferKind(C._SC_GETPW_R_SIZE_MAX) 208 groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX) 209 ) 210 211 func (k bufferKind) initialSize() C.size_t { 212 sz := C.sysconf(C.int(k)) 213 if sz == -1 { 214 // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. 215 // Additionally, not all Linux systems have it, either. For 216 // example, the musl libc returns -1. 217 return 1024 218 } 219 if !isSizeReasonable(int64(sz)) { 220 // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run. 221 return maxBufferSize 222 } 223 return C.size_t(sz) 224 } 225 226 type memBuffer struct { 227 ptr unsafe.Pointer 228 size C.size_t 229 } 230 231 func alloc(kind bufferKind) *memBuffer { 232 sz := kind.initialSize() 233 return &memBuffer{ 234 ptr: C.malloc(sz), 235 size: sz, 236 } 237 } 238 239 func (mb *memBuffer) resize(newSize C.size_t) { 240 mb.ptr = C.realloc(mb.ptr, newSize) 241 mb.size = newSize 242 } 243 244 func (mb *memBuffer) free() { 245 C.free(mb.ptr) 246 } 247 248 // retryWithBuffer repeatedly calls f(), increasing the size of the 249 // buffer each time, until f succeeds, fails with a non-ERANGE error, 250 // or the buffer exceeds a reasonable limit. 251 func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error { 252 for { 253 errno := f() 254 if errno == 0 { 255 return nil 256 } else if errno != syscall.ERANGE { 257 return errno 258 } 259 newSize := buf.size * 2 260 if !isSizeReasonable(int64(newSize)) { 261 return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize) 262 } 263 buf.resize(newSize) 264 } 265 } 266 267 const maxBufferSize = 1 << 20 268 269 func isSizeReasonable(sz int64) bool { 270 return sz > 0 && sz <= maxBufferSize 271 } 272