github.com/dvln/osext@v0.0.0-20161024010124-4ddb9cd3c863/osext_sysctl.go (about)

     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 darwin 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  }