...
Run Format

Source file src/os/user/cgo_lookup_unix.go

Documentation: os/user

  // 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 <grp.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);
  }
  
  static int mygetgrgid_r(int gid, struct group *grp,
  	char *buf, size_t buflen, struct group **result) {
   return getgrgid_r(gid, grp, buf, buflen, result);
  }
  
  static int mygetgrnam_r(const char *name, struct group *grp,
  	char *buf, size_t buflen, struct group **result) {
   return getgrnam_r(name, grp, buf, buflen, result);
  }
  */
  import "C"
  
  func current() (*User, error) {
  	return lookupUnixUid(syscall.Getuid())
  }
  
  func lookupUser(username string) (*User, error) {
  	var pwd C.struct_passwd
  	var result *C.struct_passwd
  	nameC := C.CString(username)
  	defer C.free(unsafe.Pointer(nameC))
  
  	buf := alloc(userBuffer)
  	defer buf.free()
  
  	err := retryWithBuffer(buf, func() syscall.Errno {
  		// 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.
  		return syscall.Errno(C.mygetpwnam_r(nameC,
  			&pwd,
  			(*C.char)(buf.ptr),
  			C.size_t(buf.size),
  			&result))
  	})
  	if err != nil {
  		return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
  	}
  	if result == nil {
  		return nil, UnknownUserError(username)
  	}
  	return buildUser(&pwd), err
  }
  
  func lookupUserId(uid string) (*User, error) {
  	i, e := strconv.Atoi(uid)
  	if e != nil {
  		return nil, e
  	}
  	return lookupUnixUid(i)
  }
  
  func lookupUnixUid(uid int) (*User, error) {
  	var pwd C.struct_passwd
  	var result *C.struct_passwd
  
  	buf := alloc(userBuffer)
  	defer buf.free()
  
  	err := retryWithBuffer(buf, func() syscall.Errno {
  		// 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.
  		return syscall.Errno(C.mygetpwuid_r(C.int(uid),
  			&pwd,
  			(*C.char)(buf.ptr),
  			C.size_t(buf.size),
  			&result))
  	})
  	if err != nil {
  		return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
  	}
  	if result == nil {
  		return nil, UnknownUserIdError(uid)
  	}
  	return buildUser(&pwd), nil
  }
  
  func buildUser(pwd *C.struct_passwd) *User {
  	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
  }
  
  func currentGroup() (*Group, error) {
  	return lookupUnixGid(syscall.Getgid())
  }
  
  func lookupGroup(groupname string) (*Group, error) {
  	var grp C.struct_group
  	var result *C.struct_group
  
  	buf := alloc(groupBuffer)
  	defer buf.free()
  	cname := C.CString(groupname)
  	defer C.free(unsafe.Pointer(cname))
  
  	err := retryWithBuffer(buf, func() syscall.Errno {
  		return syscall.Errno(C.mygetgrnam_r(cname,
  			&grp,
  			(*C.char)(buf.ptr),
  			C.size_t(buf.size),
  			&result))
  	})
  	if err != nil {
  		return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
  	}
  	if result == nil {
  		return nil, UnknownGroupError(groupname)
  	}
  	return buildGroup(&grp), nil
  }
  
  func lookupGroupId(gid string) (*Group, error) {
  	i, e := strconv.Atoi(gid)
  	if e != nil {
  		return nil, e
  	}
  	return lookupUnixGid(i)
  }
  
  func lookupUnixGid(gid int) (*Group, error) {
  	var grp C.struct_group
  	var result *C.struct_group
  
  	buf := alloc(groupBuffer)
  	defer buf.free()
  
  	err := retryWithBuffer(buf, func() syscall.Errno {
  		// mygetgrgid_r is a wrapper around getgrgid_r to
  		// to avoid using gid_t because C.gid_t(gid) for
  		// unknown reasons doesn't work on linux.
  		return syscall.Errno(C.mygetgrgid_r(C.int(gid),
  			&grp,
  			(*C.char)(buf.ptr),
  			C.size_t(buf.size),
  			&result))
  	})
  	if err != nil {
  		return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
  	}
  	if result == nil {
  		return nil, UnknownGroupIdError(strconv.Itoa(gid))
  	}
  	return buildGroup(&grp), nil
  }
  
  func buildGroup(grp *C.struct_group) *Group {
  	g := &Group{
  		Gid:  strconv.Itoa(int(grp.gr_gid)),
  		Name: C.GoString(grp.gr_name),
  	}
  	return g
  }
  
  type bufferKind C.int
  
  const (
  	userBuffer  = bufferKind(C._SC_GETPW_R_SIZE_MAX)
  	groupBuffer = bufferKind(C._SC_GETGR_R_SIZE_MAX)
  )
  
  func (k bufferKind) initialSize() C.size_t {
  	sz := C.sysconf(C.int(k))
  	if sz == -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.
  		return 1024
  	}
  	if !isSizeReasonable(int64(sz)) {
  		// Truncate.  If this truly isn't enough, retryWithBuffer will error on the first run.
  		return maxBufferSize
  	}
  	return C.size_t(sz)
  }
  
  type memBuffer struct {
  	ptr  unsafe.Pointer
  	size C.size_t
  }
  
  func alloc(kind bufferKind) *memBuffer {
  	sz := kind.initialSize()
  	return &memBuffer{
  		ptr:  C.malloc(sz),
  		size: sz,
  	}
  }
  
  func (mb *memBuffer) resize(newSize C.size_t) {
  	mb.ptr = C.realloc(mb.ptr, newSize)
  	mb.size = newSize
  }
  
  func (mb *memBuffer) free() {
  	C.free(mb.ptr)
  }
  
  // retryWithBuffer repeatedly calls f(), increasing the size of the
  // buffer each time, until f succeeds, fails with a non-ERANGE error,
  // or the buffer exceeds a reasonable limit.
  func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
  	for {
  		errno := f()
  		if errno == 0 {
  			return nil
  		} else if errno != syscall.ERANGE {
  			return errno
  		}
  		newSize := buf.size * 2
  		if !isSizeReasonable(int64(newSize)) {
  			return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
  		}
  		buf.resize(newSize)
  	}
  }
  
  const maxBufferSize = 1 << 20
  
  func isSizeReasonable(sz int64) bool {
  	return sz > 0 && sz <= maxBufferSize
  }
  

View as plain text