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