Repository navigation
Expand file tree
/
Copy pathdetect.go
More file actions
122 lines (110 loc) · 3.05 KB
/
Copy pathdetect.go
File metadata and controls
122 lines (110 loc) · 3.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"errors"
"os"
"path/filepath"
"strings"
"unsafe"
"golang.org/x/sys/windows"
)
const (
shellIDNu = "nu"
shellIDPwsh = "pwsh"
shellIDPowerShell = "powershell"
shellIDCmd = "cmd"
shellIDBash = "bash"
shellIDFish = "fish"
)
// shellIDFromExe maps a shell executable name or path to a shell ID, or ""
// if it isn't a shell vsenv knows.
func shellIDFromExe(exe string) string {
switch strings.ToLower(filepath.Base(exe)) {
case "nu.exe":
return shellIDNu
case "pwsh.exe":
return shellIDPwsh
case "powershell.exe":
return shellIDPowerShell
case "cmd.exe":
return shellIDCmd
case "bash.exe", "sh.exe":
return shellIDBash
case "fish.exe":
return shellIDFish
}
return ""
}
// detectParentShell identifies the shell that launched vsenv, returning its ID
// and executable. The executable is a full path when it can be determined, so
// the same binary can be relaunched; otherwise it's a bare file name or empty.
func detectParentShell() (string, string) {
if path, err := parentExePath(); err == nil {
if id := shellIDFromExe(path); id != "" {
return id, path
}
}
if os.Getenv("NU_VERSION") != "" {
return shellIDNu, ""
}
return shellIDPowerShell, ""
}
// parentExePath returns the full path of the parent process's executable,
// or just its file name if the process can't be opened.
func parentExePath() (string, error) {
pid := uint32(os.Getpid()) //nolint:gosec // Windows PIDs fit in uint32
snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
if err != nil {
return "", err
}
defer func() { _ = windows.CloseHandle(snap) }()
var entry windows.ProcessEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
var ppid uint32
if firstErr := windows.Process32First(snap, &entry); firstErr != nil {
return "", firstErr
}
for {
if entry.ProcessID == pid {
ppid = entry.ParentProcessID
break
}
if nextErr := windows.Process32Next(snap, &entry); nextErr != nil {
return "", nextErr
}
}
if ppid == 0 {
return "", errors.New("parent process not found")
}
snap2, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
if err != nil {
return "", err
}
defer func() { _ = windows.CloseHandle(snap2) }()
if firstErr := windows.Process32First(snap2, &entry); firstErr != nil {
return "", firstErr
}
for {
if entry.ProcessID == ppid {
if path, pathErr := processImagePath(ppid); pathErr == nil {
return path, nil
}
return windows.UTF16ToString(entry.ExeFile[:]), nil
}
if nextErr := windows.Process32Next(snap2, &entry); nextErr != nil {
return "", nextErr
}
}
}
func processImagePath(pid uint32) (string, error) {
h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
if err != nil {
return "", err
}
defer func() { _ = windows.CloseHandle(h) }()
size := uint32(windows.MAX_LONG_PATH)
buf := make([]uint16, size)
if queryErr := windows.QueryFullProcessImageName(h, 0, &buf[0], &size); queryErr != nil {
return "", queryErr
}
return windows.UTF16ToString(buf[:size]), nil
}