github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/os/user/cgo_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  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  	"unsafe"
    16  )
    17  
    18  /*
    19  #cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
    20  #include <unistd.h>
    21  #include <sys/types.h>
    22  #include <pwd.h>
    23  #include <grp.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  static int mygetgrgid_r(int gid, struct group *grp,
    37  	char *buf, size_t buflen, struct group **result) {
    38   return getgrgid_r(gid, grp, buf, buflen, result);
    39  }
    40  
    41  static int mygetgrnam_r(const char *name, struct group *grp,
    42  	char *buf, size_t buflen, struct group **result) {
    43   return getgrnam_r(name, grp, buf, buflen, result);
    44  }
    45  */
    46  import "C"
    47  
    48  func current() (*User, error) {
    49  	return lookupUnixUid(syscall.Getuid())
    50  }
    51  
    52  func lookupUser(username string) (*User, error) {
    53  	var pwd C.struct_passwd
    54  	var result *C.struct_passwd
    55  	nameC := make([]byte, len(username)+1)
    56  	copy(nameC, username)
    57  
    58  	buf := alloc(userBuffer)
    59  	defer buf.free()
    60  
    61  	err := retryWithBuffer(buf, func() syscall.Errno {
    62  		// mygetpwnam_r is a wrapper around getpwnam_r to avoid
    63  		// passing a size_t to getpwnam_r, because for unknown
    64  		// reasons passing a size_t to getpwnam_r doesn't work on
    65  		// Solaris.
    66  		return syscall.Errno(C.mygetpwnam_r((*C.char)(unsafe.Pointer(&nameC[0])),
    67  			&pwd,
    68  			(*C.char)(buf.ptr),
    69  			C.size_t(buf.size),
    70  			&result))
    71  	})
    72  	if err != nil {
    73  		return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
    74  	}
    75  	if result == nil {
    76  		return nil, UnknownUserError(username)
    77  	}
    78  	return buildUser(&pwd), err
    79  }
    80  
    81  func lookupUserId(uid string) (*User, error) {
    82  	i, e := strconv.Atoi(uid)
    83  	if e != nil {
    84  		return nil, e
    85  	}
    86  	return lookupUnixUid(i)
    87  }
    88  
    89  func lookupUnixUid(uid int) (*User, error) {
    90  	var pwd C.struct_passwd
    91  	var result *C.struct_passwd
    92  
    93  	buf := alloc(userBuffer)
    94  	defer buf.free()
    95  
    96  	err := retryWithBuffer(buf, func() syscall.Errno {
    97  		// mygetpwuid_r is a wrapper around getpwuid_r to
    98  		// to avoid using uid_t because C.uid_t(uid) for
    99  		// unknown reasons doesn't work on linux.
   100  		return syscall.Errno(C.mygetpwuid_r(C.int(uid),
   101  			&pwd,
   102  			(*C.char)(buf.ptr),
   103  			C.size_t(buf.size),
   104  			&result))
   105  	})
   106  	if err != nil {
   107  		return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
   108  	}
   109  	if result == nil {
   110  		return nil, UnknownUserIdError(uid)
   111  	}
   112  	return buildUser(&pwd), nil
   113  }
   114  
   115  func buildUser(pwd *C.struct_passwd) *User {
   116  	u := &User{
   117  		Uid:      strconv.Itoa(int(pwd.pw_uid)),
   118  		Gid:      strconv.Itoa(int(pwd.pw_gid)),
   119  		Username: C.GoString(pwd.pw_name),
   120  		Name:     C.GoString(pwd.pw_gecos),
   121  		HomeDir:  C.GoString(pwd.pw_dir),
   122  	}
   123  	// The pw_gecos field isn't quite standardized. Some docs
   124  	// say: "It is expected to be a comma separated list of
   125  	// personal data where the first item is the full name of the
   126  	// user."
   127  	if i := strings.Index(u.Name, ","); i >= 0 {
   128  		u.Name = u.Name[:i]
   129  	}
   130  	return u
   131  }
   132  
   133  func currentGroup() (*Group, error) {
   134  	return lookupUnixGid(syscall.Getgid())
   135  }
   136  
   137  func lookupGroup(groupname string) (*Group, error) {
   138  	var grp C.struct_group
   139  	var result *C.struct_group
   140  
   141  	buf := alloc(groupBuffer)
   142  	defer buf.free()
   143  	cname := make([]byte, len(groupname)+1)
   144  	copy(cname, groupname)
   145  
   146  	err := retryWithBuffer(buf, func() syscall.Errno {
   147  		return syscall.Errno(C.mygetgrnam_r((*C.char)(unsafe.Pointer(&cname[0])),
   148  			&grp,
   149  			(*C.char)(buf.ptr),
   150  			C.size_t(buf.size),
   151  			&result))
   152  	})
   153  	if err != nil {
   154  		return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
   155  	}
   156  	if result == nil {
   157  		return nil, UnknownGroupError(groupname)
   158  	}
   159  	return buildGroup(&grp), nil
   160  }
   161  
   162  func lookupGroupId(gid string) (*Group, error) {
   163  	i, e := strconv.Atoi(gid)
   164  	if e != nil {
   165  		return nil, e
   166  	}
   167  	return lookupUnixGid(i)
   168  }
   169  
   170  func lookupUnixGid(gid int) (*Group, error) {
   171  	var grp C.struct_group
   172  	var result *C.struct_group
   173  
   174  	buf := alloc(groupBuffer)
   175  	defer buf.free()
   176  
   177  	err := retryWithBuffer(buf, func() syscall.Errno {
   178  		// mygetgrgid_r is a wrapper around getgrgid_r to
   179  		// to avoid using gid_t because C.gid_t(gid) for
   180  		// unknown reasons doesn't work on linux.
   181  		return syscall.Errno(C.mygetgrgid_r(C.int(gid),
   182  			&grp,
   183  			(*C.char)(buf.ptr),
   184  			C.size_t(buf.size),
   185  			&result))
   186  	})
   187  	if err != nil {
   188  		return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
   189  	}
   190  	if result == nil {
   191  		return nil, UnknownGroupIdError(strconv.Itoa(gid))
   192  	}
   193  	return buildGroup(&grp), nil
   194  }
   195  
   196  func buildGroup(grp *C.struct_group) *Group {
   197  	g := &Group{
   198  		Gid:  strconv.Itoa(int(grp.gr_gid)),
   199  		Name: C.GoString(grp.gr_name),
   200  	}
   201  	return g
   202  }
   203  
   204  type bufferKind C.int
   205  
   206  const (
   207  	userBuffer  = bufferKind(C._SC_GETPW_R_SIZE_MAX)
   208  	groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
   209  )
   210  
   211  func (k bufferKind) initialSize() C.size_t {
   212  	sz := C.sysconf(C.int(k))
   213  	if sz == -1 {
   214  		// DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
   215  		// Additionally, not all Linux systems have it, either. For
   216  		// example, the musl libc returns -1.
   217  		return 1024
   218  	}
   219  	if !isSizeReasonable(int64(sz)) {
   220  		// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
   221  		return maxBufferSize
   222  	}
   223  	return C.size_t(sz)
   224  }
   225  
   226  type memBuffer struct {
   227  	ptr  unsafe.Pointer
   228  	size C.size_t
   229  }
   230  
   231  func alloc(kind bufferKind) *memBuffer {
   232  	sz := kind.initialSize()
   233  	return &memBuffer{
   234  		ptr:  C.malloc(sz),
   235  		size: sz,
   236  	}
   237  }
   238  
   239  func (mb *memBuffer) resize(newSize C.size_t) {
   240  	mb.ptr = C.realloc(mb.ptr, newSize)
   241  	mb.size = newSize
   242  }
   243  
   244  func (mb *memBuffer) free() {
   245  	C.free(mb.ptr)
   246  }
   247  
   248  // retryWithBuffer repeatedly calls f(), increasing the size of the
   249  // buffer each time, until f succeeds, fails with a non-ERANGE error,
   250  // or the buffer exceeds a reasonable limit.
   251  func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
   252  	for {
   253  		errno := f()
   254  		if errno == 0 {
   255  			return nil
   256  		} else if errno != syscall.ERANGE {
   257  			return errno
   258  		}
   259  		newSize := buf.size * 2
   260  		if !isSizeReasonable(int64(newSize)) {
   261  			return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
   262  		}
   263  		buf.resize(newSize)
   264  	}
   265  }
   266  
   267  const maxBufferSize = 1 << 20
   268  
   269  func isSizeReasonable(sz int64) bool {
   270  	return sz > 0 && sz <= maxBufferSize
   271  }