-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathos.go
More file actions
138 lines (126 loc) · 4.2 KB
/
os.go
File metadata and controls
138 lines (126 loc) · 4.2 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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package command
import (
"context"
"strings"
)
// OS detects the operating system of the given machine by probing it with
// various commands. It returns normalized GOOS values: "linux", "darwin",
// "freebsd", "openbsd", "netbsd", "dragonfly", "windows", or "unknown".
//
// If m implements OSMachine, OS() calls m.OS(ctx) and uses the returned
// value. If m.OS(ctx) returns an empty string, OS falls back to probing
// with commands.
//
// OS automatically pierces through Shell layers by trying each probe command
// first on the given machine, then unshelling and retrying if the command
// is not found. This allows Shell handlers to override probe commands for
// testing while still falling back to the underlying system.
func OS(ctx context.Context, m Machine) string {
if osm, ok := m.(OSMachine); ok {
if os := osm.OS(ctx); os != "" {
return os
}
}
return detectOS(ctx, m)
}
func detectOS(ctx context.Context, m Machine) string {
out, err := probeRead(ctx, m, "uname", "-s")
if err == nil {
os := strings.TrimSpace(strings.ToLower(out))
if strings.Contains(os, "msys_nt") {
return "windows"
}
return os
}
out, err = probeRead(ctx, m, "cmd", "/c", "ver")
if err == nil && strings.Contains(strings.ToLower(out), "windows") {
return "windows"
}
return "unknown"
}
// Arch detects the architecture of the given machine by probing it with
// various commands. It returns normalized GOARCH values: "amd64", "arm64",
// "386", "arm", or "unknown".
//
// If m implements ArchMachine, Arch() calls m.Arch(ctx) and uses the returned
// value. If m.Arch(ctx) returns an empty string, Arch falls back to probing
// with commands.
//
// Arch automatically pierces through Shell layers by trying each probe command
// first on the given machine, then unshelling and retrying if the command
// is not found. This allows Shell handlers to override probe commands for
// testing while still falling back to the underlying system.
func Arch(ctx context.Context, m Machine) string {
if archm, ok := m.(ArchMachine); ok {
if arch := archm.Arch(ctx); arch != "" {
return arch
}
}
return detectArch(ctx, m)
}
func detectArch(ctx context.Context, m Machine) string {
// On Darwin/macOS only, use uname -v to detect architecture from kernel.
//
// We cannot rely on uname -m because of how macOS handles universal
// binaries and Rosetta translation. When a process runs under Rosetta
// (x86_64 translation), child processes inherit the "translated" flag
// even if the child binary is native arm64. This causes macOS to
// preferentially run the x86_64 slice of universal binaries like uname.
//
// For example, when an arm64 Go binary is spawned from an x86_64 shell,
// the Go process inherits the translated flag. When it runs uname -m,
// macOS executes the x86_64 slice, returning "x86_64" even though the
// hardware is arm64.
//
// The kernel version string (from uname -v) always reflects the actual
// hardware architecture (e.g., "RELEASE_ARM64_T8132"), regardless of
// which process architecture is running or which binary slice executes.
//
// Only use this method on Darwin to avoid false positives on other OSes.
if OS(ctx, m) == "darwin" {
output, err := probeRead(ctx, m, "uname", "-v")
if err == nil {
version := strings.ToUpper(output)
if strings.Contains(version, "ARM64") {
return "arm64"
}
if strings.Contains(version, "X86_64") {
return "amd64"
}
}
}
out, err := probeRead(ctx, m, "uname", "-m")
if err == nil {
return mapArchitecture(strings.TrimSpace(out))
}
out, err = probeRead(ctx, m,
"cmd", "/c", "echo %PROCESSOR_ARCHITECTURE%",
)
if err == nil {
arch := strings.TrimSpace(out)
if arch != "%PROCESSOR_ARCHITECTURE%" {
return mapArchitecture(arch)
}
}
out, err = probeRead(ctx, m,
"powershell", "Write-Output", "$env:PROCESSOR_ARCHITECTURE",
)
if err == nil {
return mapArchitecture(strings.TrimSpace(out))
}
return "unknown"
}
func mapArchitecture(arch string) string {
switch strings.ToLower(arch) {
case "x86_64", "x86-64", "x64", "amd64":
return "amd64"
case "aarch64", "arm64":
return "arm64"
case "i386", "i486", "i586", "i686", "x86":
return "386"
case "armv7l", "armv6l", "arm":
return "arm"
default:
return "unknown"
}
}