| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris |
| // +build cgo |
| |
| package user |
| |
| import ( |
| "fmt" |
| "strconv" |
| "strings" |
| "syscall" |
| "unsafe" |
| ) |
| |
| /* |
| #cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <pwd.h> |
| #include <stdlib.h> |
| |
| static int mygetpwuid_r(int uid, struct passwd *pwd, |
| char *buf, size_t buflen, struct passwd **result) { |
| return getpwuid_r(uid, pwd, buf, buflen, result); |
| } |
| |
| static int mygetpwnam_r(const char *name, struct passwd *pwd, |
| char *buf, size_t buflen, struct passwd **result) { |
| return getpwnam_r(name, pwd, buf, buflen, result); |
| } |
| */ |
| import "C" |
| |
| func current() (*User, error) { |
| return lookupUnix(syscall.Getuid(), "", false) |
| } |
| |
| func lookup(username string) (*User, error) { |
| return lookupUnix(-1, username, true) |
| } |
| |
| func lookupId(uid string) (*User, error) { |
| i, e := strconv.Atoi(uid) |
| if e != nil { |
| return nil, e |
| } |
| return lookupUnix(i, "", false) |
| } |
| |
| func lookupUnix(uid int, username string, lookupByName bool) (*User, error) { |
| var pwd C.struct_passwd |
| var result *C.struct_passwd |
| |
| bufSize := C.sysconf(C._SC_GETPW_R_SIZE_MAX) |
| if bufSize == -1 { |
| // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX. |
| // Additionally, not all Linux systems have it, either. For |
| // example, the musl libc returns -1. |
| bufSize = 1024 |
| } |
| if bufSize <= 0 || bufSize > 1<<20 { |
| return nil, fmt.Errorf("user: unreasonable _SC_GETPW_R_SIZE_MAX of %d", bufSize) |
| } |
| buf := C.malloc(C.size_t(bufSize)) |
| defer C.free(buf) |
| var rv C.int |
| if lookupByName { |
| nameC := C.CString(username) |
| defer C.free(unsafe.Pointer(nameC)) |
| // mygetpwnam_r is a wrapper around getpwnam_r to avoid |
| // passing a size_t to getpwnam_r, because for unknown |
| // reasons passing a size_t to getpwnam_r doesn't work on |
| // Solaris. |
| rv = C.mygetpwnam_r(nameC, |
| &pwd, |
| (*C.char)(buf), |
| C.size_t(bufSize), |
| &result) |
| if rv != 0 { |
| return nil, fmt.Errorf("user: lookup username %s: %s", username, syscall.Errno(rv)) |
| } |
| if result == nil { |
| return nil, UnknownUserError(username) |
| } |
| } else { |
| // mygetpwuid_r is a wrapper around getpwuid_r to |
| // to avoid using uid_t because C.uid_t(uid) for |
| // unknown reasons doesn't work on linux. |
| rv = C.mygetpwuid_r(C.int(uid), |
| &pwd, |
| (*C.char)(buf), |
| C.size_t(bufSize), |
| &result) |
| if rv != 0 { |
| return nil, fmt.Errorf("user: lookup userid %d: %s", uid, syscall.Errno(rv)) |
| } |
| if result == nil { |
| return nil, UnknownUserIdError(uid) |
| } |
| } |
| u := &User{ |
| Uid: strconv.Itoa(int(pwd.pw_uid)), |
| Gid: strconv.Itoa(int(pwd.pw_gid)), |
| Username: C.GoString(pwd.pw_name), |
| Name: C.GoString(pwd.pw_gecos), |
| HomeDir: C.GoString(pwd.pw_dir), |
| } |
| // The pw_gecos field isn't quite standardized. Some docs |
| // say: "It is expected to be a comma separated list of |
| // personal data where the first item is the full name of the |
| // user." |
| if i := strings.Index(u.Name, ","); i >= 0 { |
| u.Name = u.Name[:i] |
| } |
| return u, nil |
| } |