github.com/TaceyWong/ctools@v0.1.1-0.20201221084458-4d7a8190b2ac/pkg/osext/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 !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 }