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