From be2b25a7c66aec4c2491435a18e1a2e339872592 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 6 Nov 2018 17:56:09 +0100
Subject: [PATCH 1/7] Have a real fixed HostID on Linux
On Linux, most golang programs do not run as root (or at least, they should not),
by default, the kernels uses strict permissions, so most userland programs cannot
read `/sys/class/dmi/id/product_uuid`. However, programs such as Consul are relying
on it to get fixed IDs, instead they have a different ID on each boot.
We propose to use `/etc/machine-id` as fallback https://www.freedesktop.org/software/systemd/man/machine-id.html
In order to fix this, this patch does the following:
- if `/sys/class/dmi/id/product_uuid` can be read, use it for HostID
- else if `/etc/machine-id` exists and has 32 chars, use it and add '-' to have the same format as product_uuid
- finally, if notthing works, use the `kernel.random.boot_id`
This will greatly increase the number of programs having correct behaviour when
those rely on having a fixed HostID.
This will fix the following issues:
- https://github.com/shirou/gopsutil/issues/350
- https://github.com/hashicorp/consul/issues/4741
---
host/host_linux.go | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/host/host_linux.go b/host/host_linux.go
index 7ca5089..2981675 100644
--- a/host/host_linux.go
+++ b/host/host_linux.go
@@ -73,7 +73,9 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
}
sysProductUUID := common.HostSys("class/dmi/id/product_uuid")
+ machineID := common.HostEtc("machine-id")
switch {
+ // When not running as root, lib cannot read the value
case common.PathExists(sysProductUUID):
lines, err := common.ReadLines(sysProductUUID)
if err == nil && len(lines) > 0 && lines[0] != "" {
@@ -81,6 +83,14 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
break
}
fallthrough
+ case common.PathExists(machineID):
+ lines, err := common.ReadLines(machineID)
+ if err == nil && len(lines) > 0 && len(lines[0]) == 32 {
+ st := lines[0]
+ ret.HostID = fmt.Sprintf("%s-%s-%s-%s-%s", st[0:8], st[8:12], st[12:16], st[16:20], st[20:32])
+ break
+ }
+ fallthrough
default:
values, err := common.DoSysctrl("kernel.random.boot_id")
if err == nil && len(values) == 1 && values[0] != "" {
From 890eb805010a6bee6e060ee3f9415189940fb2af Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Wed, 7 Nov 2018 00:08:23 +0100
Subject: [PATCH 2/7] Better comments for fallback on /etc/machine-id
---
host/host_linux.go | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/host/host_linux.go b/host/host_linux.go
index 2981675..2c1ac6b 100644
--- a/host/host_linux.go
+++ b/host/host_linux.go
@@ -75,7 +75,8 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
sysProductUUID := common.HostSys("class/dmi/id/product_uuid")
machineID := common.HostEtc("machine-id")
switch {
- // When not running as root, lib cannot read the value
+ // In order to read this file, needs to be supported by kernel/arch and run as root
+ // so having fallback is important
case common.PathExists(sysProductUUID):
lines, err := common.ReadLines(sysProductUUID)
if err == nil && len(lines) > 0 && lines[0] != "" {
@@ -83,6 +84,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
break
}
fallthrough
+ // Fallback on GNU Linux systems with systemd, readable by everyone
case common.PathExists(machineID):
lines, err := common.ReadLines(machineID)
if err == nil && len(lines) > 0 && len(lines[0]) == 32 {
@@ -91,6 +93,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
break
}
fallthrough
+ // Not stable between reboot, but better than nothing
default:
values, err := common.DoSysctrl("kernel.random.boot_id")
if err == nil && len(values) == 1 && values[0] != "" {
From 6b539051d2b215e59e65ff8a057cce3dd0eefad5 Mon Sep 17 00:00:00 2001
From: Lomanic
Date: Sun, 18 Nov 2018 20:11:55 +0100
Subject: [PATCH 3/7] [process][windows] Use win32 API in process.Children()
instead of slow WMI call
The CreateToolhelp32Snapshot+Process32First+Process32Next combo already
iterates over all processes, so it would be inefficient to enumerate all
processes with process.Processes() and then calling p.Ppid() on each of
them: we just use this combo to get all processes and their ppid in a
single iteration.
This is faster by a factor of 25 compared to the previous WMI call.
---
process/process_windows.go | 34 +++++++++++++++++++++++-----------
1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/process/process_windows.go b/process/process_windows.go
index 82aed37..0d27a99 100644
--- a/process/process_windows.go
+++ b/process/process_windows.go
@@ -250,6 +250,7 @@ func (p *Process) Status() (string, error) {
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
}
+
func (p *Process) Username() (string, error) {
return p.UsernameWithContext(context.Background())
}
@@ -456,22 +457,33 @@ func (p *Process) Children() ([]*Process, error) {
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
- var dst []Win32_Process
- query := wmi.CreateQuery(&dst, fmt.Sprintf("Where ParentProcessId = %d", p.Pid))
- err := common.WMIQueryWithContext(ctx, query, &dst)
- if err != nil {
- return nil, err
+ out := []*Process{}
+ snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(0))
+ if snap == 0 {
+ return out, windows.GetLastError()
+ }
+ defer w32.CloseHandle(snap)
+ var pe32 w32.PROCESSENTRY32
+ pe32.DwSize = uint32(unsafe.Sizeof(pe32))
+ if w32.Process32First(snap, &pe32) == false {
+ return out, windows.GetLastError()
}
- out := []*Process{}
- for _, proc := range dst {
- p, err := NewProcess(int32(proc.ProcessID))
- if err != nil {
- continue
+ if pe32.Th32ParentProcessID == uint32(p.Pid) {
+ p, err := NewProcess(int32(pe32.Th32ProcessID))
+ if err == nil {
+ out = append(out, p)
}
- out = append(out, p)
}
+ for w32.Process32Next(snap, &pe32) {
+ if pe32.Th32ParentProcessID == uint32(p.Pid) {
+ p, err := NewProcess(int32(pe32.Th32ProcessID))
+ if err == nil {
+ out = append(out, p)
+ }
+ }
+ }
return out, nil
}
From 88885374973702ac300ca05215da931efc9ec74a Mon Sep 17 00:00:00 2001
From: Lomanic
Date: Sun, 18 Nov 2018 20:39:18 +0100
Subject: [PATCH 4/7] [disk][linux] Fix #555 Unescape escaped sequences in
fstab path in disk.Partitions
---
disk/disk_linux.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/disk/disk_linux.go b/disk/disk_linux.go
index 1b10a38..3d68936 100644
--- a/disk/disk_linux.go
+++ b/disk/disk_linux.go
@@ -241,7 +241,7 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
fields := strings.Fields(line)
d := PartitionStat{
Device: fields[0],
- Mountpoint: fields[1],
+ Mountpoint: unescapeFstab(fields[1]),
Fstype: fields[2],
Opts: fields[3],
}
From ab57d4a5febe3b0c174545dc3ac1fd42da44f9cb Mon Sep 17 00:00:00 2001
From: Aman Gupta
Date: Tue, 20 Nov 2018 13:04:18 -0800
Subject: [PATCH 5/7] fix minor typo in disk_windows
---
disk/disk_windows.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/disk/disk_windows.go b/disk/disk_windows.go
index 326bc1f..1048cd1 100644
--- a/disk/disk_windows.go
+++ b/disk/disk_windows.go
@@ -15,7 +15,7 @@ var (
procGetDiskFreeSpaceExW = common.Modkernel32.NewProc("GetDiskFreeSpaceExW")
procGetLogicalDriveStringsW = common.Modkernel32.NewProc("GetLogicalDriveStringsW")
procGetDriveType = common.Modkernel32.NewProc("GetDriveTypeW")
- provGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW")
+ procGetVolumeInformation = common.Modkernel32.NewProc("GetVolumeInformationW")
)
var (
@@ -100,7 +100,7 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
lpFileSystemFlags := int64(0)
lpFileSystemNameBuffer := make([]byte, 256)
volpath, _ := windows.UTF16PtrFromString(string(v) + ":/")
- driveret, _, err := provGetVolumeInformation.Call(
+ driveret, _, err := procGetVolumeInformation.Call(
uintptr(unsafe.Pointer(volpath)),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
uintptr(len(lpVolumeNameBuffer)),
From d021425af3371c86da43382d08089030675a35c9 Mon Sep 17 00:00:00 2001
From: Aman Gupta
Date: Tue, 20 Nov 2018 13:08:29 -0800
Subject: [PATCH 6/7] do not ignore A: and B: drives on windows
from https://www.howtogeek.com/122891/what-are-the-windows-a-and-b-drives-used-for/
>if your computer does not have floppy disk drives, you can assign A and B to volumes
---
disk/disk_windows.go | 3 ---
1 file changed, 3 deletions(-)
diff --git a/disk/disk_windows.go b/disk/disk_windows.go
index 326bc1f..242a706 100644
--- a/disk/disk_windows.go
+++ b/disk/disk_windows.go
@@ -83,9 +83,6 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
for _, v := range lpBuffer {
if v >= 65 && v <= 90 {
path := string(v) + ":"
- if path == "A:" || path == "B:" { // skip floppy drives
- continue
- }
typepath, _ := windows.UTF16PtrFromString(path)
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
if typeret == 0 {
From f87d9813da23e81b47bb990e67a010be32324d9a Mon Sep 17 00:00:00 2001
From: Joe Stringer
Date: Fri, 9 Nov 2018 14:54:37 -0800
Subject: [PATCH 7/7] [net][linux] Support socket tables in nested namespaces
Signed-off-by: Joe Stringer
---
net/net_linux.go | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/net_linux.go b/net/net_linux.go
index 616c10a..71842fb 100644
--- a/net/net_linux.go
+++ b/net/net_linux.go
@@ -393,7 +393,11 @@ func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inode
var path string
var connKey string
var ls []connTmp
- path = fmt.Sprintf("%s/net/%s", root, t.filename)
+ if pid == 0 {
+ path = fmt.Sprintf("%s/net/%s", root, t.filename)
+ } else {
+ path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename)
+ }
switch t.family {
case syscall.AF_INET, syscall.AF_INET6:
ls, err = processInet(path, t, inodes, pid)