github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/os/user/lookup_unix.go (about)

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