| // Copyright 2012 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 !go1.8,darwin !go1.8,freebsd openbsd |
| |
| package osext |
| |
| import ( |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "syscall" |
| "unsafe" |
| ) |
| |
| var initCwd, initCwdErr = os.Getwd() |
| |
| func executable() (string, error) { |
| var mib [4]int32 |
| switch runtime.GOOS { |
| case "freebsd": |
| mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1} |
| case "darwin": |
| mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1} |
| case "openbsd": |
| mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */} |
| } |
| |
| n := uintptr(0) |
| // Get length. |
| _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0) |
| if errNum != 0 { |
| return "", errNum |
| } |
| if n == 0 { // This shouldn't happen. |
| return "", nil |
| } |
| buf := make([]byte, n) |
| _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0) |
| if errNum != 0 { |
| return "", errNum |
| } |
| if n == 0 { // This shouldn't happen. |
| return "", nil |
| } |
| |
| var execPath string |
| switch runtime.GOOS { |
| case "openbsd": |
| // buf now contains **argv, with pointers to each of the C-style |
| // NULL terminated arguments. |
| var args []string |
| argv := uintptr(unsafe.Pointer(&buf[0])) |
| Loop: |
| for { |
| argp := *(**[1 << 20]byte)(unsafe.Pointer(argv)) |
| if argp == nil { |
| break |
| } |
| for i := 0; uintptr(i) < n; i++ { |
| // we don't want the full arguments list |
| if string(argp[i]) == " " { |
| break Loop |
| } |
| if argp[i] != 0 { |
| continue |
| } |
| args = append(args, string(argp[:i])) |
| n -= uintptr(i) |
| break |
| } |
| if n < unsafe.Sizeof(argv) { |
| break |
| } |
| argv += unsafe.Sizeof(argv) |
| n -= unsafe.Sizeof(argv) |
| } |
| execPath = args[0] |
| // There is no canonical way to get an executable path on |
| // OpenBSD, so check PATH in case we are called directly |
| if execPath[0] != '/' && execPath[0] != '.' { |
| execIsInPath, err := exec.LookPath(execPath) |
| if err == nil { |
| execPath = execIsInPath |
| } |
| } |
| default: |
| for i, v := range buf { |
| if v == 0 { |
| buf = buf[:i] |
| break |
| } |
| } |
| execPath = string(buf) |
| } |
| |
| var err error |
| // execPath will not be empty due to above checks. |
| // Try to get the absolute path if the execPath is not rooted. |
| if execPath[0] != '/' { |
| execPath, err = getAbs(execPath) |
| if err != nil { |
| return execPath, err |
| } |
| } |
| // For darwin KERN_PROCARGS may return the path to a symlink rather than the |
| // actual executable. |
| if runtime.GOOS == "darwin" { |
| if execPath, err = filepath.EvalSymlinks(execPath); err != nil { |
| return execPath, err |
| } |
| } |
| return execPath, nil |
| } |
| |
| func getAbs(execPath string) (string, error) { |
| if initCwdErr != nil { |
| return execPath, initCwdErr |
| } |
| // The execPath may begin with a "../" or a "./" so clean it first. |
| // Join the two paths, trailing and starting slashes undetermined, so use |
| // the generic Join function. |
| return filepath.Join(initCwd, filepath.Clean(execPath)), nil |
| } |