Home | History | Annotate | Download | only in user
      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