Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds HostInfo.NativeArchitecture #200

Merged
merged 6 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/200.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
Adds NativeArchitecture to HostInfo to allow applications to detect whether they are running in emulation.
```
42 changes: 41 additions & 1 deletion providers/darwin/arch_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ package darwin

import (
"fmt"
"os"

"golang.org/x/sys/unix"
)

const hardwareMIB = "hw.machine"
const (
hardwareMIB = "hw.machine"
procTranslated = "sysctl.proc_translated"
archIntel = "x86_64"
archApple = "arm64"
)

func Architecture() (string, error) {
arch, err := unix.Sysctl(hardwareMIB)
Expand All @@ -35,3 +41,37 @@ func Architecture() (string, error) {

return arch, nil
}

func NativeArchitecture() (string, error) {
processArch, err := Architecture()
if err != nil {
return "", err
}

// https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment

data, err := unix.Sysctl(procTranslated)
if err != nil {
// macos without Rosetta installed doesn't have sysctl.proc_translated
if os.IsNotExist(err) {
return processArch, nil
}
return "", fmt.Errorf("failed to read sysctl.proc_translated: %w", err)
}

nativeArch := ""
translated := data[0]

switch translated {
case 0:
nativeArch = processArch
case 1:
// Rosetta 2 is supported only on Apple silicon
if processArch == archIntel {
nativeArch = archApple
}
default:
}

return nativeArch, nil
andrewkroh marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 6 additions & 0 deletions providers/darwin/arch_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ func TestArchitecture(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, a)
}

func TestNativeArchitecture(t *testing.T) {
a, err := NativeArchitecture()
assert.NoError(t, err)
assert.NotEmpty(t, a)
}
9 changes: 9 additions & 0 deletions providers/darwin/host_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ func newHost() (*host, error) {
h := &host{}
r := &reader{}
r.architecture(h)
r.nativeArchitecture(h)
r.bootTime(h)
r.hostname(h)
r.network(h)
Expand Down Expand Up @@ -206,6 +207,14 @@ func (r *reader) architecture(h *host) {
h.info.Architecture = v
}

func (r *reader) nativeArchitecture(h *host) {
v, err := NativeArchitecture()
if r.addErr(err) {
return
}
h.info.NativeArchitecture = v
}

func (r *reader) bootTime(h *host) {
v, err := BootTime()
if r.addErr(err) {
Expand Down
40 changes: 40 additions & 0 deletions providers/linux/arch_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@ package linux

import (
"fmt"
"os"
"strings"
"syscall"
)

const (
procSysKernelArch = "/proc/sys/kernel/arch"
andrewkroh marked this conversation as resolved.
Show resolved Hide resolved
procVersion = "/proc/version"
archAmd64 = "amd64"
archArm64 = "arm64"
archAarch64 = "aarch64"
)

func Architecture() (string, error) {
var uname syscall.Utsname
if err := syscall.Uname(&uname); err != nil {
Expand All @@ -38,3 +48,33 @@ func Architecture() (string, error) {

return string(data), nil
}

func NativeArchitecture() (string, error) {
data, err := os.ReadFile(procSysKernelArch)
if err != nil {
if os.IsNotExist(err) {
andrewkroh marked this conversation as resolved.
Show resolved Hide resolved
// fallback to checking version string
version, err := os.ReadFile(procVersion)
if err != nil {
return "", nil
}

versionStr := string(version)
if strings.Contains(versionStr, archAmd64) {
return archAmd64, nil
} else if strings.Contains(versionStr, archArm64) {
// for parity with Architecture() and /proc/sys/kernel/arch
// as aarch64 and arm64 are used interchangeably
return archAarch64, nil
}
return "", nil
}

return "", fmt.Errorf("failed to read kernel arch: %w", err)
}

nativeArch := string(data)
nativeArch = strings.TrimRight(nativeArch, "\n")

return string(data), nil
}
36 changes: 36 additions & 0 deletions providers/linux/arch_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package linux

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestArchitecture(t *testing.T) {
a, err := Architecture()
assert.NoError(t, err)
assert.NotEmpty(t, a)
}

func TestNativeArchitecture(t *testing.T) {
a, err := NativeArchitecture()
assert.NoError(t, err)
assert.NotEmpty(t, a)
}
9 changes: 9 additions & 0 deletions providers/linux/host_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func newHost(fs procFS) (*host, error) {
h := &host{stat: stat, procFS: fs}
r := &reader{}
r.architecture(h)
r.nativeArchitecture(h)
r.bootTime(h)
r.containerized(h)
r.hostname(h)
Expand Down Expand Up @@ -197,6 +198,14 @@ func (r *reader) architecture(h *host) {
h.info.Architecture = v
}

func (r *reader) nativeArchitecture(h *host) {
v, err := NativeArchitecture()
if r.addErr(err) {
return
}
h.info.NativeArchitecture = v
}

func (r *reader) bootTime(h *host) {
v, err := bootTime(h.procFS.FS)
if r.addErr(err) {
Expand Down
37 changes: 35 additions & 2 deletions providers/windows/arch_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,47 @@
package windows

import (
windows "github.com/elastic/go-windows"
"golang.org/x/sys/windows"

go_windows "github.com/elastic/go-windows"
intxgo marked this conversation as resolved.
Show resolved Hide resolved
)

const (
imageFileMachineAmd64 = 0x8664
imageFileMachineArm64 = 0xAA64
archIntel = "x86_64"
archArm64 = "arm64"
)

func Architecture() (string, error) {
systemInfo, err := windows.GetNativeSystemInfo()
systemInfo, err := go_windows.GetNativeSystemInfo()
if err != nil {
return "", err
}

return systemInfo.ProcessorArchitecture.String(), nil
}

func NativeArchitecture() (string, error) {
var processMachine, nativeMachine uint16
// the pseudo handle doesn't need to be closed
var currentProcessHandle = windows.CurrentProcess()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to close this handle after we're done?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, as the explicit comment says. If you're not convinced check the manual

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, we've had issues with leaving handles open, so I'm a tad paranoid...


err := windows.IsWow64Process2(currentProcessHandle, &processMachine, &nativeMachine)
if err != nil {
return "", err
}

nativeArch := ""
intxgo marked this conversation as resolved.
Show resolved Hide resolved

switch nativeMachine {
case imageFileMachineAmd64:
// for parity with Architecture() as amd64 and x86_64 are used interchangeably
nativeArch = archIntel
case imageFileMachineArm64:
nativeArch = archArm64
default:
intxgo marked this conversation as resolved.
Show resolved Hide resolved
}

return nativeArch, nil
}
36 changes: 36 additions & 0 deletions providers/windows/arch_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package windows

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestArchitecture(t *testing.T) {
a, err := Architecture()
assert.NoError(t, err)
assert.NotEmpty(t, a)
}

func TestNativeArchitecture(t *testing.T) {
a, err := NativeArchitecture()
assert.NoError(t, err)
assert.NotEmpty(t, a)
}
9 changes: 9 additions & 0 deletions providers/windows/host_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func newHost() (*host, error) {
h := &host{}
r := &reader{}
r.architecture(h)
r.nativeArchitecture(h)
r.bootTime(h)
r.hostname(h)
r.network(h)
Expand Down Expand Up @@ -141,6 +142,14 @@ func (r *reader) architecture(h *host) {
h.info.Architecture = v
}

func (r *reader) nativeArchitecture(h *host) {
v, err := NativeArchitecture()
if r.addErr(err) {
return
}
h.info.NativeArchitecture = v
}

func (r *reader) bootTime(h *host) {
v, err := BootTime()
if r.addErr(err) {
Expand Down
23 changes: 12 additions & 11 deletions types/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,18 @@ type VMStat interface {

// HostInfo contains basic host information.
type HostInfo struct {
Architecture string `json:"architecture"` // Hardware architecture (e.g. x86_64, arm, ppc, mips).
BootTime time.Time `json:"boot_time"` // Host boot time.
Containerized *bool `json:"containerized,omitempty"` // Is the process containerized.
Hostname string `json:"name"` // Hostname, lowercased.
IPs []string `json:"ip,omitempty"` // List of all IPs.
KernelVersion string `json:"kernel_version"` // Kernel version.
MACs []string `json:"mac"` // List of MAC addresses.
OS *OSInfo `json:"os"` // OS information.
Timezone string `json:"timezone"` // System timezone.
TimezoneOffsetSec int `json:"timezone_offset_sec"` // Timezone offset (seconds from UTC).
UniqueID string `json:"id,omitempty"` // Unique ID of the host (optional).
Architecture string `json:"architecture"` // Process hardware architecture (e.g. x86_64, arm, ppc, mips).
NativeArchitecture string `json:"native_architecture"` // Native OS hardware architecture (e.g. x86_64, arm, ppc, mips).
BootTime time.Time `json:"boot_time"` // Host boot time.
Containerized *bool `json:"containerized,omitempty"` // Is the process containerized.
Hostname string `json:"name"` // Hostname, lowercased.
IPs []string `json:"ip,omitempty"` // List of all IPs.
KernelVersion string `json:"kernel_version"` // Kernel version.
MACs []string `json:"mac"` // List of MAC addresses.
OS *OSInfo `json:"os"` // OS information.
Timezone string `json:"timezone"` // System timezone.
TimezoneOffsetSec int `json:"timezone_offset_sec"` // Timezone offset (seconds from UTC).
UniqueID string `json:"id,omitempty"` // Unique ID of the host (optional).
}

// Uptime returns the system uptime
Expand Down
Loading