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

@ -11,6 +11,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -29,6 +30,9 @@ var (
// sys/sysctl.h // sys/sysctl.h
const ( const (
CTLKern = 1 // "high kernel": proc, limits CTLKern = 1 // "high kernel": proc, limits
CTLHw = 6 // CTL_HW
SMT = 24 // HW_SMT
NCpuOnline = 25 // HW_NCPUONLINE
KernCptime = 40 // KERN_CPTIME KernCptime = 40 // KERN_CPTIME
KernCptime2 = 71 // KERN_CPTIME2 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) { func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu) return TimesWithContext(context.Background(), percpu)
} }
@ -82,13 +102,27 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
ncpu = 1 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++ { 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 var mib []int32
if percpu { if percpu {
mib = []int32{CTLKern, KernCptime} mib = []int32{CTLKern, KernCptime2, int32(j)}
} else { } else {
mib = []int32{CTLKern, KernCptime2, int32(i)} mib = []int32{CTLKern, KernCptime}
} }
buf, _, err := common.CallSyscall(mib) buf, _, err := common.CallSyscall(mib)
if err != nil { if err != nil {
@ -107,10 +141,10 @@ func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec, Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec,
Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec, Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec,
} }
if !percpu { if percpu {
c.CPU = "cpu-total" c.CPU = fmt.Sprintf("cpu%d", j)
} else { } else {
c.CPU = fmt.Sprintf("cpu%d", i) c.CPU = "cpu-total"
} }
ret = append(ret, c) ret = append(ret, c)
} }
@ -135,10 +169,19 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
} }
c.Mhz = float64(u32) 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 return nil, err
} }
c.Cores = int32(u32) c.Cores = ncpu
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
return nil, err return nil, err

@ -10,8 +10,6 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"github.com/shirou/gopsutil/internal/common"
) )
var ClocksPerSec = float64(128) 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) { func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu) return TimesWithContext(context.Background(), percpu)
} }
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { 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) { func Info() ([]InfoStat, error) {

@ -47,6 +47,7 @@ const (
FUSE_SUPER_MAGIC = 0x65735546 FUSE_SUPER_MAGIC = 0x65735546
FUTEXFS_SUPER_MAGIC = 0xBAD1DEA FUTEXFS_SUPER_MAGIC = 0xBAD1DEA
HFS_SUPER_MAGIC = 0x4244 HFS_SUPER_MAGIC = 0x4244
HFSPLUS_SUPER_MAGIC = 0x482b
HOSTFS_SUPER_MAGIC = 0x00c0ffee HOSTFS_SUPER_MAGIC = 0x00c0ffee
HPFS_SUPER_MAGIC = 0xF995E849 HPFS_SUPER_MAGIC = 0xF995E849
HUGETLBFS_MAGIC = 0x958458f6 HUGETLBFS_MAGIC = 0x958458f6
@ -156,6 +157,7 @@ var fsTypeMap = map[int64]string{
GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */ GFS_SUPER_MAGIC: "gfs/gfs2", /* 0x1161970 remote */
GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */ GPFS_SUPER_MAGIC: "gpfs", /* 0x47504653 remote */
HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */ HFS_SUPER_MAGIC: "hfs", /* 0x4244 local */
HFSPLUS_SUPER_MAGIC: "hfsplus", /* 0x482b local */
HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */ HPFS_SUPER_MAGIC: "hpfs", /* 0xF995E849 local */
HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */ HUGETLBFS_MAGIC: "hugetlbfs", /* 0x958458F6 local */
MTD_INODE_FS_SUPER_MAGIC: "inodefs", /* 0x11307854 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 // /dev/root is not the real device name
// so we get the real device name from its major/minor number // so we get the real device name from its major/minor number
if d.Device == "/dev/root" { 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))) 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 return platform, family, pver, nil
} }

@ -7,6 +7,7 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"io/ioutil" "io/ioutil"
"math"
"os" "os"
"runtime" "runtime"
"strings" "strings"
@ -143,11 +144,11 @@ func UsersWithContext(ctx context.Context) ([]UserStat, error) {
b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx] b := buf[i*sizeOfUtmpx : (i+1)*sizeOfUtmpx]
var u Utmpx var u Utmpx
br := bytes.NewReader(b) br := bytes.NewReader(b)
err := binary.Read(br, binary.LittleEndian, &u) err := binary.Read(br, binary.BigEndian, &u)
if err != nil || u.Type != 4 { if err != nil || u.Type != 4 {
continue continue
} }
sec := (binary.LittleEndian.Uint32(u.Tv.Sec[:])) / 2 // TODO: sec := math.Floor(float64(u.Tv) / 1000000)
user := UserStat{ user := UserStat{
User: common.IntToString(u.User[:]), User: common.IntToString(u.User[:]),
Terminal: common.IntToString(u.Line[:]), 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 // cgo -godefs types_freebsd.go
package host package host
@ -9,7 +9,7 @@ const (
sizeofInt = 0x4 sizeofInt = 0x4
sizeofLong = 0x4 sizeofLong = 0x4
sizeofLongLong = 0x8 sizeofLongLong = 0x8
sizeOfUtmpx = 197 // TODO why should 197 sizeOfUtmpx = 0xc5
) )
type ( type (
@ -27,17 +27,11 @@ type Utmp struct {
} }
type Utmpx struct { type Utmpx struct {
Type int16 Type uint8
Tv Timeval Tv uint64
Id [8]int8 Id [8]int8
Pid int32 Pid uint32
User [32]int8 User [32]int8
Line [16]int8 Line [16]int8
Host [125]int8 Host [128]int8
// X__ut_spare [64]int8
}
type Timeval struct {
Sec [4]byte
Usec [3]byte
} }

@ -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 // cgo -godefs types_freebsd.go
package host package host
@ -9,7 +9,7 @@ const (
sizeofInt = 0x4 sizeofInt = 0x4
sizeofLong = 0x8 sizeofLong = 0x8
sizeofLongLong = 0x8 sizeofLongLong = 0x8
sizeOfUtmpx = 197 // TODO: why should 197, not 0x118 sizeOfUtmpx = 0xc5
) )
type ( type (
@ -27,18 +27,11 @@ type Utmp struct {
} }
type Utmpx struct { type Utmpx struct {
Type int16 Type uint8
Tv Timeval Tv uint64
Id [8]int8 Id [8]int8
Pid int32 Pid uint32
User [32]int8 User [32]int8
Line [16]int8 Line [16]int8
Host [125]int8 Host [128]int8
// Host [128]int8
// X__ut_spare [64]int8
}
type Timeval struct {
Sec [4]byte
Usec [3]byte
} }

@ -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 // cgo -godefs types_freebsd.go
package host package host
@ -9,7 +9,7 @@ const (
sizeofInt = 0x4 sizeofInt = 0x4
sizeofLong = 0x8 sizeofLong = 0x8
sizeofLongLong = 0x8 sizeofLongLong = 0x8
sizeOfUtmpx = 197 // TODO: why should 197, not 0x118 sizeOfUtmpx = 0xc5
) )
type ( type (
@ -27,18 +27,11 @@ type Utmp struct {
} }
type Utmpx struct { type Utmpx struct {
Type int16 Type uint8
Tv Timeval Tv uint64
Id [8]int8 Id [8]int8
Pid int32 Pid uint32
User [32]int8 User [32]int8
Line [16]int8 Line [16]int8
Host [125]int8 Host [128]int8
// Host [128]int8
// X__ut_spare [64]int8
}
type Timeval struct {
Sec [4]byte
Usec [3]byte
} }

@ -15,7 +15,6 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
@ -105,71 +104,13 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) {
return ret, nil 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. // BootTime returns the system boot time expressed in seconds since the epoch.
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
return BootTimeWithContext(context.Background()) return BootTimeWithContext(context.Background())
} }
func BootTimeWithContext(ctx context.Context) (uint64, error) { func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime) return common.BootTimeWithContext(ctx)
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")
} }
func uptime(boot uint64) uint64 { 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) { func getLSB() (*LSB, error) {
ret := &LSB{} ret := &LSB{}
if common.PathExists(common.HostEtc("lsb-release")) { if common.PathExists(common.HostEtc("lsb-release")) {
@ -392,7 +313,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
version = contents[0] version = contents[0]
} }
} else if common.PathExists(common.HostEtc("os-release")) { } else if common.PathExists(common.HostEtc("os-release")) {
p, v, err := getOSRelease() p, v, err := common.GetOSRelease()
if err == nil { if err == nil {
platform = p platform = p
version = v version = v
@ -421,7 +342,7 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
family = "fedora" family = "fedora"
case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm": case "oracle", "centos", "redhat", "scientific", "enterpriseenterprise", "amazon", "xenserver", "cloudlinux", "ibm_powerkvm":
family = "rhel" family = "rhel"
case "suse", "opensuse": case "suse", "opensuse", "sles":
family = "suse" family = "suse"
case "gentoo": case "gentoo":
family = "gentoo" family = "gentoo"
@ -435,6 +356,8 @@ func PlatformInformationWithContext(ctx context.Context) (platform string, famil
family = "alpine" family = "alpine"
case "coreos": case "coreos":
family = "coreos" family = "coreos"
case "solus":
family = "solus"
} }
return platform, family, version, nil return platform, family, version, nil
@ -515,116 +438,7 @@ func Virtualization() (string, string, error) {
} }
func VirtualizationWithContext(ctx context.Context) (string, string, error) { func VirtualizationWithContext(ctx context.Context) (string, string, error) {
var system string return common.VirtualizationWithContext(ctx)
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
} }
func SensorsTemperatures() ([]TemperatureStat, error) { func SensorsTemperatures() ([]TemperatureStat, error) {
@ -645,6 +459,7 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err
return temperatures, err return temperatures, err
} }
} }
var warns Warnings
// example directory // example directory
// device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm // 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 // Get the name of the temperature you are reading
name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name")) name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name"))
if err != nil { if err != nil {
return temperatures, err warns.Add(err)
continue
} }
// Get the temperature reading // Get the temperature reading
current, err := ioutil.ReadFile(file) current, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
return temperatures, err warns.Add(err)
continue
} }
temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64) temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64)
if err != nil { if err != nil {
warns.Add(err)
continue continue
} }
@ -689,5 +507,5 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err
Temperature: temperature / 1000.0, 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/types.h>
#include <sys/time.h> #include <sys/time.h>
#include <utmpx.h> #include <utmpx.h>
#include "freebsd_headers/utxdb.h"
enum { enum {
sizeofPtr = sizeof(void*), sizeofPtr = sizeof(void*),
@ -27,7 +28,7 @@ const (
sizeofInt = C.sizeof_int sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong sizeofLongLong = C.sizeof_longlong
sizeOfUtmpx = C.sizeof_struct_utmpx sizeOfUtmpx = C.sizeof_struct_futx
) )
// Basic types // Basic types
@ -39,6 +40,5 @@ type (
_C_long_long C.longlong _C_long_long C.longlong
) )
type Utmp C.struct_utmp type Utmp C.struct_utmp // for FreeBSD 9.0 compatibility
type Utmpx C.struct_utmpx type Utmpx C.struct_futx
type Timeval C.struct_timeval

@ -213,6 +213,12 @@ func ReadInts(filename string) ([]int64, error) {
return ret, nil 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 // Parse to int32 without error
func mustParseInt32(val string) int32 { func mustParseInt32(val string) int32 {
vv, _ := strconv.ParseInt(val, 10, 32) vv, _ := strconv.ParseInt(val, 10, 32)

@ -3,9 +3,15 @@
package common package common
import ( import (
"context"
"fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strconv"
"strings" "strings"
"sync/atomic"
"time"
) )
func DoSysctrl(mib string) ([]string, error) { func DoSysctrl(mib string) ([]string, error) {
@ -39,3 +45,212 @@ func NumProcs() (uint64, error) {
} }
return uint64(len(list)), err 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) { func TestmustParseInt32(t *testing.T) {
ret := mustParseInt32("11111") ret := mustParseInt32("11111")
if ret != int32(11111) { if ret != int32(11111) {

@ -4,6 +4,9 @@ package common
import ( import (
"context" "context"
"path/filepath"
"strings"
"syscall"
"unsafe" "unsafe"
"github.com/StackExchange/wmi" "github.com/StackExchange/wmi"
@ -59,6 +62,8 @@ var (
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData") PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue") PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery") PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
) )
type FILETIME struct { type FILETIME struct {
@ -133,3 +138,23 @@ func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, con
return err 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"` 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{ var constMap = map[string]int{
"unix": syscall.AF_UNIX, "unix": syscall.AF_UNIX,
"TCP": syscall.SOCK_STREAM, "TCP": syscall.SOCK_STREAM,
@ -109,6 +199,11 @@ func (n InterfaceAddr) String() string {
return string(s) return string(s)
} }
func (n ConntrackStat) String() string {
s, _ := json.Marshal(n)
return string(s)
}
func Interfaces() ([]InterfaceStat, error) { func Interfaces() ([]InterfaceStat, error) {
return InterfacesWithContext(context.Background()) return InterfacesWithContext(context.Background())
} }

@ -6,6 +6,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/shirou/gopsutil/internal/common"
"os/exec" "os/exec"
"regexp" "regexp"
"strconv" "strconv"
@ -271,6 +272,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
return nil, errors.New("NetFilterCounters not implemented for darwin") 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 // NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise // If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned. // just the protocols in the list are returned.

@ -24,6 +24,14 @@ func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
return []FilterStat{}, common.ErrNotImplementedError 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) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols) 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") 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 // NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise // If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned. // just the protocols in the list are returned.

@ -8,6 +8,7 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -18,6 +19,26 @@ import (
"github.com/shirou/gopsutil/internal/common" "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 // NetIOCounters returnes network I/O statistics for every network
// interface installed on the system. If pernic argument is false, // interface installed on the system. If pernic argument is false,
// return only sum of all information (which name is 'all'). If true, // 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 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 // http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
var TCPStatuses = map[string]string{ var TCPStatuses = map[string]string{
"01": "ESTABLISHED", "01": "ESTABLISHED",
@ -581,7 +654,7 @@ func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
t, err := getProcInodes(root, pid, max) t, err := getProcInodes(root, pid, max)
if err != nil { if err != nil {
// skip if permission error or no longer exists // 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 continue
} }
return ret, err return ret, err

@ -161,3 +161,142 @@ func TestReverse(t *testing.T) {
src := []byte{0x01, 0x02, 0x03} src := []byte{0x01, 0x02, 0x03}
assert.Equal(t, []byte{0x03, 0x02, 0x01}, Reverse(src)) 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") 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 // NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise // If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned. // just the protocols in the list are returned.

@ -19,6 +19,7 @@ var (
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable") procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable")
procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable")
procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2")
) )
const ( const (
@ -73,6 +74,65 @@ var netConnectionKindMap = map[string][]netConnectionKindType{
"inet6": {kindTCP6, kindUDP6}, "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) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), pernic) return IOCountersWithContext(context.Background(), pernic)
} }
@ -82,17 +142,41 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
if err != nil { if err != nil {
return nil, err 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,
}
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 { for _, ifi := range ifs {
c := IOCountersStat{ c := IOCountersStat{
Name: ifi.Name, Name: ifi.Name,
} }
row := windows.MibIfRow{Index: uint32(ifi.Index)} row := windows.MibIfRow{Index: uint32(ifi.Index)}
e := windows.GetIfEntry(&row) err = windows.GetIfEntry(&row)
if e != nil { if err != nil {
return nil, os.NewSyscallError("GetIfEntry", e) return nil, os.NewSyscallError("GetIfEntry", err)
} }
c.BytesSent = uint64(row.OutOctets) c.BytesSent = uint64(row.OutOctets)
c.BytesRecv = uint64(row.InOctets) c.BytesRecv = uint64(row.InOctets)
@ -103,13 +187,14 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat,
c.Dropin = uint64(row.InDiscards) c.Dropin = uint64(row.InDiscards)
c.Dropout = uint64(row.OutDiscards) c.Dropout = uint64(row.OutDiscards)
ret = append(ret, c) counters = append(counters, c)
}
} }
if pernic == false { if !pernic {
return getIOCountersAll(ret) return getIOCountersAll(counters)
} }
return ret, nil return counters, nil
} }
// NetIOCountersByFile is an method which is added just a compatibility for linux. // 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") 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 // NetProtoCounters returns network statistics for the entire system
// If protocols is empty then all protocols are returned, otherwise // If protocols is empty then all protocols are returned, otherwise
// just the protocols in the list are returned. // just the protocols in the list are returned.

@ -16,7 +16,6 @@ import (
"strings" "strings"
"github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"github.com/shirou/gopsutil/net" "github.com/shirou/gopsutil/net"
"golang.org/x/sys/unix" "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 // to get more information about the process. An error will be returned
// if the process does not exist. // if the process does not exist.
func NewProcess(pid int32) (*Process, error) { func NewProcess(pid int32) (*Process, error) {
p := &Process{ _, err := os.Stat(common.HostProc(strconv.Itoa(int(pid))))
Pid: int32(pid), return &Process{Pid: pid}, err
}
file, err := os.Open(common.HostProc(strconv.Itoa(int(p.Pid))))
defer file.Close()
return p, err
} }
// Ppid returns Parent Process ID of the process. // 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), System: float64(stime / ClockTicks),
} }
bootTime, _ := host.BootTime() bootTime, _ := common.BootTimeWithContext(ctx)
t, err := strconv.ParseUint(fields[i+20], 10, 64) t, err := strconv.ParseUint(fields[i+20], 10, 64)
if err != nil { if err != nil {
return 0, 0, nil, 0, 0, 0, nil, err return 0, 0, nil, 0, 0, 0, nil, err

@ -27,10 +27,14 @@ const (
var ( var (
modpsapi = windows.NewLazySystemDLL("psapi.dll") modpsapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW")
advapi32 = windows.NewLazySystemDLL("advapi32.dll") advapi32 = windows.NewLazySystemDLL("advapi32.dll")
procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW") procLookupPrivilegeValue = advapi32.NewProc("LookupPrivilegeValueW")
procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges") procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges")
procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW")
procGetPriorityClass = common.Modkernel32.NewProc("GetPriorityClass")
) )
type SystemProcessInformation struct { type SystemProcessInformation struct {
@ -234,24 +238,31 @@ func (p *Process) Exe() (string, error) {
} }
func (p *Process) ExeWithContext(ctx context.Context) (string, error) { func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
if p.Pid != 0 { // 0 or null is the current process for CreateToolhelp32Snapshot // 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION
snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPMODULE|w32.TH32CS_SNAPMODULE32, uint32(p.Pid)) c, err := syscall.OpenProcess(0x1000, false, uint32(p.Pid))
if snap != 0 { // don't report errors here, fallback to WMI instead if err != nil {
defer w32.CloseHandle(snap) return "", err
var me32 w32.MODULEENTRY32
me32.Size = uint32(unsafe.Sizeof(me32))
if w32.Module32First(snap, &me32) {
szexepath := windows.UTF16ToString(me32.SzExePath[:])
return szexepath, nil
} }
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) // XP fallback
if err != nil { ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size))
return "", fmt.Errorf("could not get ExecutablePath: %s", err) if ret == 0 {
return "", err
} }
return *dst[0].ExecutablePath, nil return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
@ -382,17 +393,39 @@ func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError 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 // Nice returns priority in Windows
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
return p.NiceWithContext(context.Background()) return p.NiceWithContext(context.Background())
} }
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) { 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 { 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) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background()) return p.IOniceWithContext(context.Background())
@ -551,23 +584,18 @@ func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
defer w32.CloseHandle(snap) defer w32.CloseHandle(snap)
var pe32 w32.PROCESSENTRY32 var pe32 w32.PROCESSENTRY32
pe32.DwSize = uint32(unsafe.Sizeof(pe32)) pe32.DwSize = uint32(unsafe.Sizeof(pe32))
if w32.Process32First(snap, &pe32) == false { if !w32.Process32First(snap, &pe32) {
return out, windows.GetLastError() return out, windows.GetLastError()
} }
for {
if pe32.Th32ParentProcessID == uint32(p.Pid) { if pe32.Th32ParentProcessID == uint32(p.Pid) {
p, err := NewProcess(int32(pe32.Th32ProcessID)) p, err := NewProcess(int32(pe32.Th32ProcessID))
if err == nil { if err == nil {
out = append(out, p) out = append(out, p)
} }
} }
if !w32.Process32Next(snap, &pe32) {
for w32.Process32Next(snap, &pe32) { break
if pe32.Th32ParentProcessID == uint32(p.Pid) {
p, err := NewProcess(int32(pe32.Th32ProcessID))
if err == nil {
out = append(out, p)
}
} }
} }
return out, nil return out, nil
@ -685,22 +713,19 @@ func getFromSnapProcess(pid int32) (int32, int32, string, error) {
defer w32.CloseHandle(snap) defer w32.CloseHandle(snap)
var pe32 w32.PROCESSENTRY32 var pe32 w32.PROCESSENTRY32
pe32.DwSize = uint32(unsafe.Sizeof(pe32)) pe32.DwSize = uint32(unsafe.Sizeof(pe32))
if w32.Process32First(snap, &pe32) == false { if !w32.Process32First(snap, &pe32) {
return 0, 0, "", windows.GetLastError() return 0, 0, "", windows.GetLastError()
} }
for {
if pe32.Th32ProcessID == uint32(pid) { if pe32.Th32ProcessID == uint32(pid) {
szexe := windows.UTF16ToString(pe32.SzExeFile[:]) szexe := windows.UTF16ToString(pe32.SzExeFile[:])
return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil
} }
if !w32.Process32Next(snap, &pe32) {
for w32.Process32Next(snap, &pe32) { break
if pe32.Th32ProcessID == uint32(pid) {
szexe := windows.UTF16ToString(pe32.SzExeFile[:])
return int32(pe32.Th32ParentProcessID), int32(pe32.CntThreads), szexe, nil
} }
} }
return 0, 0, "", fmt.Errorf("Couldn't find pid: %d", pid) return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid)
} }
// Get processes // Get processes

Loading…
Cancel
Save