From c2c789350901a4ef2f65f1e51e7d39d287662791 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Tue, 12 Mar 2019 21:29:10 +0100 Subject: [PATCH 01/38] fix logic error + little refactor Fix: get cptime of n-th cpu when `percpu` instead of the average. While there, rearrange the last if statement to make the code a bit more homogeneous. --- cpu/cpu_openbsd.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpu/cpu_openbsd.go b/cpu/cpu_openbsd.go index 0177626..9ede291 100644 --- a/cpu/cpu_openbsd.go +++ b/cpu/cpu_openbsd.go @@ -85,9 +85,9 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { var cpuTimes = make([]int64, CPUStates) var mib []int32 if percpu { - mib = []int32{CTLKern, KernCptime} - } else { mib = []int32{CTLKern, KernCptime2, int32(i)} + } else { + mib = []int32{CTLKern, KernCptime} } buf, _, err := common.CallSyscall(mib) if err != nil { @@ -106,10 +106,10 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec, Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec, } - if !percpu { - c.CPU = "cpu-total" - } else { + if percpu { c.CPU = fmt.Sprintf("cpu%d", i) + } else { + c.CPU = "cpu-total" } ret = append(ret, c) } From 12d92847cfd6a4bd572925239fdfa7d290e2d54d Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Wed, 13 Mar 2019 11:03:17 +0100 Subject: [PATCH 02/38] Get hw.ncpuonline without unix.SysctlUint32 unix.Sysctl always return an error when asking for hw.ncpuonline, so revert to a direct unix.Syscall6 to get the cpu count. --- cpu/cpu_openbsd.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cpu/cpu_openbsd.go b/cpu/cpu_openbsd.go index 9ede291..d224a1b 100644 --- a/cpu/cpu_openbsd.go +++ b/cpu/cpu_openbsd.go @@ -28,6 +28,8 @@ var ( // sys/sysctl.h const ( CTLKern = 1 // "high kernel": proc, limits + CTLHw = 6 // CTL_HW + NCpuOnline = 25 // HW_NCPUONLINE KernCptime = 40 // KERN_CPTIME KernCptime2 = 71 // KERN_CPTIME2 ) @@ -134,10 +136,19 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { } c.Mhz = float64(u32) - if u32, err = unix.SysctlUint32("hw.ncpuonline"); err != nil { + mib := []int32{CTLHw, NCpuOnline} + buf, _, err := common.CallSyscall(mib) + if err != nil { return nil, err } - c.Cores = int32(u32) + + var ncpu int32 + br := bytes.NewReader(buf) + err = binary.Read(br, binary.LittleEndian, &ncpu) + if err != nil { + return nil, err + } + c.Cores = ncpu if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { return nil, err From 932f2f6049e0fbf62f15f15c3b37d84c900d1d25 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Sat, 16 Mar 2019 12:26:03 +0100 Subject: [PATCH 03/38] Fix cpu stats when hw.smt is enabled When hw.smt is enabled, and it's enabled by default from 6.4, the number of cpus given by `runtime.NumCPU()` is half of the total: only the cpuN with N = 0,2,4,... are used by the system. We need to detect that and ask for the correct stats. --- cpu/cpu_openbsd.go | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/cpu/cpu_openbsd.go b/cpu/cpu_openbsd.go index d224a1b..54f6ec2 100644 --- a/cpu/cpu_openbsd.go +++ b/cpu/cpu_openbsd.go @@ -29,6 +29,7 @@ var ( const ( CTLKern = 1 // "high kernel": proc, limits CTLHw = 6 // CTL_HW + SMT = 24 // HW_SMT NCpuOnline = 25 // HW_NCPUONLINE KernCptime = 40 // KERN_CPTIME KernCptime2 = 71 // KERN_CPTIME2 @@ -69,6 +70,22 @@ func init() { }() } +func smt() (bool, error) { + mib := []int32{CTLHw, SMT} + buf, _, err := common.CallSyscall(mib) + if err != nil { + return false, err + } + + var ret bool + br := bytes.NewReader(buf) + if err := binary.Read(br, binary.LittleEndian, ret); err != nil { + return false, err + } + + return ret, nil +} + func Times(percpu bool) ([]TimesStat, error) { return TimesWithContext(context.Background(), percpu) } @@ -83,11 +100,21 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { ncpu = 1 } + smt, err := smt() + if err != nil { + return nil, err + } + for i := 0; i < ncpu; i++ { + j := i + if !smt { + j *= 2 + } + var cpuTimes = make([]int64, CPUStates) var mib []int32 if percpu { - mib = []int32{CTLKern, KernCptime2, int32(i)} + mib = []int32{CTLKern, KernCptime2, int32(j)} } else { mib = []int32{CTLKern, KernCptime} } @@ -109,7 +136,7 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec, } if percpu { - c.CPU = fmt.Sprintf("cpu%d", i) + c.CPU = fmt.Sprintf("cpu%d", j) } else { c.CPU = "cpu-total" } From c28fe78291d43b7d61b0dc3b35b968ba55646d03 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Mon, 18 Mar 2019 19:58:50 +0100 Subject: [PATCH 04/38] forget to take addr --- cpu/cpu_openbsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu/cpu_openbsd.go b/cpu/cpu_openbsd.go index 54f6ec2..04b4e7f 100644 --- a/cpu/cpu_openbsd.go +++ b/cpu/cpu_openbsd.go @@ -79,7 +79,7 @@ func smt() (bool, error) { var ret bool br := bytes.NewReader(buf) - if err := binary.Read(br, binary.LittleEndian, ret); err != nil { + if err := binary.Read(br, binary.LittleEndian, &ret); err != nil { return false, err } From 53ce014b140c06d982700e06d7e9af275bc0820c Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Tue, 19 Mar 2019 20:06:12 +0100 Subject: [PATCH 05/38] handle EOPNOTSUPP when checking for hw.smt if hw.smt is not applicable for the current platform (e.g. i386), pretend it's enabled --- cpu/cpu_openbsd.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpu/cpu_openbsd.go b/cpu/cpu_openbsd.go index 04b4e7f..52bbcbd 100644 --- a/cpu/cpu_openbsd.go +++ b/cpu/cpu_openbsd.go @@ -10,6 +10,7 @@ import ( "os/exec" "strconv" "strings" + "syscall" "github.com/shirou/gopsutil/internal/common" "golang.org/x/sys/unix" @@ -101,7 +102,11 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { } smt, err := smt() - if err != nil { + if err == syscall.EOPNOTSUPP { + // if hw.smt is not applicable for this platform (e.g. i386), + // pretend it's enabled + smt = true + } else if err != nil { return nil, err } From 174b31f14621aa208279766cfa575ca9c916f3d7 Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 26 Apr 2019 23:12:47 +0800 Subject: [PATCH 06/38] Fix for #665 Remains backward compatible. When encountering non-fatal errors SensorsTemperatures() returns the temperatures for all the sensor we could read successfully. In that case the custom error contains a list of all the non-fatal errors encountered. Example usage: _, err := SensorsTemperatures() if err != nil { warns, ok := err.(*Warnings) if ok { fmt.Printf("%v\n", err) for i, w := range warns.List { fmt.Printf("Warning %v: %v\n", i+1, w) } } else { t.Errorf("%v", err) } } --- host/host_linux.go | 10 +++++++--- host/types.go | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 host/types.go diff --git a/host/host_linux.go b/host/host_linux.go index 03b3407..7395db7 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -635,6 +635,7 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err return temperatures, err } } + var warns Warnings // example directory // device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm @@ -660,16 +661,19 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err // Get the name of the temperature you are reading name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name")) if err != nil { - return temperatures, err + warns.Add(err) + continue } // Get the temperature reading current, err := ioutil.ReadFile(file) if err != nil { - return temperatures, err + warns.Add(err) + continue } temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64) if err != nil { + warns.Add(err) continue } @@ -679,5 +683,5 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err Temperature: temperature / 1000.0, }) } - return temperatures, nil + return temperatures, warns.Reference() } diff --git a/host/types.go b/host/types.go new file mode 100644 index 0000000..1766e7e --- /dev/null +++ b/host/types.go @@ -0,0 +1,25 @@ +package host + +import ( + "fmt" +) + +type Warnings struct { + List []error +} + +func (w *Warnings) Add(err error) { + w.List = append(w.List, err) +} + +func (w *Warnings) Reference() error { + if len(w.List) > 0 { + return w + } else { + return nil + } +} + +func (w *Warnings) Error() string { + return fmt.Sprintf("Number of warnings: %v", len(w.List)) +} From 3cbb0873de29868bd2ff13ad5f30939d6f749f18 Mon Sep 17 00:00:00 2001 From: Omar Polo Date: Thu, 2 May 2019 12:24:16 +0200 Subject: [PATCH 07/38] int32 is enough -- don't waste space --- cpu/cpu_openbsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu/cpu_openbsd.go b/cpu/cpu_openbsd.go index 52bbcbd..588d1b8 100644 --- a/cpu/cpu_openbsd.go +++ b/cpu/cpu_openbsd.go @@ -116,7 +116,7 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { j *= 2 } - var cpuTimes = make([]int64, CPUStates) + var cpuTimes = make([]int32, CPUStates) var mib []int32 if percpu { mib = []int32{CTLKern, KernCptime2, int32(j)} From 648bf4eebce6af326381701226319531e8c11781 Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Tue, 7 May 2019 07:24:17 -0400 Subject: [PATCH 08/38] Adds ConntrackStats to get conntrack summary stats --- internal/common/common.go | 6 ++ internal/common/common_test.go | 6 ++ net/net.go | 95 ++++++++++++++++++++++++++++ net/net_darwin.go | 9 +++ net/net_fallback.go | 8 +++ net/net_freebsd.go | 8 +++ net/net_linux.go | 72 +++++++++++++++++++++ net/net_linux_test.go | 139 +++++++++++++++++++++++++++++++++++++++++ net/net_openbsd.go | 8 +++ net/net_windows.go | 9 +++ 10 files changed, 360 insertions(+) diff --git a/internal/common/common.go b/internal/common/common.go index 71c2257..fc3f4dc 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -213,6 +213,12 @@ func ReadInts(filename string) ([]int64, error) { return ret, nil } +// Parse Hex to uint32 without error +func HexToUint32(hex string) uint32 { + vv, _ := strconv.ParseUint(hex, 16, 32) + return uint32(vv) +} + // Parse to int32 without error func mustParseInt32(val string) int32 { vv, _ := strconv.ParseInt(val, 10, 32) diff --git a/internal/common/common_test.go b/internal/common/common_test.go index cd33388..3d6ae10 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -51,6 +51,12 @@ func TestByteToString(t *testing.T) { } } +func TestHexToUint32(t *testing.T) { + if HexToUint32("FFFFFFFF") != 4294967295 { + t.Error("Could not convert") + } +} + func TestmustParseInt32(t *testing.T) { ret := mustParseInt32("11111") if ret != int32(11111) { diff --git a/net/net.go b/net/net.go index fce86c7..da613ac 100644 --- a/net/net.go +++ b/net/net.go @@ -70,6 +70,96 @@ type FilterStat struct { ConnTrackMax int64 `json:"conntrackMax"` } +// ConntrackStat has conntrack summary info +type ConntrackStat struct { + Entries uint32 `json:"entries"` // Number of entries in the conntrack table + Searched uint32 `json:"searched"` // Number of conntrack table lookups performed + Found uint32 `json:"found"` // Number of searched entries which were successful + New uint32 `json:"new"` // Number of entries added which were not expected before + Invalid uint32 `json:"invalid"` // Number of packets seen which can not be tracked + Ignore uint32 `json:"ignore"` // Packets seen which are already connected to an entry + Delete uint32 `json:"delete"` // Number of entries which were removed + DeleteList uint32 `json:"delete_list"` // Number of entries which were put to dying list + Insert uint32 `json:"insert"` // Number of entries inserted into the list + InsertFailed uint32 `json:"insert_failed"` // # insertion attempted but failed (same entry exists) + Drop uint32 `json:"drop"` // Number of packets dropped due to conntrack failure. + EarlyDrop uint32 `json:"early_drop"` // Dropped entries to make room for new ones, if maxsize reached + IcmpError uint32 `json:"icmp_error"` // Subset of invalid. Packets that can't be tracked d/t error + ExpectNew uint32 `json:"expect_new"` // Entries added after an expectation was already present + ExpectCreate uint32 `json:"expect_create"` // Expectations added + ExpectDelete uint32 `json:"expect_delete"` // Expectations deleted + SearchRestart uint32 `json:"search_restart"` // Conntrack table lookups restarted due to hashtable resizes +} + +func NewConntrackStat(e uint32, s uint32, f uint32, n uint32, inv uint32, ign uint32, del uint32, dlst uint32, ins uint32, insfail uint32, drop uint32, edrop uint32, ie uint32, en uint32, ec uint32, ed uint32, sr uint32) *ConntrackStat { + return &ConntrackStat{ + Entries: e, + Searched: s, + Found: f, + New: n, + Invalid: inv, + Ignore: ign, + Delete: del, + DeleteList: dlst, + Insert: ins, + InsertFailed: insfail, + Drop: drop, + EarlyDrop: edrop, + IcmpError: ie, + ExpectNew: en, + ExpectCreate: ec, + ExpectDelete: ed, + SearchRestart: sr, + } +} + +type ConntrackStatList struct { + items []*ConntrackStat +} + +func NewConntrackStatList() *ConntrackStatList { + return &ConntrackStatList{ + items: []*ConntrackStat{}, + } +} + +func (l *ConntrackStatList) Append(c *ConntrackStat) { + l.items = append(l.items, c) +} + +func (l *ConntrackStatList) Items() []ConntrackStat { + items := make([]ConntrackStat, len(l.items), len(l.items)) + for i, el := range l.items { + items[i] = *el + } + return items +} + +// Summary returns a single-element list with totals from all list items. +func (l *ConntrackStatList) Summary() []ConntrackStat { + summary := NewConntrackStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + for _, cs := range l.items { + summary.Entries += cs.Entries + summary.Searched += cs.Searched + summary.Found += cs.Found + summary.New += cs.New + summary.Invalid += cs.Invalid + summary.Ignore += cs.Ignore + summary.Delete += cs.Delete + summary.DeleteList += cs.DeleteList + summary.Insert += cs.Insert + summary.InsertFailed += cs.InsertFailed + summary.Drop += cs.Drop + summary.EarlyDrop += cs.EarlyDrop + summary.IcmpError += cs.IcmpError + summary.ExpectNew += cs.ExpectNew + summary.ExpectCreate += cs.ExpectCreate + summary.ExpectDelete += cs.ExpectDelete + summary.SearchRestart += cs.SearchRestart + } + return []ConntrackStat{*summary} +} + var constMap = map[string]int{ "unix": syscall.AF_UNIX, "TCP": syscall.SOCK_STREAM, @@ -108,6 +198,11 @@ func (n InterfaceAddr) String() string { return string(s) } +func (n ConntrackStat) String() string { + s, _ := json.Marshal(n) + return string(s) +} + func Interfaces() ([]InterfaceStat, error) { return InterfacesWithContext(context.Background()) } diff --git a/net/net_darwin.go b/net/net_darwin.go index ebebc2f..1daed86 100644 --- a/net/net_darwin.go +++ b/net/net_darwin.go @@ -6,6 +6,7 @@ import ( "context" "errors" "fmt" + "github.com/shirou/gopsutil/internal/common" "os/exec" "regexp" "strconv" @@ -271,6 +272,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { return nil, errors.New("NetFilterCounters not implemented for darwin") } +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. diff --git a/net/net_fallback.go b/net/net_fallback.go index 7c5e632..0991347 100644 --- a/net/net_fallback.go +++ b/net/net_fallback.go @@ -24,6 +24,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { return []FilterStat{}, common.ErrNotImplementedError } +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return ProtoCountersWithContext(context.Background(), protocols) } diff --git a/net/net_freebsd.go b/net/net_freebsd.go index 84b970a..2284d98 100644 --- a/net/net_freebsd.go +++ b/net/net_freebsd.go @@ -112,6 +112,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { return nil, errors.New("NetFilterCounters not implemented for freebsd") } +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, errors.New("ConntrackStats not implemented for freebsd") +} + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. diff --git a/net/net_linux.go b/net/net_linux.go index 71842fb..5e348bb 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -18,6 +18,26 @@ import ( "github.com/shirou/gopsutil/internal/common" ) +const ( // Conntrack Column numbers + CT_ENTRIES = iota + CT_SEARCHED + CT_FOUND + CT_NEW + CT_INVALID + CT_IGNORE + CT_DELETE + CT_DELETE_LIST + CT_INSERT + CT_INSERT_FAILED + CT_DROP + CT_EARLY_DROP + CT_ICMP_ERROR + CT_EXPECT_NEW + CT_EXPECT_CREATE + CT_EXPECT_DELETE + CT_SEARCH_RESTART +) + // NetIOCounters returnes network I/O statistics for every network // interface installed on the system. If pernic argument is false, // return only sum of all information (which name is 'all'). If true, @@ -232,6 +252,58 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { return stats, nil } +// ConntrackStats returns more detailed info about the conntrack table +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +// ConntrackStatsWithContext returns more detailed info about the conntrack table +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu) +} + +// conntrackStatsFromFile returns more detailed info about the conntrack table +// from `filename` +// If 'percpu' is false, the result will contain exactly one item with totals/summary +func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) { + lines, err := common.ReadLines(filename) + if err != nil { + return nil, err + } + + statlist := NewConntrackStatList() + + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) == 17 && fields[0] != "entries" { + statlist.Append(NewConntrackStat( + common.HexToUint32(fields[CT_ENTRIES]), + common.HexToUint32(fields[CT_SEARCHED]), + common.HexToUint32(fields[CT_FOUND]), + common.HexToUint32(fields[CT_NEW]), + common.HexToUint32(fields[CT_INVALID]), + common.HexToUint32(fields[CT_IGNORE]), + common.HexToUint32(fields[CT_DELETE]), + common.HexToUint32(fields[CT_DELETE_LIST]), + common.HexToUint32(fields[CT_INSERT]), + common.HexToUint32(fields[CT_INSERT_FAILED]), + common.HexToUint32(fields[CT_DROP]), + common.HexToUint32(fields[CT_EARLY_DROP]), + common.HexToUint32(fields[CT_ICMP_ERROR]), + common.HexToUint32(fields[CT_EXPECT_NEW]), + common.HexToUint32(fields[CT_EXPECT_CREATE]), + common.HexToUint32(fields[CT_EXPECT_DELETE]), + common.HexToUint32(fields[CT_SEARCH_RESTART]), + )) + } + } + + if percpu { + return statlist.Items(), nil + } + return statlist.Summary(), nil +} + // http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h var TCPStatuses = map[string]string{ "01": "ESTABLISHED", diff --git a/net/net_linux_test.go b/net/net_linux_test.go index 566a17b..cf19fcd 100644 --- a/net/net_linux_test.go +++ b/net/net_linux_test.go @@ -161,3 +161,142 @@ func TestReverse(t *testing.T) { src := []byte{0x01, 0x02, 0x03} assert.Equal(t, []byte{0x03, 0x02, 0x01}, Reverse(src)) } + +func TestConntrackStatFileParsing(t *testing.T) { + tmpfile, err := ioutil.TempFile("", "proc_net_stat_conntrack") + defer os.Remove(tmpfile.Name()) + assert.Nil(t, err, "Temporary file creation failed: ", err) + + data := []byte(` +entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart +0000007b 00000000 00000000 00000000 000b115a 00000084 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000004a +0000007b 00000000 00000000 00000000 0007eee5 00000068 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000035 +0000007b 00000000 00000000 00000000 0090346b 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000025 +0000007b 00000000 00000000 00000000 0005920f 00000069 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000064 +0000007b 00000000 00000000 00000000 000331ff 00000059 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000003b +0000007b 00000000 00000000 00000000 000314ea 00000066 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000054 +0000007b 00000000 00000000 00000000 0002b270 00000055 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000003d +0000007b 00000000 00000000 00000000 0002f67d 00000057 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000042 +`) + + // Expected results + slist := NewConntrackStatList() + + slist.Append(&ConntrackStat{ + Entries: 123, + Searched: 0, + Found: 0, + New: 0, + Invalid: 725338, + Ignore: 132, + Delete: 0, + DeleteList: 0, + Insert: 0, + InsertFailed: 0, + Drop: 0, + EarlyDrop: 0, + IcmpError: 0, + ExpectNew: 0, + ExpectCreate: 0, + ExpectDelete: 0, + SearchRestart: 74, + }) + slist.Append(&ConntrackStat{123, 0, 0, 0, 519909, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53}) + + slist.Append(&ConntrackStat{123, 0, 0, 0, 9450603, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37}) + slist.Append(&ConntrackStat{123, 0, 0, 0, 365071, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}) + + slist.Append(&ConntrackStat{123, 0, 0, 0, 209407, 89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59}) + slist.Append(&ConntrackStat{123, 0, 0, 0, 201962, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84}) + + slist.Append(&ConntrackStat{123, 0, 0, 0, 176752, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61}) + slist.Append(&ConntrackStat{123, 0, 0, 0, 194173, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66}) + + // Write data to tempfile + _, err = tmpfile.Write(data) + assert.Nil(t, err, "Temporary file writing failed: ", err) + + // Function under test + stats, err := conntrackStatsFromFile(tmpfile.Name(), true) + assert.Equal(t, 8, len(stats), "Expected 8 results") + + summary := &ConntrackStat{} + for i, exp := range slist.Items() { + st := stats[i] + + assert.Equal(t, exp.Entries, st.Entries) + summary.Entries += st.Entries + + assert.Equal(t, exp.Searched, st.Searched) + summary.Searched += st.Searched + + assert.Equal(t, exp.Found, st.Found) + summary.Found += st.Found + + assert.Equal(t, exp.New, st.New) + summary.New += st.New + + assert.Equal(t, exp.Invalid, st.Invalid) + summary.Invalid += st.Invalid + + assert.Equal(t, exp.Ignore, st.Ignore) + summary.Ignore += st.Ignore + + assert.Equal(t, exp.Delete, st.Delete) + summary.Delete += st.Delete + + assert.Equal(t, exp.DeleteList, st.DeleteList) + summary.DeleteList += st.DeleteList + + assert.Equal(t, exp.Insert, st.Insert) + summary.Insert += st.Insert + + assert.Equal(t, exp.InsertFailed, st.InsertFailed) + summary.InsertFailed += st.InsertFailed + + assert.Equal(t, exp.Drop, st.Drop) + summary.Drop += st.Drop + + assert.Equal(t, exp.EarlyDrop, st.EarlyDrop) + summary.EarlyDrop += st.EarlyDrop + + assert.Equal(t, exp.IcmpError, st.IcmpError) + summary.IcmpError += st.IcmpError + + assert.Equal(t, exp.ExpectNew, st.ExpectNew) + summary.ExpectNew += st.ExpectNew + + assert.Equal(t, exp.ExpectCreate, st.ExpectCreate) + summary.ExpectCreate += st.ExpectCreate + + assert.Equal(t, exp.ExpectDelete, st.ExpectDelete) + summary.ExpectDelete += st.ExpectDelete + + assert.Equal(t, exp.SearchRestart, st.SearchRestart) + summary.SearchRestart += st.SearchRestart + } + + // Test summary grouping + totals, err := conntrackStatsFromFile(tmpfile.Name(), false) + for i, st := range totals { + assert.Equal(t, summary.Entries, st.Entries) + assert.Equal(t, summary.Searched, st.Searched) + assert.Equal(t, summary.Found, st.Found) + assert.Equal(t, summary.New, st.New) + assert.Equal(t, summary.Invalid, st.Invalid) + assert.Equal(t, summary.Ignore, st.Ignore) + assert.Equal(t, summary.Delete, st.Delete) + assert.Equal(t, summary.DeleteList, st.DeleteList) + assert.Equal(t, summary.Insert, st.Insert) + assert.Equal(t, summary.InsertFailed, st.InsertFailed) + assert.Equal(t, summary.Drop, st.Drop) + assert.Equal(t, summary.EarlyDrop, st.EarlyDrop) + assert.Equal(t, summary.IcmpError, st.IcmpError) + assert.Equal(t, summary.ExpectNew, st.ExpectNew) + assert.Equal(t, summary.ExpectCreate, st.ExpectCreate) + assert.Equal(t, summary.ExpectDelete, st.ExpectDelete) + assert.Equal(t, summary.SearchRestart, st.SearchRestart) + + assert.Equal(t, 0, i) // Should only have one element + } +} diff --git a/net/net_openbsd.go b/net/net_openbsd.go index 0faa70e..3cf0a89 100644 --- a/net/net_openbsd.go +++ b/net/net_openbsd.go @@ -156,6 +156,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { return nil, errors.New("NetFilterCounters not implemented for openbsd") } +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. diff --git a/net/net_windows.go b/net/net_windows.go index 61eb6ec..f63eb02 100644 --- a/net/net_windows.go +++ b/net/net_windows.go @@ -206,6 +206,15 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) { return nil, errors.New("NetFilterCounters not implemented for windows") } +func ConntrackStats(percpu bool) ([]ConntrackStat, error) { + return ConntrackStatsWithContext(context.Background(), percpu) +} + +func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) { + return nil, common.ErrNotImplementedError +} + + // NetProtoCounters returns network statistics for the entire system // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. From 8037dc42c845ee253378b42d6c6f107bbe1c2973 Mon Sep 17 00:00:00 2001 From: Tony Lambiris Date: Mon, 13 May 2019 15:51:20 -0400 Subject: [PATCH 09/38] Add a check for logical volume paths --- disk/disk_linux.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 51482c0..d0dffa9 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -298,6 +298,14 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro } } + if strings.HasPrefix(d.Device, "/dev/mapper/") { + devpath, err := os.Readlink(d.Device) + if err != nil { + return nil, err + } + d.Device = "/dev/" + filepath.Base(devpath) + } + // /dev/root is not the real device name // so we get the real device name from its major/minor number if d.Device == "/dev/root" { From a02925055ca4208756a6522acbad55a3f2598a52 Mon Sep 17 00:00:00 2001 From: Tyler Dixon Date: Wed, 22 May 2019 17:38:10 -0700 Subject: [PATCH 10/38] Remove cycle between process and host packages gopsutil is a transitive dependency of another project that I am integrating into an internal build system. We target multiple platforms and as a part of the build system for the large internal repo, we calculate the build graph used to determine what targets have changed and need to be build / tested as a single DAG for all platforms. gopsutil currently does not form a DAG if linux and any other platform are considered at the same time. linux is the only platform where the process package imports the host package. To remove this cycle, the relevant methods have been moved to internal/common with the linux build tag and are consumed the host and process packages. --- host/host_linux.go | 184 +----------------------------------- internal/common/common_linux.go | 200 ++++++++++++++++++++++++++++++++++++++++ process/process_linux.go | 3 +- 3 files changed, 204 insertions(+), 183 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index 49f52c9..537d822 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -15,7 +15,6 @@ import ( "runtime" "strconv" "strings" - "sync/atomic" "time" "github.com/shirou/gopsutil/internal/common" @@ -105,71 +104,13 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { return ret, nil } -// cachedBootTime must be accessed via atomic.Load/StoreUint64 -var cachedBootTime uint64 - // BootTime returns the system boot time expressed in seconds since the epoch. func BootTime() (uint64, error) { return BootTimeWithContext(context.Background()) } func BootTimeWithContext(ctx context.Context) (uint64, error) { - t := atomic.LoadUint64(&cachedBootTime) - if t != 0 { - return t, nil - } - - system, role, err := Virtualization() - if err != nil { - return 0, err - } - - statFile := "stat" - if system == "lxc" && role == "guest" { - // if lxc, /proc/uptime is used. - statFile = "uptime" - } else if system == "docker" && role == "guest" { - // also docker, guest - statFile = "uptime" - } - - filename := common.HostProc(statFile) - lines, err := common.ReadLines(filename) - if err != nil { - return 0, err - } - - if statFile == "stat" { - for _, line := range lines { - if strings.HasPrefix(line, "btime") { - f := strings.Fields(line) - if len(f) != 2 { - return 0, fmt.Errorf("wrong btime format") - } - b, err := strconv.ParseInt(f[1], 10, 64) - if err != nil { - return 0, err - } - t = uint64(b) - atomic.StoreUint64(&cachedBootTime, t) - return t, nil - } - } - } else if statFile == "uptime" { - if len(lines) != 1 { - return 0, fmt.Errorf("wrong uptime format") - } - f := strings.Fields(lines[0]) - b, err := strconv.ParseFloat(f[0], 64) - if err != nil { - return 0, err - } - t = uint64(time.Now().Unix()) - uint64(b) - atomic.StoreUint64(&cachedBootTime, t) - return t, nil - } - - return 0, fmt.Errorf("could not find btime") + return common.BootTimeWithContext(ctx) } func uptime(boot uint64) uint64 { @@ -235,26 +176,6 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) { } -func getOSRelease() (platform string, version string, err error) { - contents, err := common.ReadLines(common.HostEtc("os-release")) - if err != nil { - return "", "", nil // return empty - } - for _, line := range contents { - field := strings.Split(line, "=") - if len(field) < 2 { - continue - } - switch field[0] { - case "ID": // use ID for lowercase - platform = field[1] - case "VERSION": - version = field[1] - } - } - return platform, version, nil -} - func getLSB() (*LSB, error) { ret := &LSB{} if common.PathExists(common.HostEtc("lsb-release")) { @@ -392,7 +313,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil version = contents[0] } } else if common.PathExists(common.HostEtc("os-release")) { - p, v, err := getOSRelease() + p, v, err := common.GetOSRelease() if err == nil { platform = p version = v @@ -515,106 +436,7 @@ func Virtualization() (string, string, error) { } func VirtualizationWithContext(ctx context.Context) (string, string, error) { - var system string - var role string - - filename := common.HostProc("xen") - if common.PathExists(filename) { - system = "xen" - role = "guest" // assume guest - - if common.PathExists(filepath.Join(filename, "capabilities")) { - contents, err := common.ReadLines(filepath.Join(filename, "capabilities")) - if err == nil { - if common.StringsContains(contents, "control_d") { - role = "host" - } - } - } - } - - filename = common.HostProc("modules") - if common.PathExists(filename) { - contents, err := common.ReadLines(filename) - if err == nil { - if common.StringsContains(contents, "kvm") { - system = "kvm" - role = "host" - } else if common.StringsContains(contents, "vboxdrv") { - system = "vbox" - role = "host" - } else if common.StringsContains(contents, "vboxguest") { - system = "vbox" - role = "guest" - } else if common.StringsContains(contents, "vmware") { - system = "vmware" - role = "guest" - } - } - } - - filename = common.HostProc("cpuinfo") - if common.PathExists(filename) { - contents, err := common.ReadLines(filename) - if err == nil { - if common.StringsContains(contents, "QEMU Virtual CPU") || - common.StringsContains(contents, "Common KVM processor") || - common.StringsContains(contents, "Common 32-bit KVM processor") { - system = "kvm" - role = "guest" - } - } - } - - filename = common.HostProc() - if common.PathExists(filepath.Join(filename, "bc", "0")) { - system = "openvz" - role = "host" - } else if common.PathExists(filepath.Join(filename, "vz")) { - system = "openvz" - role = "guest" - } - - // not use dmidecode because it requires root - if common.PathExists(filepath.Join(filename, "self", "status")) { - contents, err := common.ReadLines(filepath.Join(filename, "self", "status")) - if err == nil { - - if common.StringsContains(contents, "s_context:") || - common.StringsContains(contents, "VxID:") { - system = "linux-vserver" - } - // TODO: guest or host - } - } - - if common.PathExists(filepath.Join(filename, "self", "cgroup")) { - contents, err := common.ReadLines(filepath.Join(filename, "self", "cgroup")) - if err == nil { - if common.StringsContains(contents, "lxc") { - system = "lxc" - role = "guest" - } else if common.StringsContains(contents, "docker") { - system = "docker" - role = "guest" - } else if common.StringsContains(contents, "machine-rkt") { - system = "rkt" - role = "guest" - } else if common.PathExists("/usr/bin/lxc-version") { - system = "lxc" - role = "host" - } - } - } - - if common.PathExists(common.HostEtc("os-release")) { - p, _, err := getOSRelease() - if err == nil && p == "coreos" { - system = "rkt" // Is it true? - role = "host" - } - } - return system, role, nil + return common.VirtualizationWithContext(ctx) } func SensorsTemperatures() ([]TemperatureStat, error) { diff --git a/internal/common/common_linux.go b/internal/common/common_linux.go index c65151a..415c848 100644 --- a/internal/common/common_linux.go +++ b/internal/common/common_linux.go @@ -3,9 +3,15 @@ package common import ( + "context" + "fmt" "os" "os/exec" + "path/filepath" + "strconv" "strings" + "sync/atomic" + "time" ) func DoSysctrl(mib string) ([]string, error) { @@ -39,3 +45,197 @@ func NumProcs() (uint64, error) { } return uint64(len(list)), err } + +// cachedBootTime must be accessed via atomic.Load/StoreUint64 +var cachedBootTime uint64 + +// BootTime returns the system boot time expressed in seconds since the epoch. +func BootTime() (uint64, error) { + return BootTimeWithContext(context.Background()) +} + +func BootTimeWithContext(ctx context.Context) (uint64, error) { + t := atomic.LoadUint64(&cachedBootTime) + if t != 0 { + return t, nil + } + + system, role, err := Virtualization() + if err != nil { + return 0, err + } + + statFile := "stat" + if system == "lxc" && role == "guest" { + // if lxc, /proc/uptime is used. + statFile = "uptime" + } else if system == "docker" && role == "guest" { + // also docker, guest + statFile = "uptime" + } + + filename := HostProc(statFile) + lines, err := ReadLines(filename) + if err != nil { + return 0, err + } + + if statFile == "stat" { + for _, line := range lines { + if strings.HasPrefix(line, "btime") { + f := strings.Fields(line) + if len(f) != 2 { + return 0, fmt.Errorf("wrong btime format") + } + b, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return 0, err + } + t = uint64(b) + atomic.StoreUint64(&cachedBootTime, t) + return t, nil + } + } + } else if statFile == "uptime" { + if len(lines) != 1 { + return 0, fmt.Errorf("wrong uptime format") + } + f := strings.Fields(lines[0]) + b, err := strconv.ParseFloat(f[0], 64) + if err != nil { + return 0, err + } + t = uint64(time.Now().Unix()) - uint64(b) + atomic.StoreUint64(&cachedBootTime, t) + return t, nil + } + + return 0, fmt.Errorf("could not find btime") +} + +func Virtualization() (string, string, error) { + return VirtualizationWithContext(context.Background()) +} + +func VirtualizationWithContext(ctx context.Context) (string, string, error) { + var system string + var role string + + filename := HostProc("xen") + if PathExists(filename) { + system = "xen" + role = "guest" // assume guest + + if PathExists(filepath.Join(filename, "capabilities")) { + contents, err := ReadLines(filepath.Join(filename, "capabilities")) + if err == nil { + if StringsContains(contents, "control_d") { + role = "host" + } + } + } + } + + filename = HostProc("modules") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "kvm") { + system = "kvm" + role = "host" + } else if StringsContains(contents, "vboxdrv") { + system = "vbox" + role = "host" + } else if StringsContains(contents, "vboxguest") { + system = "vbox" + role = "guest" + } else if StringsContains(contents, "vmware") { + system = "vmware" + role = "guest" + } + } + } + + filename = HostProc("cpuinfo") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "QEMU Virtual CPU") || + StringsContains(contents, "Common KVM processor") || + StringsContains(contents, "Common 32-bit KVM processor") { + system = "kvm" + role = "guest" + } + } + } + + filename = HostProc() + if PathExists(filepath.Join(filename, "bc", "0")) { + system = "openvz" + role = "host" + } else if PathExists(filepath.Join(filename, "vz")) { + system = "openvz" + role = "guest" + } + + // not use dmidecode because it requires root + if PathExists(filepath.Join(filename, "self", "status")) { + contents, err := ReadLines(filepath.Join(filename, "self", "status")) + if err == nil { + + if StringsContains(contents, "s_context:") || + StringsContains(contents, "VxID:") { + system = "linux-vserver" + } + // TODO: guest or host + } + } + + if PathExists(filepath.Join(filename, "self", "cgroup")) { + contents, err := ReadLines(filepath.Join(filename, "self", "cgroup")) + if err == nil { + if StringsContains(contents, "lxc") { + system = "lxc" + role = "guest" + } else if StringsContains(contents, "docker") { + system = "docker" + role = "guest" + } else if StringsContains(contents, "machine-rkt") { + system = "rkt" + role = "guest" + } else if PathExists("/usr/bin/lxc-version") { + system = "lxc" + role = "host" + } + } + } + + if PathExists(HostEtc("os-release")) { + p, _, err := GetOSRelease() + if err == nil && p == "coreos" { + system = "rkt" // Is it true? + role = "host" + } + } + return system, role, nil +} + +func GetOSRelease() (platform string, version string, err error) { + contents, err := ReadLines(HostEtc("os-release")) + if err != nil { + return "", "", nil // return empty + } + for _, line := range contents { + field := strings.Split(line, "=") + if len(field) < 2 { + continue + } + switch field[0] { + case "ID": // use ID for lowercase + platform = field[1] + case "VERSION": + version = field[1] + } + } + return platform, version, nil +} diff --git a/process/process_linux.go b/process/process_linux.go index 8ae99ff..49e5829 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -16,7 +16,6 @@ import ( "strings" "github.com/shirou/gopsutil/cpu" - "github.com/shirou/gopsutil/host" "github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/net" "golang.org/x/sys/unix" @@ -1236,7 +1235,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui System: float64(stime / ClockTicks), } - bootTime, _ := host.BootTime() + bootTime, _ := common.BootTime() t, err := strconv.ParseUint(fields[i+20], 10, 64) if err != nil { return 0, 0, nil, 0, 0, 0, nil, err From 2a0b67d19cda8a0a86de2bff4d2d95c8bca02e6a Mon Sep 17 00:00:00 2001 From: Arturo Reuschenbach Puncernau Date: Fri, 24 May 2019 11:33:07 +0200 Subject: [PATCH 11/38] added sles to the suse platform family --- host/host_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/host_linux.go b/host/host_linux.go index 49f52c9..8eab651 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -421,7 +421,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil family = "fedora" case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm": family = "rhel" - case "suse", "opensuse": + case "suse", "opensuse", "sles": family = "suse" case "gentoo": family = "gentoo" From 4e81681ab397520daa2da375ebb31992857e41ee Mon Sep 17 00:00:00 2001 From: Tyler Dixon Date: Fri, 24 May 2019 09:48:27 -0700 Subject: [PATCH 12/38] code review --- internal/common/common_linux.go | 5 ----- process/process_linux.go | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/common/common_linux.go b/internal/common/common_linux.go index 415c848..29a9be7 100644 --- a/internal/common/common_linux.go +++ b/internal/common/common_linux.go @@ -49,11 +49,6 @@ func NumProcs() (uint64, error) { // cachedBootTime must be accessed via atomic.Load/StoreUint64 var cachedBootTime uint64 -// BootTime returns the system boot time expressed in seconds since the epoch. -func BootTime() (uint64, error) { - return BootTimeWithContext(context.Background()) -} - func BootTimeWithContext(ctx context.Context) (uint64, error) { t := atomic.LoadUint64(&cachedBootTime) if t != 0 { diff --git a/process/process_linux.go b/process/process_linux.go index 49e5829..c3d2c12 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -1235,7 +1235,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui System: float64(stime / ClockTicks), } - bootTime, _ := common.BootTime() + bootTime, _ := common.BootTimeWithContext(ctx) t, err := strconv.ParseUint(fields[i+20], 10, 64) if err != nil { return 0, 0, nil, 0, 0, 0, nil, err From 3fb9243fc2175e7b163e9b0e8aa8e233f2cf9bed Mon Sep 17 00:00:00 2001 From: Lomanic Date: Fri, 31 May 2019 17:39:23 +0200 Subject: [PATCH 13/38] [net][windows] Fix #693 use MIB_IF_ROW2/GetIfEntry2 to get real uint64 values from win32 API --- net/net_windows.go | 127 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/net/net_windows.go b/net/net_windows.go index 61eb6ec..111a73c 100644 --- a/net/net_windows.go +++ b/net/net_windows.go @@ -19,6 +19,7 @@ var ( modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable") procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") + procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2") ) const ( @@ -73,6 +74,65 @@ var netConnectionKindMap = map[string][]netConnectionKindType{ "inet6": {kindTCP6, kindUDP6}, } +// https://github.com/microsoft/ethr/blob/aecdaf923970e5a9b4c461b4e2e3963d781ad2cc/plt_windows.go#L114-L170 +type guid struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +const ( + maxStringSize = 256 + maxPhysAddressLength = 32 + pad0for64_4for32 = 0 +) + +type mibIfRow2 struct { + InterfaceLuid uint64 + InterfaceIndex uint32 + InterfaceGuid guid + Alias [maxStringSize + 1]uint16 + Description [maxStringSize + 1]uint16 + PhysicalAddressLength uint32 + PhysicalAddress [maxPhysAddressLength]uint8 + PermanentPhysicalAddress [maxPhysAddressLength]uint8 + Mtu uint32 + Type uint32 + TunnelType uint32 + MediaType uint32 + PhysicalMediumType uint32 + AccessType uint32 + DirectionType uint32 + InterfaceAndOperStatusFlags uint32 + OperStatus uint32 + AdminStatus uint32 + MediaConnectState uint32 + NetworkGuid guid + ConnectionType uint32 + padding1 [pad0for64_4for32]byte + TransmitLinkSpeed uint64 + ReceiveLinkSpeed uint64 + InOctets uint64 + InUcastPkts uint64 + InNUcastPkts uint64 + InDiscards uint64 + InErrors uint64 + InUnknownProtos uint64 + InUcastOctets uint64 + InMulticastOctets uint64 + InBroadcastOctets uint64 + OutOctets uint64 + OutUcastPkts uint64 + OutNUcastPkts uint64 + OutDiscards uint64 + OutErrors uint64 + OutUcastOctets uint64 + OutMulticastOctets uint64 + OutBroadcastOctets uint64 + OutQLen uint64 +} + func IOCounters(pernic bool) ([]IOCountersStat, error) { return IOCountersWithContext(context.Background(), pernic) } @@ -82,34 +142,59 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, if err != nil { return nil, err } - var ret []IOCountersStat + var counters []IOCountersStat + + err = procGetIfEntry2.Find() + if err == nil { // Vista+, uint64 values (issue#693) + for _, ifi := range ifs { + c := IOCountersStat{ + Name: ifi.Name, + } - for _, ifi := range ifs { - c := IOCountersStat{ - Name: ifi.Name, + row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)} + ret, _, err := procGetIfEntry2.Call(uintptr(unsafe.Pointer(&row))) + if ret != 0 { + return nil, os.NewSyscallError("GetIfEntry2", err) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + counters = append(counters, c) } + } else { // WinXP fallback, uint32 values + for _, ifi := range ifs { + c := IOCountersStat{ + Name: ifi.Name, + } - row := windows.MibIfRow{Index: uint32(ifi.Index)} - e := windows.GetIfEntry(&row) - if e != nil { - return nil, os.NewSyscallError("GetIfEntry", e) + row := windows.MibIfRow{Index: uint32(ifi.Index)} + err = windows.GetIfEntry(&row) + if err != nil { + return nil, os.NewSyscallError("GetIfEntry", err) + } + c.BytesSent = uint64(row.OutOctets) + c.BytesRecv = uint64(row.InOctets) + c.PacketsSent = uint64(row.OutUcastPkts) + c.PacketsRecv = uint64(row.InUcastPkts) + c.Errin = uint64(row.InErrors) + c.Errout = uint64(row.OutErrors) + c.Dropin = uint64(row.InDiscards) + c.Dropout = uint64(row.OutDiscards) + + counters = append(counters, c) } - c.BytesSent = uint64(row.OutOctets) - c.BytesRecv = uint64(row.InOctets) - c.PacketsSent = uint64(row.OutUcastPkts) - c.PacketsRecv = uint64(row.InUcastPkts) - c.Errin = uint64(row.InErrors) - c.Errout = uint64(row.OutErrors) - c.Dropin = uint64(row.InDiscards) - c.Dropout = uint64(row.OutDiscards) - - ret = append(ret, c) } - if pernic == false { - return getIOCountersAll(ret) + if !pernic { + return getIOCountersAll(counters) } - return ret, nil + return counters, nil } // NetIOCountersByFile is an method which is added just a compatibility for linux. From 3af6e1ffe781405a7ff9915ff37754cba707e680 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Fri, 31 May 2019 18:04:45 +0200 Subject: [PATCH 14/38] [host][linux] Properly handle double quotes in /etc/os-release in PlatformInformation --- host/host_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index 8eab651..89af40d 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -247,9 +247,9 @@ func getOSRelease() (platform string, version string, err error) { } switch field[0] { case "ID": // use ID for lowercase - platform = field[1] + platform = strings.Trim(field[1], `"`) case "VERSION": - version = field[1] + version = strings.Trim(field[1], `"`) } } return platform, version, nil From d13ba02ef07b4ea5852bd8143324504965d5b01f Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Fri, 31 May 2019 13:13:06 -0400 Subject: [PATCH 15/38] Update host_darwin.go The /System/Library/CoreServices/ServerVersion.plist exists on macOS servers , but not on a workstation such as my laptop. The actual terminoly is mostly borrowed from the windows equivalent as @Lomanic suggested. In theory, this should make interpreting the results from the two platforms a bit more consistent. Note: The macOS server application can be installed on almost any macOS workstation to make it a server that can manage other apple devices. --- host/host_darwin.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/host/host_darwin.go b/host/host_darwin.go index 9f2b6b4..f42e691 100644 --- a/host/host_darwin.go +++ b/host/host_darwin.go @@ -189,6 +189,16 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string pver = strings.ToLower(strings.TrimSpace(string(out))) } + // check if the macos server version file exists + _, err := os.Stat("/System/Library/CoreServices/ServerVersion.plist") + + // server file doesn't exist + if os.IsNotExist(err) { + family = "Standalone Workstation" + } else { + family = "Server" + } + return platform, family, pver, nil } From e507f44421592eca2227e93a1d04c391e9896a45 Mon Sep 17 00:00:00 2001 From: Kent 'picat' Gruber Date: Fri, 31 May 2019 13:19:04 -0400 Subject: [PATCH 16/38] Update host_darwin.go Copy+pasta got me again! :spaghetti: No new variable on the left of `:=` :joy: --- host/host_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/host_darwin.go b/host/host_darwin.go index f42e691..de8277f 100644 --- a/host/host_darwin.go +++ b/host/host_darwin.go @@ -190,7 +190,7 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string } // check if the macos server version file exists - _, err := os.Stat("/System/Library/CoreServices/ServerVersion.plist") + _, err = os.Stat("/System/Library/CoreServices/ServerVersion.plist") // server file doesn't exist if os.IsNotExist(err) { From 07863cab0c4138c6afcabda42fbb483bd252ad16 Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 1 Jun 2019 11:31:00 +0900 Subject: [PATCH 17/38] [host]linux: add #688 diff which is removed after merging #689 --- internal/common/common_linux.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/common/common_linux.go b/internal/common/common_linux.go index 29a9be7..1938b49 100644 --- a/internal/common/common_linux.go +++ b/internal/common/common_linux.go @@ -164,6 +164,16 @@ func VirtualizationWithContext(ctx context.Context) (string, string, error) { } } + filename = HostProc("bus/pci/devices") + if PathExists(filename) { + contents, err := ReadLines(filename) + if err == nil { + if StringsContains(contents, "virtio-pci") { + role = "guest" + } + } + } + filename = HostProc() if PathExists(filepath.Join(filename, "bc", "0")) { system = "openvz" From 354718bdd87e0a8edbb9e21093598b3e6dbeee4d Mon Sep 17 00:00:00 2001 From: Chad Harp Date: Sat, 1 Jun 2019 15:05:46 -0500 Subject: [PATCH 18/38] Add support for Solaris CPU times --- cpu/cpu_solaris.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/cpu/cpu_solaris.go b/cpu/cpu_solaris.go index 947ac97..ab22c87 100644 --- a/cpu/cpu_solaris.go +++ b/cpu/cpu_solaris.go @@ -1,6 +1,7 @@ package cpu import ( + "bytes" "context" "errors" "fmt" @@ -10,8 +11,6 @@ import ( "sort" "strconv" "strings" - - "github.com/shirou/gopsutil/internal/common" ) var ClocksPerSec = float64(128) @@ -31,12 +30,96 @@ func init() { } } +//sum all values in a float64 map with float64 keys +func msum(x map[float64]float64) float64 { + total := 0.0 + for _, y := range x { + total += y + } + return total +} + func Times(percpu bool) ([]TimesStat, error) { return TimesWithContext(context.Background(), percpu) } func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { - return []TimesStat{}, common.ErrNotImplementedError + kstatSys, err := exec.LookPath("kstat") + if err != nil { + return nil, fmt.Errorf("cannot find kstat: %s", err) + } + cpu := make(map[float64]float64) + idle := make(map[float64]float64) + user := make(map[float64]float64) + kern := make(map[float64]float64) + iowt := make(map[float64]float64) + //swap := make(map[float64]float64) + kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-C", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/") + if err != nil { + return nil, fmt.Errorf("cannot execute kstat: %s", err) + } + for _, line := range strings.Split(bytes.NewBuffer(kstatSysOut).String(), "\n") { + fields := strings.Split(line, ":") + if fields[0] != "cpu_stat" { + continue + } + cpuNumber, err := strconv.ParseFloat(fields[1], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse cpu number: %s", err) + } + cpu[cpuNumber] = cpuNumber + switch fields[3] { + case "idle": + idle[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse idle: %s", err) + } + case "user": + user[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse user: %s", err) + } + case "kernel": + kern[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse kernel: %s", err) + } + case "iowait": + iowt[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse iowait: %s", err) + } + //not sure how this translates, don't report, add to kernel, something else? + /*case "swap": + swap[cpuNumber], err = strconv.ParseFloat(fields[4], 64) + if err != nil { + return nil, fmt.Errorf("cannot parse swap: %s", err) + } */ + } + } + ret := make([]TimesStat, 0, len(cpu)) + if percpu { + for _, c := range cpu { + ct := &TimesStat{ + CPU: fmt.Sprintf("cpu%d", int(cpu[c])), + Idle: idle[c] / ClocksPerSec, + User: user[c] / ClocksPerSec, + System: kern[c] / ClocksPerSec, + Iowait: iowt[c] / ClocksPerSec, + } + ret = append(ret, *ct) + } + } else { + ct := &TimesStat{ + CPU: "cpu-total", + Idle: msum(idle) / ClocksPerSec, + User: msum(user) / ClocksPerSec, + System: msum(kern) / ClocksPerSec, + Iowait: msum(iowt) / ClocksPerSec, + } + ret = append(ret, *ct) + } + return ret, nil } func Info() ([]InfoStat, error) { From 16b37cc9c27f53362af380bbdb0e862ddfdae614 Mon Sep 17 00:00:00 2001 From: Chad Harp Date: Sun, 2 Jun 2019 09:58:35 -0500 Subject: [PATCH 19/38] Modify cpu_solaris to support Sorlais 10 --- cpu/cpu_solaris.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpu/cpu_solaris.go b/cpu/cpu_solaris.go index ab22c87..07c3106 100644 --- a/cpu/cpu_solaris.go +++ b/cpu/cpu_solaris.go @@ -54,12 +54,13 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { kern := make(map[float64]float64) iowt := make(map[float64]float64) //swap := make(map[float64]float64) - kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-C", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/") + kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-p", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/") if err != nil { return nil, fmt.Errorf("cannot execute kstat: %s", err) } + re := regexp.MustCompile(`[:\s]+`) for _, line := range strings.Split(bytes.NewBuffer(kstatSysOut).String(), "\n") { - fields := strings.Split(line, ":") + fields := re.Split(line, -1) if fields[0] != "cpu_stat" { continue } From eb15d06a52f037f61a026a76e4baabc52c52809d Mon Sep 17 00:00:00 2001 From: Arturo Reuschenbach Puncernau Date: Mon, 3 Jun 2019 14:21:04 +0200 Subject: [PATCH 20/38] trim quotes when reading from os-release --- internal/common/common_linux.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/internal/common/common_linux.go b/internal/common/common_linux.go index 1938b49..ea2860e 100644 --- a/internal/common/common_linux.go +++ b/internal/common/common_linux.go @@ -237,10 +237,20 @@ func GetOSRelease() (platform string, version string, err error) { } switch field[0] { case "ID": // use ID for lowercase - platform = field[1] + platform = trimQuotes(field[1]) case "VERSION": - version = field[1] + version = trimQuotes(field[1]) } } return platform, version, nil } + +// Remove quotes of the source string +func trimQuotes(s string) string { + if len(s) >= 2 { + if s[0] == '"' && s[len(s)-1] == '"' { + return s[1 : len(s)-1] + } + } + return s +} From 8c6072d1112e1047484e6caafbd471cca394634e Mon Sep 17 00:00:00 2001 From: Chad Harp Date: Fri, 7 Jun 2019 07:39:57 -0500 Subject: [PATCH 21/38] Cast bytes to string --- cpu/cpu_solaris.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpu/cpu_solaris.go b/cpu/cpu_solaris.go index 07c3106..3de0984 100644 --- a/cpu/cpu_solaris.go +++ b/cpu/cpu_solaris.go @@ -1,7 +1,6 @@ package cpu import ( - "bytes" "context" "errors" "fmt" @@ -59,7 +58,7 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { return nil, fmt.Errorf("cannot execute kstat: %s", err) } re := regexp.MustCompile(`[:\s]+`) - for _, line := range strings.Split(bytes.NewBuffer(kstatSysOut).String(), "\n") { + for _, line := range strings.Split(string(kstatSysOut), "\n") { fields := re.Split(line, -1) if fields[0] != "cpu_stat" { continue From 809306b78a9a4c6016fbf11688b6bb0395c6360e Mon Sep 17 00:00:00 2001 From: chi-chi weng <949409306@qq.com> Date: Wed, 12 Jun 2019 11:37:08 +0800 Subject: [PATCH 22/38] Fix the net.ConnectionsMax BUG `connectionsList, err := net.ConnectionsMax("tcp4", 1000)` when you run net.ConnectionsMax,you will find some proc is not equal with the `netstat -lptn` --- net/net_linux.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/net_linux.go b/net/net_linux.go index 5e348bb..873a21a 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "syscall" + "io" "github.com/shirou/gopsutil/internal/common" ) @@ -653,7 +654,7 @@ func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) { t, err := getProcInodes(root, pid, max) if err != nil { // skip if permission error or no longer exists - if os.IsPermission(err) || os.IsNotExist(err) { + if os.IsPermission(err) || os.IsNotExist(err) || err == io.EOF { continue } return ret, err From 47323f9ad5780511d033d75bc55e894881d7bce2 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 16 Jun 2019 22:59:03 +0200 Subject: [PATCH 23/38] [process][windows] Fix #586 use win32 API in process.Exe() instead of slow WMI call This is faster by a factor of 100. References: https://github.com/giampaolo/psutil/blob/5f4287d17fc6aa2643c4c6e3589c12abd0f1ded9/psutil/_pswindows.py#L221 https://github.com/giampaolo/psutil/blob/921870d54091f399cd2b129db19530cc486b5700/psutil/_psutil_windows.c#L1211 https://github.com/giampaolo/psutil/blob/921870d54091f399cd2b129db19530cc486b5700/psutil/_psutil_windows.c#L626 --- internal/common/common_windows.go | 25 ++++++++++++++++++++++ process/process_windows.go | 44 ++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/internal/common/common_windows.go b/internal/common/common_windows.go index 0997c9b..dbc8b67 100644 --- a/internal/common/common_windows.go +++ b/internal/common/common_windows.go @@ -4,6 +4,9 @@ package common import ( "context" + "path/filepath" + "strings" + "syscall" "unsafe" "github.com/StackExchange/wmi" @@ -59,6 +62,8 @@ var ( PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") + + procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW") ) type FILETIME struct { @@ -133,3 +138,23 @@ func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, con return err } } + +// Convert paths using native DOS format like: +// "\Device\HarddiskVolume1\Windows\systemew\file.txt" +// into: +// "C:\Windows\systemew\file.txt" +func ConvertDOSPath(p string) string { + rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`) + + for d := 'A'; d <= 'Z'; d++ { + szDeviceName := string(d) + ":" + szTarget := make([]uint16, 512) + ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))), + uintptr(unsafe.Pointer(&szTarget[0])), + uintptr(len(szTarget))) + if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive { + return filepath.Join(szDeviceName, p[len(rawDrive):]) + } + } + return p +} diff --git a/process/process_windows.go b/process/process_windows.go index 2520958..c580ac5 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -25,12 +25,15 @@ const ( ) var ( - modpsapi = windows.NewLazySystemDLL("psapi.dll") - procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + modpsapi = windows.NewLazySystemDLL("psapi.dll") + procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") + procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW") advapi32 = windows.NewLazySystemDLL("advapi32.dll") procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW") procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") + + procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW") ) type SystemProcessInformation struct { @@ -234,24 +237,31 @@ func (p *Process) Exe() (string, error) { } func (p *Process) ExeWithContext(ctx context.Context) (string, error) { - if p.Pid != 0 { // 0 or null is the current process for CreateToolhelp32Snapshot - snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPMODULE|w32.TH32CS_SNAPMODULE32, uint32(p.Pid)) - if snap != 0 { // don't report errors here, fallback to WMI instead - defer w32.CloseHandle(snap) - var me32 w32.MODULEENTRY32 - me32.Size = uint32(unsafe.Sizeof(me32)) - - if w32.Module32First(snap, &me32) { - szexepath := windows.UTF16ToString(me32.SzExePath[:]) - return szexepath, nil - } + // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION + c, err := syscall.OpenProcess(0x1000, false, uint32(p.Pid)) + if err != nil { + return "", err + } + defer syscall.CloseHandle(c) + buf := make([]uint16, syscall.MAX_LONG_PATH) + size := uint32(syscall.MAX_LONG_PATH) + if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+ + ret, _, err := procQueryFullProcessImageNameW.Call( + uintptr(c), + uintptr(0), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(unsafe.Pointer(&size))) + if ret == 0 { + return "", err } + return windows.UTF16ToString(buf[:]), nil } - dst, err := GetWin32ProcWithContext(ctx, p.Pid) - if err != nil { - return "", fmt.Errorf("could not get ExecutablePath: %s", err) + // XP fallback + ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size)) + if ret == 0 { + return "", err } - return *dst[0].ExecutablePath, nil + return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil } func (p *Process) Cmdline() (string, error) { From cf9aa4a8ec20b054a533b388c1d1dd6b38880878 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Tue, 18 Jun 2019 22:41:32 +0200 Subject: [PATCH 24/38] [process][windows] Use win32 API in process.Nice() instead of slow WMI call Convert priority classes values to their WMI equivalent for backward compatiblity. --- process/process_windows.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index c580ac5..c84bb46 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -34,6 +34,7 @@ var ( procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW") + procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass") ) type SystemProcessInformation struct { @@ -392,17 +393,39 @@ func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { return "", common.ErrNotImplementedError } +// priorityClasses maps a win32 priority class to its WMI equivalent Win32_Process.Priority +// https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass +// https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process +var priorityClasses = map[int]int32{ + 0x00008000: 10, // ABOVE_NORMAL_PRIORITY_CLASS + 0x00004000: 6, // BELOW_NORMAL_PRIORITY_CLASS + 0x00000080: 13, // HIGH_PRIORITY_CLASS + 0x00000040: 4, // IDLE_PRIORITY_CLASS + 0x00000020: 8, // NORMAL_PRIORITY_CLASS + 0x00000100: 24, // REALTIME_PRIORITY_CLASS +} + // Nice returns priority in Windows func (p *Process) Nice() (int32, error) { return p.NiceWithContext(context.Background()) } func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { - dst, err := GetWin32ProcWithContext(ctx, p.Pid) + // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION + c, err := syscall.OpenProcess(0x1000, false, uint32(p.Pid)) if err != nil { - return 0, fmt.Errorf("could not get Priority: %s", err) + return 0, err + } + defer syscall.CloseHandle(c) + ret, _, err := procGetPriorityClass.Call(uintptr(c)) + if ret == 0 { + return 0, err + } + priority, ok := priorityClasses[int(ret)] + if !ok { + return 0, fmt.Errorf("unknown priority class %s", ret) } - return int32(dst[0].Priority), nil + return priority, nil } func (p *Process) IOnice() (int32, error) { return p.IOniceWithContext(context.Background()) From 258343806a4d3002c09ae9632715b5364df840e0 Mon Sep 17 00:00:00 2001 From: Jose De La O Date: Sat, 15 Jun 2019 15:14:42 -0400 Subject: [PATCH 25/38] Preventing file open and bad defer close. Allocating mem as late as possible --- process/process_linux.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/process/process_linux.go b/process/process_linux.go index c3d2c12..8bac376 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -69,12 +69,10 @@ func (m MemoryMapsStat) String() string { // to get more information about the process. An error will be returned // if the process does not exist. func NewProcess(pid int32) (*Process, error) { - p := &Process{ - Pid: int32(pid), + if _, err := os.Stat(common.HostProc(strconv.Itoa(int(p.Pid)))); err != nil { + return nil, err } - file, err := os.Open(common.HostProc(strconv.Itoa(int(p.Pid)))) - defer file.Close() - return p, err + return &Process{Pid: int32(pid)}, nil } // Ppid returns Parent Process ID of the process. From 2ac72f1fa1a81f78dadfeb5cc4867a356c7da738 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 23 Jun 2019 15:52:01 +0200 Subject: [PATCH 26/38] [process][linux] Fix NewProcess() on Linux Related to #704. Don't break previous API where a Process is always returned, fix undefined variable p. --- process/process_linux.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/process/process_linux.go b/process/process_linux.go index 8bac376..1367b5c 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -69,10 +69,8 @@ func (m MemoryMapsStat) String() string { // to get more information about the process. An error will be returned // if the process does not exist. func NewProcess(pid int32) (*Process, error) { - if _, err := os.Stat(common.HostProc(strconv.Itoa(int(p.Pid)))); err != nil { - return nil, err - } - return &Process{Pid: int32(pid)}, nil + _, err := os.Stat(common.HostProc(strconv.Itoa(int(pid)))) + return &Process{Pid: pid}, err } // Ppid returns Parent Process ID of the process. From 946c9ce6ea57f47a8b7b38687f0924e06953e59a Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 23 Jun 2019 16:14:14 +0200 Subject: [PATCH 27/38] [cpu] Fix #599 cap percent values returned by Percent() between 0 and 100 See https://github.com/shirou/gopsutil/issues/599#issuecomment-491942842 for a repoduction case --- cpu/cpu.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index daf3524..80ce3f0 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "math" "strconv" "strings" "sync" @@ -112,7 +113,7 @@ func calculateBusy(t1, t2 TimesStat) float64 { if t2All <= t1All { return 1 } - return (t2Busy - t1Busy) / (t2All - t1All) * 100 + return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100)) } func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) { From f036e8b9e8fd05d8f4dbe57855f714d0a69a42e4 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 23 Jun 2019 16:53:58 +0200 Subject: [PATCH 28/38] [process][windows] Deduplicate repeated code in CreateToolhelp32Snapshot related functions --- process/process_windows.go | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/process/process_windows.go b/process/process_windows.go index c580ac5..7a7d5a6 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -561,24 +561,19 @@ func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { defer w32.CloseHandle(snap) var pe32 w32.PROCESSENTRY32 pe32.DwSize = uint32(unsafe.Sizeof(pe32)) - if w32.Process32First(snap, &pe32) == false { + if !w32.Process32First(snap, &pe32) { return out, windows.GetLastError() } - - if pe32.Th32ParentProcessID == uint32(p.Pid) { - p, err := NewProcess(int32(pe32.Th32ProcessID)) - if err == nil { - out = append(out, p) - } - } - - for w32.Process32Next(snap, &pe32) { + for { if pe32.Th32ParentProcessID == uint32(p.Pid) { p, err := NewProcess(int32(pe32.Th32ProcessID)) if err == nil { out = append(out, p) } } + if !w32.Process32Next(snap, &pe32) { + break + } } return out, nil } @@ -695,22 +690,19 @@ func getFromSnapProcess(pid int32) (int32, int32, string, error) { defer w32.CloseHandle(snap) var pe32 w32.PROCESSENTRY32 pe32.DwSize = uint32(unsafe.Sizeof(pe32)) - if w32.Process32First(snap, &pe32) == false { + if !w32.Process32First(snap, &pe32) { return 0, 0, "", windows.GetLastError() } - - if pe32.Th32ProcessID == uint32(pid) { - szexe := windows.UTF16ToString(pe32.SzExeFile[:]) - return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil - } - - for w32.Process32Next(snap, &pe32) { + for { if pe32.Th32ProcessID == uint32(pid) { szexe := windows.UTF16ToString(pe32.SzExeFile[:]) return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil } + if !w32.Process32Next(snap, &pe32) { + break + } } - return 0, 0, "", fmt.Errorf("Couldn't find pid: %d", pid) + return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid) } // Get processes From d7405fd87376efc3079c865d3b9977a12ee193f6 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sun, 23 Jun 2019 17:38:42 +0200 Subject: [PATCH 29/38] [disk][linux] Follow symlinks with filepath.EvalSymlinks for LVM volumes See #686 --- disk/disk_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index d0dffa9..415669f 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -299,11 +299,11 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro } if strings.HasPrefix(d.Device, "/dev/mapper/") { - devpath, err := os.Readlink(d.Device) + devpath, err := filepath.EvalSymlinks(d.Device) if err != nil { return nil, err } - d.Device = "/dev/" + filepath.Base(devpath) + d.Device = devpath } // /dev/root is not the real device name From 86fdae99e146944d7f9f59db6087926f446e0b93 Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 6 Jul 2019 11:24:35 +0900 Subject: [PATCH 30/38] [cpu]: return 100 instead 1 if t1 are bigger than t2 --- cpu/cpu.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index 80ce3f0..ec0a85c 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -111,7 +111,7 @@ func calculateBusy(t1, t2 TimesStat) float64 { return 0 } if t2All <= t1All { - return 1 + return 100 } return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100)) } From 80ceab90aa1eb27a37d1aeab98b19eb55067b616 Mon Sep 17 00:00:00 2001 From: Tony Lambiris Date: Sat, 6 Jul 2019 08:33:10 -0400 Subject: [PATCH 31/38] Add support for hfsplus file system --- disk/disk_linux.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 415669f..340f973 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -47,6 +47,7 @@ const ( FUSE_SUPER_MAGIC = 0x65735546 FUTEXFS_SUPER_MAGIC = 0xBAD1DEA HFS_SUPER_MAGIC = 0x4244 + HFSPLUS_SUPER_MAGIC = 0x482b HOSTFS_SUPER_MAGIC = 0x00c0ffee HPFS_SUPER_MAGIC = 0xF995E849 HUGETLBFS_MAGIC = 0x958458f6 @@ -156,6 +157,7 @@ var fsTypeMap = map[int64]string{ GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */ GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */ HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */ + HFSPLUS_SUPER_MAGIC: "hfsplus", /* 0x482b local */ HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */ HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */ MTD_INODE_FS_SUPER_MAGIC: "inodefs", /* 0x11307854 local */ From 2fd3f03f8455356d7e0034fbeed6356dc15c2499 Mon Sep 17 00:00:00 2001 From: WAKAYAMA shirou Date: Sat, 6 Jul 2019 23:25:34 +0900 Subject: [PATCH 32/38] [freebsd][host]: fix Users() return start secs. --- host/freebsd_headers/utxdb.h | 63 ++++++++++++++++++++++++++++++++++++++++++++ host/host_freebsd.go | 5 ++-- host/host_freebsd_amd64.go | 19 +++++-------- host/types_freebsd.go | 8 +++--- 4 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 host/freebsd_headers/utxdb.h diff --git a/host/freebsd_headers/utxdb.h b/host/freebsd_headers/utxdb.h new file mode 100644 index 0000000..912dd0f --- /dev/null +++ b/host/freebsd_headers/utxdb.h @@ -0,0 +1,63 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2010 Ed Schouten + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _UTXDB_H_ +#define _UTXDB_H_ + +#include + +#define _PATH_UTX_ACTIVE "/var/run/utx.active" +#define _PATH_UTX_LASTLOGIN "/var/log/utx.lastlogin" +#define _PATH_UTX_LOG "/var/log/utx.log" + +/* + * Entries in struct futx are ordered by how often they are used. In + * utx.log only entries will be written until the last non-zero byte, + * which means we want to put the hostname at the end. Most primitive + * records only store a ut_type and ut_tv, which means we want to store + * those at the front. + */ + +struct utmpx; + +struct futx { + uint8_t fu_type; + uint64_t fu_tv; + char fu_id[8]; + uint32_t fu_pid; + char fu_user[32]; + char fu_line[16]; + char fu_host[128]; +} __packed; + +void utx_to_futx(const struct utmpx *, struct futx *); +struct utmpx *futx_to_utx(const struct futx *); + +#endif /* !_UTXDB_H_ */ diff --git a/host/host_freebsd.go b/host/host_freebsd.go index 00a8519..9cf654e 100644 --- a/host/host_freebsd.go +++ b/host/host_freebsd.go @@ -7,6 +7,7 @@ import ( "context" "encoding/binary" "io/ioutil" + "math" "os" "runtime" "strings" @@ -143,11 +144,11 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) { b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx] var u Utmpx br := bytes.NewReader(b) - err := binary.Read(br, binary.LittleEndian, &u) + err := binary.Read(br, binary.BigEndian, &u) if err != nil || u.Type != 4 { continue } - sec := (binary.LittleEndian.Uint32(u.Tv.Sec[:])) / 2 // TODO: + sec := math.Floor(float64(u.Tv) / 1000000) user := UserStat{ User: common.IntToString(u.User[:]), Terminal: common.IntToString(u.Line[:]), diff --git a/host/host_freebsd_amd64.go b/host/host_freebsd_amd64.go index 3f015f0..8af74b0 100644 --- a/host/host_freebsd_amd64.go +++ b/host/host_freebsd_amd64.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs types_freebsd.go package host @@ -9,7 +9,7 @@ const ( sizeofInt = 0x4 sizeofLong = 0x8 sizeofLongLong = 0x8 - sizeOfUtmpx = 197 // TODO: why should 197, not 0x118 + sizeOfUtmpx = 0xc5 ) type ( @@ -27,18 +27,11 @@ type Utmp struct { } type Utmpx struct { - Type int16 - Tv Timeval + Type uint8 + Tv uint64 Id [8]int8 - Pid int32 + Pid uint32 User [32]int8 Line [16]int8 - Host [125]int8 - // Host [128]int8 - // X__ut_spare [64]int8 -} - -type Timeval struct { - Sec [4]byte - Usec [3]byte + Host [128]int8 } diff --git a/host/types_freebsd.go b/host/types_freebsd.go index e70677f..bbdce0c 100644 --- a/host/types_freebsd.go +++ b/host/types_freebsd.go @@ -11,6 +11,7 @@ package host #include #include #include +#include "freebsd_headers/utxdb.h" enum { sizeofPtr = sizeof(void*), @@ -27,7 +28,7 @@ const ( sizeofInt = C.sizeof_int sizeofLong = C.sizeof_long sizeofLongLong = C.sizeof_longlong - sizeOfUtmpx = C.sizeof_struct_utmpx + sizeOfUtmpx = C.sizeof_struct_futx ) // Basic types @@ -39,6 +40,5 @@ type ( _C_long_long C.longlong ) -type Utmp C.struct_utmp -type Utmpx C.struct_utmpx -type Timeval C.struct_timeval +type Utmp C.struct_utmp // for FreeBSD 9.0 compatibility +type Utmpx C.struct_futx From 669b2710bfa491adc807476d102c14c57e9ef99c Mon Sep 17 00:00:00 2001 From: WAKAYAMA shirou Date: Sat, 6 Jul 2019 23:49:57 +0900 Subject: [PATCH 33/38] [freebsd]host: change freebsd struct for 386 and arm. --- host/host_freebsd_386.go | 18 ++++++------------ host/host_freebsd_arm.go | 19 ++++++------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/host/host_freebsd_386.go b/host/host_freebsd_386.go index 7f06d8f..88453d2 100644 --- a/host/host_freebsd_386.go +++ b/host/host_freebsd_386.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs types_freebsd.go package host @@ -9,7 +9,7 @@ const ( sizeofInt = 0x4 sizeofLong = 0x4 sizeofLongLong = 0x8 - sizeOfUtmpx = 197 // TODO why should 197 + sizeOfUtmpx = 0xc5 ) type ( @@ -27,17 +27,11 @@ type Utmp struct { } type Utmpx struct { - Type int16 - Tv Timeval + Type uint8 + Tv uint64 Id [8]int8 - Pid int32 + Pid uint32 User [32]int8 Line [16]int8 - Host [125]int8 - // X__ut_spare [64]int8 -} - -type Timeval struct { - Sec [4]byte - Usec [3]byte + Host [128]int8 } diff --git a/host/host_freebsd_arm.go b/host/host_freebsd_arm.go index ac74980..f7d6ede 100644 --- a/host/host_freebsd_arm.go +++ b/host/host_freebsd_arm.go @@ -1,4 +1,4 @@ -// Created by cgo -godefs - DO NOT EDIT +// Code generated by cmd/cgo -godefs; DO NOT EDIT. // cgo -godefs types_freebsd.go package host @@ -9,7 +9,7 @@ const ( sizeofInt = 0x4 sizeofLong = 0x8 sizeofLongLong = 0x8 - sizeOfUtmpx = 197 // TODO: why should 197, not 0x118 + sizeOfUtmpx = 0xc5 ) type ( @@ -27,18 +27,11 @@ type Utmp struct { } type Utmpx struct { - Type int16 - Tv Timeval + Type uint8 + Tv uint64 Id [8]int8 - Pid int32 + Pid uint32 User [32]int8 Line [16]int8 - Host [125]int8 - // Host [128]int8 - // X__ut_spare [64]int8 -} - -type Timeval struct { - Sec [4]byte - Usec [3]byte + Host [128]int8 } From 6a8ab0308ed6ddb7a871c5bb14ed4a5f3d79ac55 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Thu, 11 Jul 2019 00:33:41 +0200 Subject: [PATCH 34/38] [net][linux] Go fmt net/net_linux.go --- net/net_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/net_linux.go b/net/net_linux.go index 873a21a..c8d70ed 100644 --- a/net/net_linux.go +++ b/net/net_linux.go @@ -8,13 +8,13 @@ import ( "encoding/hex" "errors" "fmt" + "io" "io/ioutil" "net" "os" "strconv" "strings" "syscall" - "io" "github.com/shirou/gopsutil/internal/common" ) From 9219f16f03a97b3df85bfd867c2af2faca56b01d Mon Sep 17 00:00:00 2001 From: Lomanic Date: Thu, 11 Jul 2019 22:18:40 +0200 Subject: [PATCH 35/38] [host][linux] Fix #340 return Solus OS as from the "solus" PlatformFamily in Info() --- host/host_linux.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/host/host_linux.go b/host/host_linux.go index d1c5f5d..02ff554 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -356,6 +356,8 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil family = "alpine" case "coreos": family = "coreos" + case "solus": + family = "solus" } return platform, family, version, nil From 3aa75af2ac502b28e078f653497a24d3809c63d4 Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Sat, 13 Jul 2019 00:54:37 +0200 Subject: [PATCH 36/38] [disk][openbsd] Use fallback for openBSD not on amd64 --- disk/disk_fallback.go | 2 +- disk/disk_openbsd.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/disk/disk_fallback.go b/disk/disk_fallback.go index 22eb507..414f280 100644 --- a/disk/disk_fallback.go +++ b/disk/disk_fallback.go @@ -1,4 +1,4 @@ -// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris +// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris openbsd,!amd64 package disk diff --git a/disk/disk_openbsd.go b/disk/disk_openbsd.go index 6fdf386..9d7e125 100644 --- a/disk/disk_openbsd.go +++ b/disk/disk_openbsd.go @@ -1,4 +1,4 @@ -// +build openbsd +// +build openbsd,amd64 package disk From 8abc5387a0f4bb662761d941f2e60aa7b139ed69 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Sat, 13 Jul 2019 23:39:05 +0200 Subject: [PATCH 37/38] [disk][openbsd] Add 386 const and types definitions --- disk/disk_openbsd_386.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 disk/disk_openbsd_386.go diff --git a/disk/disk_openbsd_386.go b/disk/disk_openbsd_386.go new file mode 100644 index 0000000..bee3cc1 --- /dev/null +++ b/disk/disk_openbsd_386.go @@ -0,0 +1,89 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs types_openbsd.go + +package disk + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + sizeofLongDouble = 0x8 + + DEVSTAT_NO_DATA = 0x00 + DEVSTAT_READ = 0x01 + DEVSTAT_WRITE = 0x02 + DEVSTAT_FREE = 0x03 + + MNT_RDONLY = 0x00000001 + MNT_SYNCHRONOUS = 0x00000002 + MNT_NOEXEC = 0x00000004 + MNT_NOSUID = 0x00000008 + MNT_NODEV = 0x00000010 + MNT_ASYNC = 0x00000040 + + MNT_WAIT = 1 + MNT_NOWAIT = 2 + MNT_LAZY = 3 +) + +const ( + sizeOfDiskstats = 0x60 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 + _C_long_double int64 +) + +type Statfs struct { + F_flags uint32 + F_bsize uint32 + F_iosize uint32 + F_blocks uint64 + F_bfree uint64 + F_bavail int64 + F_files uint64 + F_ffree uint64 + F_favail int64 + F_syncwrites uint64 + F_syncreads uint64 + F_asyncwrites uint64 + F_asyncreads uint64 + F_fsid Fsid + F_namemax uint32 + F_owner uint32 + F_ctime uint64 + F_fstypename [16]int8 + F_mntonname [90]int8 + F_mntfromname [90]int8 + F_mntfromspec [90]int8 + Pad_cgo_0 [2]byte + Mount_info [160]byte +} +type Diskstats struct { + Name [16]int8 + Busy int32 + Rxfer uint64 + Wxfer uint64 + Seek uint64 + Rbytes uint64 + Wbytes uint64 + Attachtime Timeval + Timestamp Timeval + Time Timeval +} +type Fsid struct { + Val [2]int32 +} +type Timeval struct { + Sec int64 + Usec int32 +} + +type Diskstat struct{} +type Bintime struct{} From fb73f7095e78ba163904d4f868cc0d308c1d86fb Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Tue, 16 Jul 2019 12:25:53 +0200 Subject: [PATCH 38/38] Revert "[disk][openbsd] Use fallback for openBSD not on amd64" This reverts commit 3aa75af2ac502b28e078f653497a24d3809c63d4. --- disk/disk_fallback.go | 2 +- disk/disk_openbsd.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/disk/disk_fallback.go b/disk/disk_fallback.go index 414f280..22eb507 100644 --- a/disk/disk_fallback.go +++ b/disk/disk_fallback.go @@ -1,4 +1,4 @@ -// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris openbsd,!amd64 +// +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris package disk diff --git a/disk/disk_openbsd.go b/disk/disk_openbsd.go index 9d7e125..6fdf386 100644 --- a/disk/disk_openbsd.go +++ b/disk/disk_openbsd.go @@ -1,4 +1,4 @@ -// +build openbsd,amd64 +// +build openbsd package disk