Skip to content

Commit 80edf3d

Browse files
authored
adds HostInfo.NativeArchitecture (#200)
1 parent 489579d commit 80edf3d

File tree

11 files changed

+236
-14
lines changed

11 files changed

+236
-14
lines changed

.changelog/200.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
Adds NativeArchitecture to HostInfo to allow applications to detect whether they are running in emulation.
3+
```

providers/darwin/arch_darwin.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ package darwin
2121

2222
import (
2323
"fmt"
24+
"os"
2425

2526
"golang.org/x/sys/unix"
2627
)
2728

28-
const hardwareMIB = "hw.machine"
29+
const (
30+
hardwareMIB = "hw.machine"
31+
procTranslated = "sysctl.proc_translated"
32+
archIntel = "x86_64"
33+
archApple = "arm64"
34+
)
2935

3036
func Architecture() (string, error) {
3137
arch, err := unix.Sysctl(hardwareMIB)
@@ -35,3 +41,33 @@ func Architecture() (string, error) {
3541

3642
return arch, nil
3743
}
44+
45+
func NativeArchitecture() (string, error) {
46+
processArch, err := Architecture()
47+
if err != nil {
48+
return "", err
49+
}
50+
51+
// https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment
52+
53+
translated, err := unix.SysctlUint32(procTranslated)
54+
if err != nil {
55+
// macos without Rosetta installed doesn't have sysctl.proc_translated
56+
if os.IsNotExist(err) {
57+
return processArch, nil
58+
}
59+
return "", fmt.Errorf("failed to read sysctl.proc_translated: %w", err)
60+
}
61+
62+
var nativeArch string
63+
64+
switch translated {
65+
case 0:
66+
nativeArch = processArch
67+
case 1:
68+
// Rosetta 2 is supported only on Apple silicon
69+
nativeArch = archApple
70+
}
71+
72+
return nativeArch, nil
73+
}

providers/darwin/arch_darwin_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,9 @@ func TestArchitecture(t *testing.T) {
2828
assert.NoError(t, err)
2929
assert.NotEmpty(t, a)
3030
}
31+
32+
func TestNativeArchitecture(t *testing.T) {
33+
a, err := NativeArchitecture()
34+
assert.NoError(t, err)
35+
assert.NotEmpty(t, a)
36+
}

providers/darwin/host_darwin.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ func newHost() (*host, error) {
167167
h := &host{}
168168
r := &reader{}
169169
r.architecture(h)
170+
r.nativeArchitecture(h)
170171
r.bootTime(h)
171172
r.hostname(h)
172173
r.network(h)
@@ -206,6 +207,14 @@ func (r *reader) architecture(h *host) {
206207
h.info.Architecture = v
207208
}
208209

210+
func (r *reader) nativeArchitecture(h *host) {
211+
v, err := NativeArchitecture()
212+
if r.addErr(err) {
213+
return
214+
}
215+
h.info.NativeArchitecture = v
216+
}
217+
209218
func (r *reader) bootTime(h *host) {
210219
v, err := BootTime()
211220
if r.addErr(err) {

providers/linux/arch_linux.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,19 @@ package linux
1919

2020
import (
2121
"fmt"
22+
"os"
23+
"strings"
2224
"syscall"
2325
)
2426

27+
const (
28+
procSysKernelArch = "/proc/sys/kernel/arch"
29+
procVersion = "/proc/version"
30+
archAmd64 = "amd64"
31+
archArm64 = "arm64"
32+
archAarch64 = "aarch64"
33+
)
34+
2535
func Architecture() (string, error) {
2636
var uname syscall.Utsname
2737
if err := syscall.Uname(&uname); err != nil {
@@ -38,3 +48,38 @@ func Architecture() (string, error) {
3848

3949
return string(data), nil
4050
}
51+
52+
func NativeArchitecture() (string, error) {
53+
// /proc/sys/kernel/arch was introduced in Kernel 6.1
54+
// https://www.kernel.org/doc/html/v6.1/admin-guide/sysctl/kernel.html#arch
55+
// It's the same as uname -m, except that for a process running in emulation
56+
// machine returned from syscall reflects the emulated machine, whilst /proc
57+
// filesystem is read as file so its value is not emulated
58+
data, err := os.ReadFile(procSysKernelArch)
59+
if err != nil {
60+
if os.IsNotExist(err) {
61+
// fallback to checking version string for older kernels
62+
version, err := os.ReadFile(procVersion)
63+
if err != nil {
64+
return "", nil
65+
}
66+
67+
versionStr := string(version)
68+
if strings.Contains(versionStr, archAmd64) {
69+
return archAmd64, nil
70+
} else if strings.Contains(versionStr, archArm64) {
71+
// for parity with Architecture() and /proc/sys/kernel/arch
72+
// as aarch64 and arm64 are used interchangeably
73+
return archAarch64, nil
74+
}
75+
return "", nil
76+
}
77+
78+
return "", fmt.Errorf("failed to read kernel arch: %w", err)
79+
}
80+
81+
nativeArch := string(data)
82+
nativeArch = strings.TrimRight(nativeArch, "\n")
83+
84+
return string(data), nil
85+
}

providers/linux/arch_linux_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package linux
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestArchitecture(t *testing.T) {
27+
a, err := Architecture()
28+
assert.NoError(t, err)
29+
assert.NotEmpty(t, a)
30+
}
31+
32+
func TestNativeArchitecture(t *testing.T) {
33+
a, err := NativeArchitecture()
34+
assert.NoError(t, err)
35+
assert.NotEmpty(t, a)
36+
}

providers/linux/host_linux.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ func newHost(fs procFS) (*host, error) {
156156
h := &host{stat: stat, procFS: fs}
157157
r := &reader{}
158158
r.architecture(h)
159+
r.nativeArchitecture(h)
159160
r.bootTime(h)
160161
r.containerized(h)
161162
r.hostname(h)
@@ -197,6 +198,14 @@ func (r *reader) architecture(h *host) {
197198
h.info.Architecture = v
198199
}
199200

201+
func (r *reader) nativeArchitecture(h *host) {
202+
v, err := NativeArchitecture()
203+
if r.addErr(err) {
204+
return
205+
}
206+
h.info.NativeArchitecture = v
207+
}
208+
200209
func (r *reader) bootTime(h *host) {
201210
v, err := bootTime(h.procFS.FS)
202211
if r.addErr(err) {

providers/windows/arch_windows.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,46 @@
1818
package windows
1919

2020
import (
21-
windows "github.com/elastic/go-windows"
21+
"golang.org/x/sys/windows"
22+
23+
gowindows "github.com/elastic/go-windows"
24+
)
25+
26+
const (
27+
imageFileMachineAmd64 = 0x8664
28+
imageFileMachineArm64 = 0xAA64
29+
archIntel = "x86_64"
30+
archArm64 = "arm64"
2231
)
2332

2433
func Architecture() (string, error) {
25-
systemInfo, err := windows.GetNativeSystemInfo()
34+
systemInfo, err := gowindows.GetNativeSystemInfo()
2635
if err != nil {
2736
return "", err
2837
}
2938

3039
return systemInfo.ProcessorArchitecture.String(), nil
3140
}
41+
42+
func NativeArchitecture() (string, error) {
43+
var processMachine, nativeMachine uint16
44+
// the pseudo handle doesn't need to be closed
45+
var currentProcessHandle = windows.CurrentProcess()
46+
47+
err := windows.IsWow64Process2(currentProcessHandle, &processMachine, &nativeMachine)
48+
if err != nil {
49+
return "", err
50+
}
51+
52+
var nativeArch string
53+
54+
switch nativeMachine {
55+
case imageFileMachineAmd64:
56+
// for parity with Architecture() as amd64 and x86_64 are used interchangeably
57+
nativeArch = archIntel
58+
case imageFileMachineArm64:
59+
nativeArch = archArm64
60+
}
61+
62+
return nativeArch, nil
63+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package windows
19+
20+
import (
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestArchitecture(t *testing.T) {
27+
a, err := Architecture()
28+
assert.NoError(t, err)
29+
assert.NotEmpty(t, a)
30+
}
31+
32+
func TestNativeArchitecture(t *testing.T) {
33+
a, err := NativeArchitecture()
34+
assert.NoError(t, err)
35+
assert.NotEmpty(t, a)
36+
}

providers/windows/host_windows.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func newHost() (*host, error) {
102102
h := &host{}
103103
r := &reader{}
104104
r.architecture(h)
105+
r.nativeArchitecture(h)
105106
r.bootTime(h)
106107
r.hostname(h)
107108
r.network(h)
@@ -141,6 +142,14 @@ func (r *reader) architecture(h *host) {
141142
h.info.Architecture = v
142143
}
143144

145+
func (r *reader) nativeArchitecture(h *host) {
146+
v, err := NativeArchitecture()
147+
if r.addErr(err) {
148+
return
149+
}
150+
h.info.NativeArchitecture = v
151+
}
152+
144153
func (r *reader) bootTime(h *host) {
145154
v, err := BootTime()
146155
if r.addErr(err) {

0 commit comments

Comments
 (0)