From 613ada987d2da5d1bcbbd7d4fd40ef0ab69132c2 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Thu, 11 Aug 2016 00:48:24 -0700 Subject: [PATCH 1/4] Add DoSysctrl() to Linux's common utilities. --- internal/common/common_linux.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/internal/common/common_linux.go b/internal/common/common_linux.go index 06fe4cbb1..9568106fb 100644 --- a/internal/common/common_linux.go +++ b/internal/common/common_linux.go @@ -2,7 +2,31 @@ package common -import "os" +import ( + "os" + "os/exec" + "strings" +) + +func DoSysctrl(mib string) ([]string, error) { + err := os.Setenv("LC_ALL", "C") + if err != nil { + return []string{}, err + } + sysctl, err := exec.LookPath("/sbin/sysctl") + if err != nil { + return []string{}, err + } + out, err := exec.Command(sysctl, "-n", mib).Output() + if err != nil { + return []string{}, err + } + v := strings.Replace(string(out), "{ ", "", 1) + v = strings.Replace(string(v), " }", "", 1) + values := strings.Fields(string(v)) + + return values, nil +} func NumProcs() (uint64, error) { f, err := os.Open(HostProc()) From e4f857a9ca183ceba11d366ff2ff8eb4e70a5e5d Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Thu, 11 Aug 2016 00:49:29 -0700 Subject: [PATCH 2/4] Fix a linter issue: s/TimeoutErr/ErrTimeout/g --- internal/common/common.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/common/common.go b/internal/common/common.go index 42cbaf92a..f0ea6dd36 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -26,7 +26,7 @@ import ( var ( Timeout = 3 * time.Second - TimeoutErr = errors.New("Command timed out.") + ErrTimeout = errors.New("Command timed out.") ) type Invoker interface { @@ -338,6 +338,6 @@ func WaitTimeout(c *exec.Cmd, timeout time.Duration) error { } // wait for the command to return after killing it <-done - return TimeoutErr + return ErrTimeout } } From 59094cd5b7d13a0165f0a935bb4a83d4ff99d206 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Thu, 11 Aug 2016 00:51:07 -0700 Subject: [PATCH 3/4] Add HostID to the Host InfoStat struct returned from host.Info(). On supported hosts the value returned is a UUID (case preserving from the value of the underlying OS). For Linux this is generated once, randomly per boot. For FreeBSD and Darwin this is a more durable value that should persist across reboots. --- host/host.go | 2 +- host/host_darwin.go | 5 +++++ host/host_freebsd.go | 5 +++++ host/host_linux.go | 5 +++++ host/host_test.go | 3 ++- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/host/host.go b/host/host.go index 1a6545b99..548e92e82 100644 --- a/host/host.go +++ b/host/host.go @@ -25,7 +25,7 @@ type InfoStat struct { PlatformVersion string `json:"platformVersion"` VirtualizationSystem string `json:"virtualizationSystem"` VirtualizationRole string `json:"virtualizationRole"` // guest or host - + HostID string `json:"hostid"` // ex: uuid } type UserStat struct { diff --git a/host/host_darwin.go b/host/host_darwin.go index 199ea61e5..43c49b2d1 100644 --- a/host/host_darwin.go +++ b/host/host_darwin.go @@ -56,6 +56,11 @@ func Info() (*InfoStat, error) { ret.Procs = uint64(len(procs)) } + values, err := common.DoSysctrl("kern.uuid") + if err == nil && len(values) == 1 && values[0] != "" { + ret.HostID = values[0] + } + return ret, nil } diff --git a/host/host_freebsd.go b/host/host_freebsd.go index 2159ae874..58a06c298 100644 --- a/host/host_freebsd.go +++ b/host/host_freebsd.go @@ -59,6 +59,11 @@ func Info() (*InfoStat, error) { ret.Procs = uint64(len(procs)) } + values, err := common.DoSysctrl("kern.hostuuid") + if err == nil && len(values) == 1 && values[0] != "" { + ret.HostID = values[0] + } + return ret, nil } diff --git a/host/host_linux.go b/host/host_linux.go index 3d73242d1..a9f374963 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -61,6 +61,11 @@ func Info() (*InfoStat, error) { ret.Procs = numProcs } + values, err := common.DoSysctrl("kernel.random.boot_id") + if err == nil && len(values) == 1 && values[0] != "" { + ret.HostID = values[0] + } + return ret, nil } diff --git a/host/host_test.go b/host/host_test.go index d73d1879f..18832abf3 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -66,8 +66,9 @@ func TestHostInfoStat_String(t *testing.T) { OS: "linux", Platform: "ubuntu", BootTime: 1447040000, + HostID: "edfd25ff-3c9c-b1a4-e660-bd826495ad35", } - e := `{"hostname":"test","uptime":3000,"bootTime":1447040000,"procs":100,"os":"linux","platform":"ubuntu","platformFamily":"","platformVersion":"","virtualizationSystem":"","virtualizationRole":""}` + e := `{"hostname":"test","uptime":3000,"bootTime":1447040000,"procs":100,"os":"linux","platform":"ubuntu","platformFamily":"","platformVersion":"","virtualizationSystem":"","virtualizationRole":"","hostid":"edfd25ff-3c9c-b1a4-e660-bd826495ad35"}` if e != fmt.Sprintf("%v", v) { t.Errorf("HostInfoStat string is invalid: %v", v) } From d490d634ca0cff0150ce1f6d4385ce98c01881a6 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Thu, 11 Aug 2016 01:29:46 -0700 Subject: [PATCH 4/4] On Linux, attempt to read from /sys/class/dmi/id/product_uuid first before falling back to kernel.random.boot_id. `/sys/class/dmi/id/product_uuid` is still managed by permissions, so for root-run processes where `/sys/class/dmi/id/product_uuid` is available, the host's UUID will be used instead, otherwise the UUID from kernel.random.boot_id will be used instead. --- host/host_linux.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index a9f374963..838609cec 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -61,9 +61,20 @@ func Info() (*InfoStat, error) { ret.Procs = numProcs } - values, err := common.DoSysctrl("kernel.random.boot_id") - if err == nil && len(values) == 1 && values[0] != "" { - ret.HostID = values[0] + sysProductUUID := common.HostSys("class/dmi/id/product_uuid") + switch { + case common.PathExists(sysProductUUID): + lines, err := common.ReadLines(sysProductUUID) + if err == nil && len(lines) > 0 && lines[0] != "" { + ret.HostID = lines[0] + break + } + fallthrough + default: + values, err := common.DoSysctrl("kernel.random.boot_id") + if err == nil && len(values) == 1 && values[0] != "" { + ret.HostID = values[0] + } } return ret, nil