Source file src/pkg/os/user/lookup_unix.go
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 freebsd linux 6 // +build cgo 7 8 package user 9 10 import ( 11 "fmt" 12 "runtime" 13 "strconv" 14 "strings" 15 "syscall" 16 "unsafe" 17 ) 18 19 /* 20 #include <unistd.h> 21 #include <sys/types.h> 22 #include <pwd.h> 23 #include <stdlib.h> 24 25 static int mygetpwuid_r(int uid, struct passwd *pwd, 26 char *buf, size_t buflen, struct passwd **result) { 27 return getpwuid_r(uid, pwd, buf, buflen, result); 28 } 29 */ 30 import "C" 31 32 // Current returns the current user. 33 func Current() (*User, error) { 34 return lookup(syscall.Getuid(), "", false) 35 } 36 37 // Lookup looks up a user by username. If the user cannot be found, 38 // the returned error is of type UnknownUserError. 39 func Lookup(username string) (*User, error) { 40 return lookup(-1, username, true) 41 } 42 43 // LookupId looks up a user by userid. If the user cannot be found, 44 // the returned error is of type UnknownUserIdError. 45 func LookupId(uid string) (*User, error) { 46 i, e := strconv.Atoi(uid) 47 if e != nil { 48 return nil, e 49 } 50 return lookup(i, "", false) 51 } 52 53 func lookup(uid int, username string, lookupByName bool) (*User, error) { 54 var pwd C.struct_passwd 55 var result *C.struct_passwd 56 57 var bufSize C.long 58 if runtime.GOOS == "freebsd" { 59 // FreeBSD doesn't have _SC_GETPW_R_SIZE_MAX 60 // and just returns -1. So just use the same 61 // size that Linux returns 62 bufSize = 1024 63 } else { 64 bufSize = C.sysconf(C._SC_GETPW_R_SIZE_MAX) 65 if bufSize <= 0 || bufSize > 1<<20 { 66 return nil, fmt.Errorf("user: unreasonable _SC_GETPW_R_SIZE_MAX of %d", bufSize) 67 } 68 } 69 buf := C.malloc(C.size_t(bufSize)) 70 defer C.free(buf) 71 var rv C.int 72 if lookupByName { 73 nameC := C.CString(username) 74 defer C.free(unsafe.Pointer(nameC)) 75 rv = C.getpwnam_r(nameC, 76 &pwd, 77 (*C.char)(buf), 78 C.size_t(bufSize), 79 &result) 80 if rv != 0 { 81 return nil, fmt.Errorf("user: lookup username %s: %s", username, syscall.Errno(rv)) 82 } 83 if result == nil { 84 return nil, UnknownUserError(username) 85 } 86 } else { 87 // mygetpwuid_r is a wrapper around getpwuid_r to 88 // to avoid using uid_t because C.uid_t(uid) for 89 // unknown reasons doesn't work on linux. 90 rv = C.mygetpwuid_r(C.int(uid), 91 &pwd, 92 (*C.char)(buf), 93 C.size_t(bufSize), 94 &result) 95 if rv != 0 { 96 return nil, fmt.Errorf("user: lookup userid %d: %s", uid, syscall.Errno(rv)) 97 } 98 if result == nil { 99 return nil, UnknownUserIdError(uid) 100 } 101 } 102 u := &User{ 103 Uid: strconv.Itoa(int(pwd.pw_uid)), 104 Gid: strconv.Itoa(int(pwd.pw_gid)), 105 Username: C.GoString(pwd.pw_name), 106 Name: C.GoString(pwd.pw_gecos), 107 HomeDir: C.GoString(pwd.pw_dir), 108 } 109 // The pw_gecos field isn't quite standardized. Some docs 110 // say: "It is expected to be a comma separated list of 111 // personal data where the first item is the full name of the 112 // user." 113 if i := strings.Index(u.Name, ","); i >= 0 { 114 u.Name = u.Name[:i] 115 } 116 return u, nil 117 }