Merge pull request #2 from shirou/master

merge master
pull/751/head
Snow Fox 6 years ago committed by GitHub
commit 018950cf06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"strconv"
"strings"
"sync"
@ -110,9 +111,9 @@ func calculateBusy(t1, t2 TimesStat) float64 {
return 0
}
if t2All <= t1All {
return 1
return 100
}
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) {

@ -11,6 +11,7 @@ import (
"runtime"
"strconv"
"strings"
"syscall"
"github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
@ -29,6 +30,9 @@ var (
// sys/sysctl.h
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
)
@ -68,6 +72,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)
}
@ -82,13 +102,27 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
ncpu = 1
}
smt, err := smt()
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
}
for i := 0; i < ncpu; i++ {
var cpuTimes = make([]int64, CPUStates)
j := i
if !smt {
j *= 2
}
var cpuTimes = make([]int32, CPUStates)
var mib []int32
if percpu {
mib = []int32{CTLKern, KernCptime}
mib = []int32{CTLKern, KernCptime2, int32(j)}
} else {
mib = []int32{CTLKern, KernCptime2, int32(i)}
mib = []int32{CTLKern, KernCptime}
}
buf, _, err := common.CallSyscall(mib)
if err != nil {
@ -107,10 +141,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"
if percpu {
c.CPU = fmt.Sprintf("cpu%d", j)
} else {
c.CPU = fmt.Sprintf("cpu%d", i)
c.CPU = "cpu-total"
}
ret = append(ret, c)
}
@ -135,10 +169,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
}
var ncpu int32
br := bytes.NewReader(buf)
err = binary.Read(br, binary.LittleEndian, &ncpu)
if err != nil {
return nil, err
}
c.Cores = int32(u32)
c.Cores = ncpu
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
return nil, err

@ -10,8 +10,6 @@ import (
"sort"
"strconv"
"strings"
"github.com/shirou/gopsutil/internal/common"
)
var ClocksPerSec = float64(128)
@ -31,12 +29,97 @@ 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, "-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(string(kstatSysOut), "\n") {
fields := re.Split(line, -1)
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) {

@ -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 */
@ -298,6 +300,14 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
}
}
if strings.HasPrefix(d.Device, "/dev/mapper/") {
devpath, err := filepath.EvalSymlinks(d.Device)
if err != nil {
return nil, err
}
d.Device = 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" {

@ -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{}

@ -0,0 +1,63 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
* 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 <stdint.h>
#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_ */

@ -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
}

@ -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[:]),

@ -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
}

@ -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
}

@ -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
}

@ -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
@ -421,7 +342,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"
@ -435,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
@ -515,116 +438,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("bus/pci/devices")
if common.PathExists(filename) {
contents, err := common.ReadLines(filename)
if err == nil {
if common.StringsContains(contents, "virtio-pci") {
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) {
@ -645,6 +459,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
@ -670,16 +485,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
}
@ -689,5 +507,5 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err
Temperature: temperature / 1000.0,
})
}
return temperatures, nil
return temperatures, warns.Reference()
}

@ -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))
}

@ -11,6 +11,7 @@ package host
#include <sys/types.h>
#include <sys/time.h>
#include <utmpx.h>
#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

@ -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)

@ -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,212 @@ func NumProcs() (uint64, error) {
}
return uint64(len(list)), err
}
// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64
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("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"
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 = trimQuotes(field[1])
case "VERSION":
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
}

@ -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) {

@ -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
}

@ -71,6 +71,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,
@ -109,6 +199,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())
}

@ -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.

@ -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)
}

@ -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.

@ -8,6 +8,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"os"
@ -18,6 +19,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 +253,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",
@ -581,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

@ -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
}
}

@ -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.

@ -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.
@ -206,6 +291,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.

@ -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"
@ -70,12 +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) {
p := &Process{
Pid: int32(pid),
}
file, err := os.Open(common.HostProc(strconv.Itoa(int(p.Pid))))
defer file.Close()
return p, err
_, err := os.Stat(common.HostProc(strconv.Itoa(int(pid))))
return &Process{Pid: pid}, err
}
// Ppid returns Parent Process ID of the process.
@ -1236,7 +1231,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui
System: float64(stime / ClockTicks),
}
bootTime, _ := host.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

@ -25,12 +25,16 @@ 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")
procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass")
)
type SystemProcessInformation struct {
@ -234,24 +238,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) {
@ -382,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
}
return int32(dst[0].Priority), nil
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 priority, nil
}
func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
@ -551,24 +584,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
}
@ -685,22 +713,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

Loading…
Cancel
Save