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)