osext_sysctl.go raw

   1  // Copyright 2012 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 !go1.8,darwin !go1.8,freebsd openbsd
   6  
   7  package osext
   8  
   9  import (
  10  	"os"
  11  	"os/exec"
  12  	"path/filepath"
  13  	"runtime"
  14  	"syscall"
  15  	"unsafe"
  16  )
  17  
  18  var initCwd, initCwdErr = os.Getwd()
  19  
  20  func executable() (string, error) {
  21  	var mib [4]int32
  22  	switch runtime.GOOS {
  23  	case "freebsd":
  24  		mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
  25  	case "darwin":
  26  		mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
  27  	case "openbsd":
  28  		mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
  29  	}
  30  
  31  	n := uintptr(0)
  32  	// Get length.
  33  	_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
  34  	if errNum != 0 {
  35  		return "", errNum
  36  	}
  37  	if n == 0 { // This shouldn't happen.
  38  		return "", nil
  39  	}
  40  	buf := make([]byte, n)
  41  	_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
  42  	if errNum != 0 {
  43  		return "", errNum
  44  	}
  45  	if n == 0 { // This shouldn't happen.
  46  		return "", nil
  47  	}
  48  
  49  	var execPath string
  50  	switch runtime.GOOS {
  51  	case "openbsd":
  52  		// buf now contains **argv, with pointers to each of the C-style
  53  		// NULL terminated arguments.
  54  		var args []string
  55  		argv := uintptr(unsafe.Pointer(&buf[0]))
  56  	Loop:
  57  		for {
  58  			argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
  59  			if argp == nil {
  60  				break
  61  			}
  62  			for i := 0; uintptr(i) < n; i++ {
  63  				// we don't want the full arguments list
  64  				if string(argp[i]) == " " {
  65  					break Loop
  66  				}
  67  				if argp[i] != 0 {
  68  					continue
  69  				}
  70  				args = append(args, string(argp[:i]))
  71  				n -= uintptr(i)
  72  				break
  73  			}
  74  			if n < unsafe.Sizeof(argv) {
  75  				break
  76  			}
  77  			argv += unsafe.Sizeof(argv)
  78  			n -= unsafe.Sizeof(argv)
  79  		}
  80  		execPath = args[0]
  81  		// There is no canonical way to get an executable path on
  82  		// OpenBSD, so check PATH in case we are called directly
  83  		if execPath[0] != '/' && execPath[0] != '.' {
  84  			execIsInPath, err := exec.LookPath(execPath)
  85  			if err == nil {
  86  				execPath = execIsInPath
  87  			}
  88  		}
  89  	default:
  90  		for i, v := range buf {
  91  			if v == 0 {
  92  				buf = buf[:i]
  93  				break
  94  			}
  95  		}
  96  		execPath = string(buf)
  97  	}
  98  
  99  	var err error
 100  	// execPath will not be empty due to above checks.
 101  	// Try to get the absolute path if the execPath is not rooted.
 102  	if execPath[0] != '/' {
 103  		execPath, err = getAbs(execPath)
 104  		if err != nil {
 105  			return execPath, err
 106  		}
 107  	}
 108  	// For darwin KERN_PROCARGS may return the path to a symlink rather than the
 109  	// actual executable.
 110  	if runtime.GOOS == "darwin" {
 111  		if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
 112  			return execPath, err
 113  		}
 114  	}
 115  	return execPath, nil
 116  }
 117  
 118  func getAbs(execPath string) (string, error) {
 119  	if initCwdErr != nil {
 120  		return execPath, initCwdErr
 121  	}
 122  	// The execPath may begin with a "../" or a "./" so clean it first.
 123  	// Join the two paths, trailing and starting slashes undetermined, so use
 124  	// the generic Join function.
 125  	return filepath.Join(initCwd, filepath.Clean(execPath)), nil
 126  }
 127