// 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"
"runtime"
"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
var bufSize C.long
if runtime.GOOS == "dragonfly" || runtime.GOOS == "freebsd" {
// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX
// and just return -1. So just use the same
// size that Linux returns.
bufSize = 1024
} else {
bufSize = C.sysconf(C._SC_GETPW_R_SIZE_MAX)
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
}