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 }