vendor: upgrade to latest version of gopsutil

This commit is contained in:
Jack Pearkes 2018-10-19 11:33:23 -07:00
parent a20804df28
commit 0358c7a063
80 changed files with 5223 additions and 1489 deletions

View File

@ -6,18 +6,18 @@ function refresh_docker_images {
# Return: # Return:
# 0 - success # 0 - success
# * - failure # * - failure
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. refresh_docker_images must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. refresh_docker_images must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local sdir="$1" local sdir="$1"
local targets="$2" local targets="$2"
test -n "${targets}" || targets="docker-images" test -n "${targets}" || targets="docker-images"
make -C "${sdir}" ${targets} make -C "${sdir}" ${targets}
return $? return $?
} }
@ -34,30 +34,30 @@ function build_ui {
# #
# Notes: # Notes:
# Use the GIT_COMMIT environment variable to pass off to the build # Use the GIT_COMMIT environment variable to pass off to the build
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. build_ui must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. build_ui must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local image_name=${UI_BUILD_CONTAINER_DEFAULT} local image_name=${UI_BUILD_CONTAINER_DEFAULT}
if test -n "$2" if test -n "$2"
then then
image_name="$2" image_name="$2"
fi fi
local sdir="$1" local sdir="$1"
local ui_dir="${1}/ui-v2" local ui_dir="${1}/ui-v2"
# parse the version # parse the version
version=$(parse_version "${sdir}") version=$(parse_version "${sdir}")
if test -n "$3" if test -n "$3"
then then
version="$3" version="$3"
fi fi
local commit_hash="${GIT_COMMIT}" local commit_hash="${GIT_COMMIT}"
if test -z "${commit_hash}" if test -z "${commit_hash}"
then then
@ -68,10 +68,10 @@ function build_ui {
then then
logo_type="enterprise" logo_type="enterprise"
fi fi
# make sure we run within the ui dir # make sure we run within the ui dir
pushd ${ui_dir} > /dev/null pushd ${ui_dir} > /dev/null
status "Creating the UI Build Container with image: ${image_name} and version '${version}'" status "Creating the UI Build Container with image: ${image_name} and version '${version}'"
local container_id=$(docker create -it -e "CONSUL_GIT_SHA=${commit_hash}" -e "CONSUL_VERSION=${version}" -e "CONSUL_BINARY_TYPE=${CONSUL_BINARY_TYPE}" ${image_name}) local container_id=$(docker create -it -e "CONSUL_GIT_SHA=${commit_hash}" -e "CONSUL_VERSION=${version}" -e "CONSUL_BINARY_TYPE=${CONSUL_BINARY_TYPE}" ${image_name})
local ret=$? local ret=$?
@ -115,9 +115,9 @@ function build_ui {
then then
rm -rf ${1}/pkg/web_ui/v2 rm -rf ${1}/pkg/web_ui/v2
mkdir -p ${1}/pkg/web_ui mkdir -p ${1}/pkg/web_ui
cp -r ${1}/ui-v2/dist ${1}/pkg/web_ui/v2 cp -r ${1}/ui-v2/dist ${1}/pkg/web_ui/v2
fi fi
popd > /dev/null popd > /dev/null
return $ret return $ret
} }
@ -130,22 +130,22 @@ function build_ui_legacy {
# Returns: # Returns:
# 0 - success # 0 - success
# * - error # * - error
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. build_ui_legacy must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. build_ui_legacy must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local sdir="$1" local sdir="$1"
local ui_legacy_dir="${sdir}/ui" local ui_legacy_dir="${sdir}/ui"
local image_name=${UI_LEGACY_BUILD_CONTAINER_DEFAULT} local image_name=${UI_LEGACY_BUILD_CONTAINER_DEFAULT}
if test -n "$2" if test -n "$2"
then then
image_name="$2" image_name="$2"
fi fi
pushd ${ui_legacy_dir} > /dev/null pushd ${ui_legacy_dir} > /dev/null
status "Creating the Legacy UI Build Container with image: ${image_name}" status "Creating the Legacy UI Build Container with image: ${image_name}"
rm -r ${sdir}/pkg/web_ui/v1 >/dev/null 2>&1 rm -r ${sdir}/pkg/web_ui/v1 >/dev/null 2>&1
@ -157,9 +157,9 @@ function build_ui_legacy {
status "Copying the source from '${ui_legacy_dir}' to /consul-src/ui within the container" status "Copying the source from '${ui_legacy_dir}' to /consul-src/ui within the container"
( (
docker cp . ${container_id}:/consul-src/ui && docker cp . ${container_id}:/consul-src/ui &&
status "Running build in container" && status "Running build in container" &&
docker start -i ${container_id} && docker start -i ${container_id} &&
status "Copying back artifacts" && status "Copying back artifacts" &&
docker cp ${container_id}:/consul-src/pkg/web_ui/v1/. ${sdir}/pkg/web_ui/v1 docker cp ${container_id}:/consul-src/pkg/web_ui/v1/. ${sdir}/pkg/web_ui/v1
) )
ret=$? ret=$?
@ -180,20 +180,20 @@ function build_assetfs {
# #
# Note: # Note:
# The GIT_COMMIT, GIT_DIRTY and GIT_DESCRIBE environment variables will be used if present # The GIT_COMMIT, GIT_DIRTY and GIT_DESCRIBE environment variables will be used if present
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. build_assetfs must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. build_assetfs must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local sdir="$1" local sdir="$1"
local image_name=${GO_BUILD_CONTAINER_DEFAULT} local image_name=${GO_BUILD_CONTAINER_DEFAULT}
if test -n "$2" if test -n "$2"
then then
image_name="$2" image_name="$2"
fi fi
pushd ${sdir} > /dev/null pushd ${sdir} > /dev/null
status "Creating the Go Build Container with image: ${image_name}" status "Creating the Go Build Container with image: ${image_name}"
local container_id=$(docker create -it -e GIT_COMMIT=${GIT_COMMIT} -e GIT_DIRTY=${GIT_DIRTY} -e GIT_DESCRIBE=${GIT_DESCRIBE} ${image_name} make static-assets ASSETFS_PATH=bindata_assetfs.go) local container_id=$(docker create -it -e GIT_COMMIT=${GIT_COMMIT} -e GIT_DIRTY=${GIT_DIRTY} -e GIT_DESCRIBE=${GIT_DESCRIBE} ${image_name} make static-assets ASSETFS_PATH=bindata_assetfs.go)
@ -206,11 +206,11 @@ function build_assetfs {
status "Running build in container" && docker start -i ${container_id} && status "Running build in container" && docker start -i ${container_id} &&
status "Copying back artifacts" && docker cp ${container_id}:/go/src/github.com/hashicorp/consul/bindata_assetfs.go ${sdir}/agent/bindata_assetfs.go status "Copying back artifacts" && docker cp ${container_id}:/go/src/github.com/hashicorp/consul/bindata_assetfs.go ${sdir}/agent/bindata_assetfs.go
) )
ret=$? ret=$?
docker rm ${container_id} > /dev/null docker rm ${container_id} > /dev/null
fi fi
popd >/dev/null popd >/dev/null
return $ret return $ret
} }
function build_consul_post { function build_consul_post {
@ -226,25 +226,25 @@ function build_consul_post {
# pkg/bin is where to place binary packages # pkg/bin is where to place binary packages
# pkg.bin.new is where the just built binaries are located # pkg.bin.new is where the just built binaries are located
# bin is where to place the local systems versions # bin is where to place the local systems versions
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. build_consul_post must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. build_consul_post must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local sdir="$1" local sdir="$1"
local extra_dir_name="$2" local extra_dir_name="$2"
local extra_dir="" local extra_dir=""
if test -n "${extra_dir_name}" if test -n "${extra_dir_name}"
then then
extra_dir="${extra_dir_name}/" extra_dir="${extra_dir_name}/"
fi fi
pushd "${sdir}" > /dev/null pushd "${sdir}" > /dev/null
# recreate the pkg dir # recreate the pkg dir
rm -r pkg/bin/${extra_dir}* 2> /dev/null rm -r pkg/bin/${extra_dir}* 2> /dev/null
mkdir -p pkg/bin/${extra_dir} 2> /dev/null mkdir -p pkg/bin/${extra_dir} 2> /dev/null
@ -252,20 +252,20 @@ function build_consul_post {
# move all files in pkg.new into pkg # move all files in pkg.new into pkg
cp -r pkg.bin.new/${extra_dir}* pkg/bin/${extra_dir} cp -r pkg.bin.new/${extra_dir}* pkg/bin/${extra_dir}
rm -r pkg.bin.new rm -r pkg.bin.new
DEV_PLATFORM="./pkg/bin/${extra_dir}$(go env GOOS)_$(go env GOARCH)" DEV_PLATFORM="./pkg/bin/${extra_dir}$(go env GOOS)_$(go env GOARCH)"
for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f 2>/dev/null) for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f 2>/dev/null)
do do
# recreate the bin dir # recreate the bin dir
rm -r bin/* 2> /dev/null rm -r bin/* 2> /dev/null
mkdir -p bin 2> /dev/null mkdir -p bin 2> /dev/null
cp ${F} bin/ cp ${F} bin/
cp ${F} ${MAIN_GOPATH}/bin cp ${F} ${MAIN_GOPATH}/bin
done done
popd > /dev/null popd > /dev/null
return 0 return 0
} }
@ -284,13 +284,13 @@ function build_consul {
# If the CONSUL_DEV environment var is truthy only the local platform/architecture is built. # If the CONSUL_DEV environment var is truthy only the local platform/architecture is built.
# If the XC_OS or the XC_ARCH environment vars are present then only those platforms/architectures # If the XC_OS or the XC_ARCH environment vars are present then only those platforms/architectures
# will be built. Otherwise all supported platform/architectures are built # will be built. Otherwise all supported platform/architectures are built
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local sdir="$1" local sdir="$1"
local extra_dir_name="$2" local extra_dir_name="$2"
local extra_dir="" local extra_dir=""
@ -298,8 +298,8 @@ function build_consul {
if test -n "$3" if test -n "$3"
then then
image_name="$3" image_name="$3"
fi fi
pushd ${sdir} > /dev/null pushd ${sdir} > /dev/null
status "Creating the Go Build Container with image: ${image_name}" status "Creating the Go Build Container with image: ${image_name}"
if is_set "${CONSUL_DEV}" if is_set "${CONSUL_DEV}"
@ -308,7 +308,7 @@ function build_consul {
then then
XC_OS=$(go env GOOS) XC_OS=$(go env GOOS)
fi fi
if test -z "${XC_ARCH}" if test -z "${XC_ARCH}"
then then
XC_ARCH=$(go env GOARCH) XC_ARCH=$(go env GOARCH)
@ -337,7 +337,7 @@ function build_consul {
) )
ret=$? ret=$?
docker rm ${container_id} > /dev/null docker rm ${container_id} > /dev/null
if test $ret -eq 0 if test $ret -eq 0
then then
build_consul_post "${sdir}" "${extra_dir_name}" build_consul_post "${sdir}" "${extra_dir_name}"
@ -346,7 +346,7 @@ function build_consul {
rm -r pkg.bin.new 2> /dev/null rm -r pkg.bin.new 2> /dev/null
fi fi
fi fi
popd > /dev/null popd > /dev/null
return $ret return $ret
} }
@ -368,24 +368,24 @@ function build_consul_local {
# will be built. Otherwise all supported platform/architectures are built # will be built. Otherwise all supported platform/architectures are built
# The NOGOX environment variable will be used if present. This will prevent using gox and instead # The NOGOX environment variable will be used if present. This will prevent using gox and instead
# build with go install # build with go install
if ! test -d "$1" if ! test -d "$1"
then then
err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'" err "ERROR: '$1' is not a directory. build_consul must be called with the path to the top level source as the first argument'"
return 1 return 1
fi fi
local sdir="$1" local sdir="$1"
local build_os="$2" local build_os="$2"
local build_arch="$3" local build_arch="$3"
local extra_dir_name="$4" local extra_dir_name="$4"
local extra_dir="" local extra_dir=""
if test -n "${extra_dir_name}" if test -n "${extra_dir_name}"
then then
extra_dir="${extra_dir_name}/" extra_dir="${extra_dir_name}/"
fi fi
pushd ${sdir} > /dev/null pushd ${sdir} > /dev/null
if is_set "${CONSUL_DEV}" if is_set "${CONSUL_DEV}"
then then

View File

@ -1,6 +1,7 @@
package cpu package cpu
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"runtime" "runtime"
@ -12,6 +13,9 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
// TimesStat contains the amounts of time the CPU has spent performing different
// kinds of work. Time units are in USER_HZ or Jiffies (typically hundredths of
// a second). It is based on linux /proc/stat file.
type TimesStat struct { type TimesStat struct {
CPU string `json:"cpu"` CPU string `json:"cpu"`
User float64 `json:"user"` User float64 `json:"user"`
@ -40,6 +44,7 @@ type InfoStat struct {
Mhz float64 `json:"mhz"` Mhz float64 `json:"mhz"`
CacheSize int32 `json:"cacheSize"` CacheSize int32 `json:"cacheSize"`
Flags []string `json:"flags"` Flags []string `json:"flags"`
Microcode string `json:"microcode"`
} }
type lastPercent struct { type lastPercent struct {
@ -49,10 +54,9 @@ type lastPercent struct {
} }
var lastCPUPercent lastPercent var lastCPUPercent lastPercent
var invoke common.Invoker var invoke common.Invoker = common.Invoke{}
func init() { func init() {
invoke = common.Invoke{}
lastCPUPercent.Lock() lastCPUPercent.Lock()
lastCPUPercent.lastCPUTimes, _ = Times(false) lastCPUPercent.lastCPUTimes, _ = Times(false)
lastCPUPercent.lastPerCPUTimes, _ = Times(true) lastCPUPercent.lastPerCPUTimes, _ = Times(true)
@ -60,6 +64,10 @@ func init() {
} }
func Counts(logical bool) (int, error) { func Counts(logical bool) (int, error) {
return CountsWithContext(context.Background(), logical)
}
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
return runtime.NumCPU(), nil return runtime.NumCPU(), nil
} }
@ -129,9 +137,14 @@ func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) {
return ret, nil return ret, nil
} }
//Percent calculates the percentage of cpu used either per CPU or combined. // Percent calculates the percentage of cpu used either per CPU or combined.
//If an interval of 0 is given it will compare the current cpu times against the last call. // If an interval of 0 is given it will compare the current cpu times against the last call.
// Returns one value per cpu, or a single value if percpu is set to false.
func Percent(interval time.Duration, percpu bool) ([]float64, error) { func Percent(interval time.Duration, percpu bool) ([]float64, error) {
return PercentWithContext(context.Background(), interval, percpu)
}
func PercentWithContext(ctx context.Context, interval time.Duration, percpu bool) ([]float64, error) {
if interval <= 0 { if interval <= 0 {
return percentUsedFromLastCall(percpu) return percentUsedFromLastCall(percpu)
} }
@ -170,7 +183,7 @@ func percentUsedFromLastCall(percpu bool) ([]float64, error) {
} }
if lastTimes == nil { if lastTimes == nil {
return nil, fmt.Errorf("Error getting times for cpu percent. LastTimes was nil") return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil")
} }
return calculateAllBusy(lastTimes, cpuTimes) return calculateAllBusy(lastTimes, cpuTimes)
} }

View File

@ -3,6 +3,7 @@
package cpu package cpu
import ( import (
"context"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
@ -22,6 +23,10 @@ const (
var ClocksPerSec = float64(128) var ClocksPerSec = float64(128)
func Times(percpu bool) ([]TimesStat, error) { func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
if percpu { if percpu {
return perCPUTimes() return perCPUTimes()
} }
@ -31,12 +36,16 @@ func Times(percpu bool) ([]TimesStat, error) {
// Returns only one CPUInfoStat on FreeBSD // Returns only one CPUInfoStat on FreeBSD
func Info() ([]InfoStat, error) { func Info() ([]InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
var ret []InfoStat var ret []InfoStat
sysctl, err := exec.LookPath("/usr/sbin/sysctl") sysctl, err := exec.LookPath("/usr/sbin/sysctl")
if err != nil { if err != nil {
return ret, err return ret, err
} }
out, err := invoke.Command(sysctl, "machdep.cpu") out, err := invoke.CommandWithContext(ctx, sysctl, "machdep.cpu")
if err != nil { if err != nil {
return ret, err return ret, err
} }
@ -90,7 +99,7 @@ func Info() ([]InfoStat, error) {
// Use the rated frequency of the CPU. This is a static value and does not // Use the rated frequency of the CPU. This is a static value and does not
// account for low power or Turbo Boost modes. // account for low power or Turbo Boost modes.
out, err = invoke.Command(sysctl, "hw.cpufrequency") out, err = invoke.CommandWithContext(ctx, sysctl, "hw.cpufrequency")
if err != nil { if err != nil {
return ret, err return ret, err
} }

View File

@ -1,21 +1,25 @@
// +build !darwin,!linux,!freebsd,!openbsd,!windows // +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows
package cpu package cpu
import ( import (
"time" "context"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
func Times(percpu bool) ([]TimesStat, error) { func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
return []TimesStat{}, common.ErrNotImplementedError return []TimesStat{}, common.ErrNotImplementedError
} }
func Info() ([]InfoStat, error) { func Info() ([]InfoStat, error) {
return []InfoStat{}, common.ErrNotImplementedError return InfoWithContext(context.Background())
} }
func Percent(interval time.Duration, percpu bool) ([]float64, error) { func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
return []float64{}, common.ErrNotImplementedError return []InfoStat{}, common.ErrNotImplementedError
} }

View File

@ -1,23 +1,17 @@
package cpu package cpu
import ( import (
"context"
"fmt" "fmt"
"os/exec" "os/exec"
"reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) "golang.org/x/sys/unix"
// sys/resource.h
const (
CPUser = 0
CPNice = 1
CPSys = 2
CPIntr = 3
CPIdle = 4
CPUStates = 5
) )
var ClocksPerSec = float64(128) var ClocksPerSec = float64(128)
@ -27,6 +21,8 @@ var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`) var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
var cpuEnd = regexp.MustCompile(`^Trying to mount root`) var cpuEnd = regexp.MustCompile(`^Trying to mount root`)
var cpuCores = regexp.MustCompile(`FreeBSD/SMP: (\d*) package\(s\) x (\d*) core\(s\)`) var cpuCores = regexp.MustCompile(`FreeBSD/SMP: (\d*) package\(s\) x (\d*) core\(s\)`)
var cpuTimesSize int
var emptyTimes cpuTimes
func init() { func init() {
getconf, err := exec.LookPath("/usr/bin/getconf") getconf, err := exec.LookPath("/usr/bin/getconf")
@ -43,97 +39,84 @@ func init() {
} }
} }
func timeStat(name string, t *cpuTimes) *TimesStat {
return &TimesStat{
User: float64(t.User) / ClocksPerSec,
Nice: float64(t.Nice) / ClocksPerSec,
System: float64(t.Sys) / ClocksPerSec,
Idle: float64(t.Idle) / ClocksPerSec,
Irq: float64(t.Intr) / ClocksPerSec,
CPU: name,
}
}
func Times(percpu bool) ([]TimesStat, error) { func Times(percpu bool) ([]TimesStat, error) {
var ret []TimesStat return TimesWithContext(context.Background(), percpu)
}
var sysctlCall string func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
var ncpu int
if percpu { if percpu {
sysctlCall = "kern.cp_times" buf, err := unix.SysctlRaw("kern.cp_times")
ncpu, _ = Counts(true) if err != nil {
} else { return nil, err
sysctlCall = "kern.cp_time" }
ncpu = 1
// We can't do this in init due to the conflict with cpu.init()
if cpuTimesSize == 0 {
cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size())
}
ncpus := len(buf) / cpuTimesSize
ret := make([]TimesStat, 0, ncpus)
for i := 0; i < ncpus; i++ {
times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize]))
if *times == emptyTimes {
// CPU not present
continue
}
ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times))
}
return ret, nil
} }
cpuTimes, err := common.DoSysctrl(sysctlCall) buf, err := unix.SysctlRaw("kern.cp_time")
if err != nil { if err != nil {
return ret, err return nil, err
} }
for i := 0; i < ncpu; i++ { times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
offset := CPUStates * i return []TimesStat{*timeStat("cpu-total", times)}, nil
user, err := strconv.ParseFloat(cpuTimes[CPUser+offset], 64)
if err != nil {
return ret, err
}
nice, err := strconv.ParseFloat(cpuTimes[CPNice+offset], 64)
if err != nil {
return ret, err
}
sys, err := strconv.ParseFloat(cpuTimes[CPSys+offset], 64)
if err != nil {
return ret, err
}
idle, err := strconv.ParseFloat(cpuTimes[CPIdle+offset], 64)
if err != nil {
return ret, err
}
intr, err := strconv.ParseFloat(cpuTimes[CPIntr+offset], 64)
if err != nil {
return ret, err
}
c := TimesStat{
User: float64(user / ClocksPerSec),
Nice: float64(nice / ClocksPerSec),
System: float64(sys / ClocksPerSec),
Idle: float64(idle / ClocksPerSec),
Irq: float64(intr / ClocksPerSec),
}
if !percpu {
c.CPU = "cpu-total"
} else {
c.CPU = fmt.Sprintf("cpu%d", i)
}
ret = append(ret, c)
}
return ret, nil
} }
// Returns only one InfoStat on FreeBSD. The information regarding core // Returns only one InfoStat on FreeBSD. The information regarding core
// count, however is accurate and it is assumed that all InfoStat attributes // count, however is accurate and it is assumed that all InfoStat attributes
// are the same across CPUs. // are the same across CPUs.
func Info() ([]InfoStat, error) { func Info() ([]InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
const dmesgBoot = "/var/run/dmesg.boot" const dmesgBoot = "/var/run/dmesg.boot"
c, num, err := parseDmesgBoot(dmesgBoot) c, num, err := parseDmesgBoot(dmesgBoot)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var vals []string
if vals, err = common.DoSysctrl("hw.clockrate"); err != nil {
return nil, err
}
if c.Mhz, err = strconv.ParseFloat(vals[0], 64); err != nil {
return nil, fmt.Errorf("Unable to parse FreeBSD CPU clock rate: %v", err)
}
if vals, err = common.DoSysctrl("hw.ncpu"); err != nil { var u32 uint32
if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
return nil, err return nil, err
} }
var i64 int64 c.Mhz = float64(u32)
if i64, err = strconv.ParseInt(vals[0], 10, 32); err != nil {
return nil, fmt.Errorf("Unable to parse FreeBSD cores: %v", err)
}
c.Cores = int32(i64)
if vals, err = common.DoSysctrl("hw.model"); err != nil { if u32, err = unix.SysctlUint32("hw.ncpu"); err != nil {
return nil, err
}
c.Cores = int32(u32)
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
return nil, err return nil, err
} }
c.ModelName = strings.Join(vals, " ")
ret := make([]InfoStat, num) ret := make([]InfoStat, num)
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
@ -146,7 +129,7 @@ func Info() ([]InfoStat, error) {
func parseDmesgBoot(fileName string) (InfoStat, int, error) { func parseDmesgBoot(fileName string) (InfoStat, int, error) {
c := InfoStat{} c := InfoStat{}
lines, _ := common.ReadLines(fileName) lines, _ := common.ReadLines(fileName)
var cpuNum int cpuNum := 1 // default cpu num is 1
for _, line := range lines { for _, line := range lines {
if matches := cpuEnd.FindStringSubmatch(line); matches != nil { if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
break break
@ -156,7 +139,7 @@ func parseDmesgBoot(fileName string) (InfoStat, int, error) {
c.Model = matches[4] c.Model = matches[4]
t, err := strconv.ParseInt(matches[5], 10, 32) t, err := strconv.ParseInt(matches[5], 10, 32)
if err != nil { if err != nil {
return c, 0, fmt.Errorf("Unable to parse FreeBSD CPU stepping information from %q: %v", line, err) return c, 0, fmt.Errorf("unable to parse FreeBSD CPU stepping information from %q: %v", line, err)
} }
c.Stepping = int32(t) c.Stepping = int32(t)
} else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
@ -170,12 +153,12 @@ func parseDmesgBoot(fileName string) (InfoStat, int, error) {
} else if matches := cpuCores.FindStringSubmatch(line); matches != nil { } else if matches := cpuCores.FindStringSubmatch(line); matches != nil {
t, err := strconv.ParseInt(matches[1], 10, 32) t, err := strconv.ParseInt(matches[1], 10, 32)
if err != nil { if err != nil {
return c, 0, fmt.Errorf("Unable to parse FreeBSD CPU Nums from %q: %v", line, err) return c, 0, fmt.Errorf("unable to parse FreeBSD CPU Nums from %q: %v", line, err)
} }
cpuNum = int(t) cpuNum = int(t)
t2, err := strconv.ParseInt(matches[2], 10, 32) t2, err := strconv.ParseInt(matches[2], 10, 32)
if err != nil { if err != nil {
return c, 0, fmt.Errorf("Unable to parse FreeBSD CPU cores from %q: %v", line, err) return c, 0, fmt.Errorf("unable to parse FreeBSD CPU cores from %q: %v", line, err)
} }
c.Cores = int32(t2) c.Cores = int32(t2)
} }

View File

@ -0,0 +1,9 @@
package cpu
type cpuTimes struct {
User uint32
Nice uint32
Sys uint32
Intr uint32
Idle uint32
}

View File

@ -0,0 +1,9 @@
package cpu
type cpuTimes struct {
User uint64
Nice uint64
Sys uint64
Intr uint64
Idle uint64
}

View File

@ -3,6 +3,7 @@
package cpu package cpu
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os/exec" "os/exec"
@ -12,39 +13,40 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var cpu_tick = float64(100) var CPUTick = float64(100)
func init() { func init() {
getconf, err := exec.LookPath("/usr/bin/getconf") getconf, err := exec.LookPath("/usr/bin/getconf")
if err != nil { if err != nil {
return return
} }
out, err := invoke.Command(getconf, "CLK_TCK") out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK")
// ignore errors // ignore errors
if err == nil { if err == nil {
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
if err == nil { if err == nil {
cpu_tick = float64(i) CPUTick = i
} }
} }
} }
func Times(percpu bool) ([]TimesStat, error) { func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
filename := common.HostProc("stat") filename := common.HostProc("stat")
var lines = []string{} var lines = []string{}
if percpu { if percpu {
var startIdx uint = 1 statlines, err := common.ReadLines(filename)
for { if err != nil || len(statlines) < 2 {
linen, _ := common.ReadLinesOffsetN(filename, startIdx, 1) return []TimesStat{}, nil
if len(linen) == 0 { }
break for _, line := range statlines[1:] {
}
line := linen[0]
if !strings.HasPrefix(line, "cpu") { if !strings.HasPrefix(line, "cpu") {
break break
} }
lines = append(lines, line) lines = append(lines, line)
startIdx++
} }
} else { } else {
lines, _ = common.ReadLinesOffsetN(filename, 0, 1) lines, _ = common.ReadLinesOffsetN(filename, 0, 1)
@ -83,24 +85,19 @@ func finishCPUInfo(c *InfoStat) error {
// of the value from /proc/cpuinfo because we want to report the maximum // of the value from /proc/cpuinfo because we want to report the maximum
// clock-speed of the CPU for c.Mhz, matching the behaviour of Windows // clock-speed of the CPU for c.Mhz, matching the behaviour of Windows
lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq")) lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq"))
// if we encounter errors below but has a value from parsing /proc/cpuinfo // if we encounter errors below such as there are no cpuinfo_max_freq file,
// then we ignore the error // we just ignore. so let Mhz is 0.
if err != nil { if err != nil {
if c.Mhz == 0 { return nil
return err
} else {
return nil
}
} }
value, err = strconv.ParseFloat(lines[0], 64) value, err = strconv.ParseFloat(lines[0], 64)
if err != nil { if err != nil {
if c.Mhz == 0 { return nil
return err
} else {
return nil
}
} }
c.Mhz = value / 1000.0 // value is in kHz c.Mhz = value / 1000.0 // value is in kHz
if c.Mhz > 9999 {
c.Mhz = c.Mhz / 1000.0 // value in Hz
}
return nil return nil
} }
@ -112,10 +109,15 @@ func finishCPUInfo(c *InfoStat) error {
// For example a single socket board with two cores each with HT will // For example a single socket board with two cores each with HT will
// return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1. // return 4 CPUInfoStat structs on Linux and the "Cores" field set to 1.
func Info() ([]InfoStat, error) { func Info() ([]InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
filename := common.HostProc("cpuinfo") filename := common.HostProc("cpuinfo")
lines, _ := common.ReadLines(filename) lines, _ := common.ReadLines(filename)
var ret []InfoStat var ret []InfoStat
var processorName string
c := InfoStat{CPU: -1, Cores: 1} c := InfoStat{CPU: -1, Cores: 1}
for _, line := range lines { for _, line := range lines {
@ -127,6 +129,8 @@ func Info() ([]InfoStat, error) {
value := strings.TrimSpace(fields[1]) value := strings.TrimSpace(fields[1])
switch key { switch key {
case "Processor":
processorName = value
case "processor": case "processor":
if c.CPU >= 0 { if c.CPU >= 0 {
err := finishCPUInfo(&c) err := finishCPUInfo(&c)
@ -135,7 +139,7 @@ func Info() ([]InfoStat, error) {
} }
ret = append(ret, c) ret = append(ret, c)
} }
c = InfoStat{Cores: 1} c = InfoStat{Cores: 1, ModelName: processorName}
t, err := strconv.ParseInt(value, 10, 64) t, err := strconv.ParseInt(value, 10, 64)
if err != nil { if err != nil {
return ret, err return ret, err
@ -186,6 +190,8 @@ func Info() ([]InfoStat, error) {
c.Flags = strings.FieldsFunc(value, func(r rune) bool { c.Flags = strings.FieldsFunc(value, func(r rune) bool {
return r == ',' || r == ' ' return r == ',' || r == ' '
}) })
case "microcode":
c.Microcode = value
} }
} }
if c.CPU >= 0 { if c.CPU >= 0 {
@ -201,8 +207,11 @@ func Info() ([]InfoStat, error) {
func parseStatLine(line string) (*TimesStat, error) { func parseStatLine(line string) (*TimesStat, error) {
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) == 0 {
return nil, errors.New("stat does not contain cpu info")
}
if strings.HasPrefix(fields[0], "cpu") == false { if strings.HasPrefix(fields[0], "cpu") == false {
// return CPUTimesStat{}, e
return nil, errors.New("not contain cpu") return nil, errors.New("not contain cpu")
} }
@ -241,34 +250,34 @@ func parseStatLine(line string) (*TimesStat, error) {
ct := &TimesStat{ ct := &TimesStat{
CPU: cpu, CPU: cpu,
User: float64(user) / cpu_tick, User: user / CPUTick,
Nice: float64(nice) / cpu_tick, Nice: nice / CPUTick,
System: float64(system) / cpu_tick, System: system / CPUTick,
Idle: float64(idle) / cpu_tick, Idle: idle / CPUTick,
Iowait: float64(iowait) / cpu_tick, Iowait: iowait / CPUTick,
Irq: float64(irq) / cpu_tick, Irq: irq / CPUTick,
Softirq: float64(softirq) / cpu_tick, Softirq: softirq / CPUTick,
} }
if len(fields) > 8 { // Linux >= 2.6.11 if len(fields) > 8 { // Linux >= 2.6.11
steal, err := strconv.ParseFloat(fields[8], 64) steal, err := strconv.ParseFloat(fields[8], 64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ct.Steal = float64(steal) / cpu_tick ct.Steal = steal / CPUTick
} }
if len(fields) > 9 { // Linux >= 2.6.24 if len(fields) > 9 { // Linux >= 2.6.24
guest, err := strconv.ParseFloat(fields[9], 64) guest, err := strconv.ParseFloat(fields[9], 64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ct.Guest = float64(guest) / cpu_tick ct.Guest = guest / CPUTick
} }
if len(fields) > 10 { // Linux >= 3.2.0 if len(fields) > 10 { // Linux >= 3.2.0
guestNice, err := strconv.ParseFloat(fields[10], 64) guestNice, err := strconv.ParseFloat(fields[10], 64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ct.GuestNice = float64(guestNice) / cpu_tick ct.GuestNice = guestNice / CPUTick
} }
return ct, nil return ct, nil

View File

@ -4,14 +4,15 @@ package cpu
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
) )
// sys/sched.h // sys/sched.h
@ -49,6 +50,10 @@ func init() {
} }
func Times(percpu bool) ([]TimesStat, error) { func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
var ret []TimesStat var ret []TimesStat
var ncpu int var ncpu int
@ -96,11 +101,15 @@ func Times(percpu bool) ([]TimesStat, error) {
// Returns only one (minimal) CPUInfoStat on OpenBSD // Returns only one (minimal) CPUInfoStat on OpenBSD
func Info() ([]InfoStat, error) { func Info() ([]InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
var ret []InfoStat var ret []InfoStat
c := InfoStat{} c := InfoStat{}
v, err := syscall.Sysctl("hw.model") v, err := unix.Sysctl("hw.model")
if err != nil { if err != nil {
return nil, err return nil, err
} }

198
vendor/github.com/shirou/gopsutil/cpu/cpu_solaris.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
package cpu
import (
"context"
"errors"
"fmt"
"os/exec"
"regexp"
"sort"
"strconv"
"strings"
"github.com/shirou/gopsutil/internal/common"
)
var ClocksPerSec = float64(128)
func init() {
getconf, err := exec.LookPath("/usr/bin/getconf")
if err != nil {
return
}
out, err := invoke.Command(getconf, "CLK_TCK")
// ignore errors
if err == nil {
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
if err == nil {
ClocksPerSec = float64(i)
}
}
}
func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
return []TimesStat{}, common.ErrNotImplementedError
}
func Info() ([]InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
psrInfo, err := exec.LookPath("/usr/sbin/psrinfo")
if err != nil {
return nil, fmt.Errorf("cannot find psrinfo: %s", err)
}
psrInfoOut, err := invoke.CommandWithContext(ctx, psrInfo, "-p", "-v")
if err != nil {
return nil, fmt.Errorf("cannot execute psrinfo: %s", err)
}
isaInfo, err := exec.LookPath("/usr/bin/isainfo")
if err != nil {
return nil, fmt.Errorf("cannot find isainfo: %s", err)
}
isaInfoOut, err := invoke.CommandWithContext(ctx, isaInfo, "-b", "-v")
if err != nil {
return nil, fmt.Errorf("cannot execute isainfo: %s", err)
}
procs, err := parseProcessorInfo(string(psrInfoOut))
if err != nil {
return nil, fmt.Errorf("error parsing psrinfo output: %s", err)
}
flags, err := parseISAInfo(string(isaInfoOut))
if err != nil {
return nil, fmt.Errorf("error parsing isainfo output: %s", err)
}
result := make([]InfoStat, 0, len(flags))
for _, proc := range procs {
procWithFlags := proc
procWithFlags.Flags = flags
result = append(result, procWithFlags)
}
return result, nil
}
var flagsMatch = regexp.MustCompile(`[\w\.]+`)
func parseISAInfo(cmdOutput string) ([]string, error) {
words := flagsMatch.FindAllString(cmdOutput, -1)
// Sanity check the output
if len(words) < 4 || words[1] != "bit" || words[3] != "applications" {
return nil, errors.New("attempted to parse invalid isainfo output")
}
flags := make([]string, len(words)-4)
for i, val := range words[4:] {
flags[i] = val
}
sort.Strings(flags)
return flags, nil
}
var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processor \(([\d]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`)
const (
psrNumCoresOffset = 1
psrNumCoresHTOffset = 3
psrNumHTOffset = 4
psrVendorIDOffset = 5
psrFamilyOffset = 7
psrModelOffset = 8
psrStepOffset = 9
psrClockOffset = 10
psrModelNameOffset = 11
)
func parseProcessorInfo(cmdOutput string) ([]InfoStat, error) {
matches := psrInfoMatch.FindAllStringSubmatch(cmdOutput, -1)
var infoStatCount int32
result := make([]InfoStat, 0, len(matches))
for physicalIndex, physicalCPU := range matches {
var step int32
var clock float64
if physicalCPU[psrStepOffset] != "" {
stepParsed, err := strconv.ParseInt(physicalCPU[psrStepOffset], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse value %q for step as 32-bit integer: %s", physicalCPU[9], err)
}
step = int32(stepParsed)
}
if physicalCPU[psrClockOffset] != "" {
clockParsed, err := strconv.ParseInt(physicalCPU[psrClockOffset], 10, 64)
if err != nil {
return nil, fmt.Errorf("cannot parse value %q for clock as 32-bit integer: %s", physicalCPU[10], err)
}
clock = float64(clockParsed)
}
var err error
var numCores int64
var numHT int64
switch {
case physicalCPU[psrNumCoresOffset] != "":
numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresOffset], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[1], err)
}
for i := 0; i < int(numCores); i++ {
result = append(result, InfoStat{
CPU: infoStatCount,
PhysicalID: strconv.Itoa(physicalIndex),
CoreID: strconv.Itoa(i),
Cores: 1,
VendorID: physicalCPU[psrVendorIDOffset],
ModelName: physicalCPU[psrModelNameOffset],
Family: physicalCPU[psrFamilyOffset],
Model: physicalCPU[psrModelOffset],
Stepping: step,
Mhz: clock,
})
infoStatCount++
}
case physicalCPU[psrNumCoresHTOffset] != "":
numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresHTOffset], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[3], err)
}
numHT, err = strconv.ParseInt(physicalCPU[psrNumHTOffset], 10, 32)
if err != nil {
return nil, fmt.Errorf("cannot parse value %q for hyperthread count as 32-bit integer: %s", physicalCPU[4], err)
}
for i := 0; i < int(numCores); i++ {
result = append(result, InfoStat{
CPU: infoStatCount,
PhysicalID: strconv.Itoa(physicalIndex),
CoreID: strconv.Itoa(i),
Cores: int32(numHT) / int32(numCores),
VendorID: physicalCPU[psrVendorIDOffset],
ModelName: physicalCPU[psrModelNameOffset],
Family: physicalCPU[psrFamilyOffset],
Model: physicalCPU[psrModelOffset],
Stepping: step,
Mhz: clock,
})
infoStatCount++
}
default:
return nil, errors.New("values for cores with and without hyperthreading are both set")
}
}
return result, nil
}

View File

@ -3,13 +3,13 @@
package cpu package cpu
import ( import (
"context"
"fmt" "fmt"
"syscall"
"unsafe" "unsafe"
"github.com/StackExchange/wmi" "github.com/StackExchange/wmi"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows"
) )
type Win32_Processor struct { type Win32_Processor struct {
@ -23,10 +23,38 @@ type Win32_Processor struct {
MaxClockSpeed uint32 MaxClockSpeed uint32
} }
// TODO: Get percpu // Win32_PerfFormattedData_Counters_ProcessorInformation stores instance value of the perf counters
func Times(percpu bool) ([]TimesStat, error) { type Win32_PerfFormattedData_Counters_ProcessorInformation struct {
var ret []TimesStat Name string
PercentDPCTime uint64
PercentIdleTime uint64
PercentUserTime uint64
PercentProcessorTime uint64
PercentInterruptTime uint64
PercentPriorityTime uint64
PercentPrivilegedTime uint64
InterruptsPerSec uint32
ProcessorFrequency uint32
DPCRate uint32
}
// Win32_PerfFormattedData_PerfOS_System struct to have count of processes and processor queue length
type Win32_PerfFormattedData_PerfOS_System struct {
Processes uint32
ProcessorQueueLength uint32
}
// Times returns times stat per cpu and combined for all CPUs
func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
if percpu {
return perCPUTimes()
}
var ret []TimesStat
var lpIdleTime common.FILETIME var lpIdleTime common.FILETIME
var lpKernelTime common.FILETIME var lpKernelTime common.FILETIME
var lpUserTime common.FILETIME var lpUserTime common.FILETIME
@ -35,7 +63,7 @@ func Times(percpu bool) ([]TimesStat, error) {
uintptr(unsafe.Pointer(&lpKernelTime)), uintptr(unsafe.Pointer(&lpKernelTime)),
uintptr(unsafe.Pointer(&lpUserTime))) uintptr(unsafe.Pointer(&lpUserTime)))
if r == 0 { if r == 0 {
return ret, syscall.GetLastError() return ret, windows.GetLastError()
} }
LOT := float64(0.0000001) LOT := float64(0.0000001)
@ -46,6 +74,7 @@ func Times(percpu bool) ([]TimesStat, error) {
system := (kernel - idle) system := (kernel - idle)
ret = append(ret, TimesStat{ ret = append(ret, TimesStat{
CPU: "cpu-total",
Idle: float64(idle), Idle: float64(idle),
User: float64(user), User: float64(user),
System: float64(system), System: float64(system),
@ -54,11 +83,14 @@ func Times(percpu bool) ([]TimesStat, error) {
} }
func Info() ([]InfoStat, error) { func Info() ([]InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
var ret []InfoStat var ret []InfoStat
var dst []Win32_Processor var dst []Win32_Processor
q := wmi.CreateQuery(&dst, "") q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst) if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
if err != nil {
return ret, err return ret, err
} }
@ -84,3 +116,57 @@ func Info() ([]InfoStat, error) {
return ret, nil return ret, nil
} }
// PerfInfo returns the performance counter's instance value for ProcessorInformation.
// Name property is the key by which overall, per cpu and per core metric is known.
func PerfInfo() ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) {
return PerfInfoWithContext(context.Background())
}
func PerfInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_Counters_ProcessorInformation, error) {
var ret []Win32_PerfFormattedData_Counters_ProcessorInformation
q := wmi.CreateQuery(&ret, "")
err := common.WMIQueryWithContext(ctx, q, &ret)
if err != nil {
return []Win32_PerfFormattedData_Counters_ProcessorInformation{}, err
}
return ret, err
}
// ProcInfo returns processes count and processor queue length in the system.
// There is a single queue for processor even on multiprocessors systems.
func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) {
return ProcInfoWithContext(context.Background())
}
func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_System, error) {
var ret []Win32_PerfFormattedData_PerfOS_System
q := wmi.CreateQuery(&ret, "")
err := common.WMIQueryWithContext(ctx, q, &ret)
if err != nil {
return []Win32_PerfFormattedData_PerfOS_System{}, err
}
return ret, err
}
// perCPUTimes returns times stat per cpu, per core and overall for all CPUs
func perCPUTimes() ([]TimesStat, error) {
var ret []TimesStat
stats, err := PerfInfo()
if err != nil {
return nil, err
}
for _, v := range stats {
c := TimesStat{
CPU: v.Name,
User: float64(v.PercentUserTime),
System: float64(v.PercentPrivilegedTime),
Idle: float64(v.PercentIdleTime),
Irq: float64(v.PercentInterruptTime),
}
ret = append(ret, c)
}
return ret, nil
}

View File

@ -6,11 +6,7 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var invoke common.Invoker var invoke common.Invoker = common.Invoke{}
func init() {
invoke = common.Invoke{}
}
type UsageStat struct { type UsageStat struct {
Path string `json:"path"` Path string `json:"path"`
@ -46,6 +42,7 @@ type IOCountersStat struct {
WeightedIO uint64 `json:"weightedIO"` WeightedIO uint64 `json:"weightedIO"`
Name string `json:"name"` Name string `json:"name"`
SerialNumber string `json:"serialNumber"` SerialNumber string `json:"serialNumber"`
Label string `json:"label"`
} }
func (d UsageStat) String() string { func (d UsageStat) String() string {

View File

@ -3,22 +3,29 @@
package disk package disk
import ( import (
"context"
"path" "path"
"syscall"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
) )
func Partitions(all bool) ([]PartitionStat, error) { func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat var ret []PartitionStat
count, err := Getfsstat(nil, MntWait) count, err := Getfsstat(nil, MntWait)
if err != nil { if err != nil {
return ret, err return ret, err
} }
fs := make([]Statfs_t, count) fs := make([]Statfs, count)
_, err = Getfsstat(fs, MntWait) if _, err = Getfsstat(fs, MntWait); err != nil {
return ret, err
}
for _, stat := range fs { for _, stat := range fs {
opts := "rw" opts := "rw"
if stat.Flags&MntReadOnly != 0 { if stat.Flags&MntReadOnly != 0 {
@ -87,18 +94,18 @@ func Partitions(all bool) ([]PartitionStat, error) {
return ret, nil return ret, nil
} }
func IOCounters() (map[string]IOCountersStat, error) { func Getfsstat(buf []Statfs, flags int) (n int, err error) {
return nil, common.ErrNotImplementedError return GetfsstatWithContext(context.Background(), buf, flags)
} }
func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { func GetfsstatWithContext(ctx context.Context, buf []Statfs, flags int) (n int, err error) {
var _p0 unsafe.Pointer var _p0 unsafe.Pointer
var bufsize uintptr var bufsize uintptr
if len(buf) > 0 { if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0]) _p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf))
} }
r0, _, e1 := syscall.Syscall(SYS_GETFSSTAT64, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := unix.Syscall(SYS_GETFSSTAT64, uintptr(_p0), bufsize, uintptr(flags))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
@ -106,6 +113,6 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
return return
} }
func getFsType(stat syscall.Statfs_t) string { func getFsType(stat unix.Statfs_t) string {
return common.IntToString(stat.Fstypename[:]) return common.IntToString(stat.Fstypename[:])
} }

164
vendor/github.com/shirou/gopsutil/disk/disk_darwin.h generated vendored Normal file
View File

@ -0,0 +1,164 @@
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
// The iterator of all things disk. Allocated by StartIOCounterFetch, released
// by EndIOCounterFetch.
static io_iterator_t diskIter;
// Begins fetching IO counters.
//
// Returns 1 if the fetch started successfully, false otherwise.
//
// If the fetch was started successfully, you must call EndIOCounterFetch once
// done to release resources.
int StartIOCounterFetch()
{
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(kIOMediaClass),
&diskIter) != kIOReturnSuccess) {
return 0;
}
return 1;
}
// Releases resources from fetching IO counters.
void EndIOCounterFetch()
{
IOObjectRelease(diskIter);
}
// The current disk entry of interest. Allocated by FetchNextDisk(), released by
// ReadDiskInfo().
static io_registry_entry_t diskEntry;
// The parent of diskEntry. Same lifetimes.
static io_registry_entry_t parentEntry;
// Fetches the next disk. Note that a disk entry is allocated, and will be held
// until it is processed and freed by ReadDiskInfo.
int FetchNextDisk()
{
while ((diskEntry = IOIteratorNext(diskIter)) != 0) {
// We are iterating IOMedia. We need to get the parent too (IOBSD).
if (IORegistryEntryGetParentEntry(diskEntry, kIOServicePlane, &parentEntry) != kIOReturnSuccess) {
// something is wrong...
IOObjectRelease(diskEntry);
continue;
}
if (!IOObjectConformsTo(parentEntry, "IOBlockStorageDriver")) {
// no use to us, try the next disk
IOObjectRelease(diskEntry);
IOObjectRelease(parentEntry);
continue;
}
// Got a disk OK.
return 1;
}
// No more disks.
return 0;
}
// Reads the current disk (from iteration) info into DiskInfo struct.
// Once done, all resources from the current iteration of reading are freed,
// ready for FetchNextDisk() to be called again.
int ReadDiskInfo(DiskInfo *info)
{
// Parent props. Allocated by us.
CFDictionaryRef parentProps = NULL;
// Disk props. Allocated by us.
CFDictionaryRef diskProps = NULL;
// Disk stats, fetched by us, but not allocated by us.
CFDictionaryRef stats = NULL;
if (IORegistryEntryCreateCFProperties(diskEntry, (CFMutableDictionaryRef *)&parentProps,
kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
{
// can't get parent props, give up
CFRelease(parentProps);
IOObjectRelease(diskEntry);
IOObjectRelease(parentEntry);
return -1;
}
if (IORegistryEntryCreateCFProperties(parentEntry, (CFMutableDictionaryRef *)&diskProps,
kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
{
// can't get disk props, give up
CFRelease(parentProps);
CFRelease(diskProps);
IOObjectRelease(diskEntry);
IOObjectRelease(parentEntry);
return -1;
}
// Start fetching
CFStringRef cfDiskName = (CFStringRef)CFDictionaryGetValue(parentProps, CFSTR(kIOBSDNameKey));
CFStringGetCString(cfDiskName, info->DiskName, MAX_DISK_NAME, CFStringGetSystemEncoding());
stats = (CFDictionaryRef)CFDictionaryGetValue( diskProps, CFSTR(kIOBlockStorageDriverStatisticsKey));
if (stats == NULL) {
// stat fetch failed...
CFRelease(parentProps);
CFRelease(diskProps);
IOObjectRelease(parentEntry);
IOObjectRelease(diskEntry);
return -1;
}
CFNumberRef cfnum;
if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->Reads);
} else {
info->Reads = 0;
}
if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->Writes);
} else {
info->Writes = 0;
}
if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) {
CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->ReadBytes);
} else {
info->ReadBytes = 0;
}
if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) {
CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->WriteBytes);
} else {
info->WriteBytes = 0;
}
if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) {
CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->ReadTime);
} else {
info->ReadTime = 0;
}
if ((cfnum = (CFNumberRef)CFDictionaryGetValue(stats, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) {
CFNumberGetValue(cfnum, kCFNumberSInt64Type, &info->WriteTime);
} else {
info->WriteTime = 0;
}
// note: read/write time are in ns, but we want ms.
info->ReadTime = info->ReadTime / 1000 / 1000;
info->WriteTime = info->WriteTime / 1000 / 1000;
CFRelease(parentProps);
CFRelease(diskProps);
IOObjectRelease(parentEntry);
IOObjectRelease(diskEntry);
return 0;
}

View File

@ -0,0 +1,59 @@
// +build darwin
// +build 386
package disk
const (
MntWait = 1
MfsNameLen = 15 /* length of fs type name, not inc. nul */
MNameLen = 90 /* length of buffer for returned name */
MFSTYPENAMELEN = 16 /* length of fs type name including null */
MAXPATHLEN = 1024
MNAMELEN = MAXPATHLEN
SYS_GETFSSTAT64 = 347
)
type Fsid struct{ val [2]int32 } /* file system id type */
type uid_t int32
// sys/mount.h
const (
MntReadOnly = 0x00000001 /* read only filesystem */
MntSynchronous = 0x00000002 /* filesystem written synchronously */
MntNoExec = 0x00000004 /* can't exec from filesystem */
MntNoSuid = 0x00000008 /* don't honor setuid bits on fs */
MntUnion = 0x00000020 /* union with underlying filesystem */
MntAsync = 0x00000040 /* filesystem written asynchronously */
MntSuidDir = 0x00100000 /* special handling of SUID on dirs */
MntSoftDep = 0x00200000 /* soft updates being done */
MntNoSymFollow = 0x00400000 /* do not follow symlinks */
MntGEOMJournal = 0x02000000 /* GEOM journal support enabled */
MntMultilabel = 0x04000000 /* MAC support for individual objects */
MntACLs = 0x08000000 /* ACL support enabled */
MntNoATime = 0x10000000 /* disable update of file access time */
MntClusterRead = 0x40000000 /* disable cluster read */
MntClusterWrite = 0x80000000 /* disable cluster write */
MntNFS4ACLs = 0x00000010
)
// https://github.com/golang/go/blob/master/src/syscall/ztypes_darwin_386.go#L82
type Statfs struct {
Bsize uint32
Iosize int32
Blocks uint64
Bfree uint64
Bavail uint64
Files uint64
Ffree uint64
Fsid Fsid
Owner uint32
Type uint32
Flags uint32
Fssubtype uint32
Fstypename [16]int8
Mntonname [1024]int8
Mntfromname [1024]int8
Reserved [8]uint32
}

View File

@ -38,7 +38,7 @@ const (
MntNFS4ACLs = 0x00000010 MntNFS4ACLs = 0x00000010
) )
type Statfs_t struct { type Statfs struct {
Bsize uint32 Bsize uint32
Iosize int32 Iosize int32
Blocks uint64 Blocks uint64

View File

@ -38,7 +38,7 @@ const (
MntNFS4ACLs = 0x00000010 MntNFS4ACLs = 0x00000010
) )
type Statfs_t struct { type Statfs struct {
Bsize uint32 Bsize uint32
Iosize int32 Iosize int32
Blocks uint64 Blocks uint64

View File

@ -0,0 +1,95 @@
// +build darwin
// +build cgo
package disk
/*
#cgo LDFLAGS: -lobjc -framework Foundation -framework IOKit
#include <stdint.h>
// ### enough?
const int MAX_DISK_NAME = 100;
typedef struct
{
char DiskName[MAX_DISK_NAME];
int64_t Reads;
int64_t Writes;
int64_t ReadBytes;
int64_t WriteBytes;
int64_t ReadTime;
int64_t WriteTime;
} DiskInfo;
#include "disk_darwin.h"
*/
import "C"
import (
"context"
"errors"
"strings"
"unsafe"
"github.com/shirou/gopsutil/internal/common"
)
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
if C.StartIOCounterFetch() == 0 {
return nil, errors.New("Unable to fetch disk list")
}
// Clean up when we are done.
defer C.EndIOCounterFetch()
ret := make(map[string]IOCountersStat, 0)
for {
res := C.FetchNextDisk()
if res == -1 {
return nil, errors.New("Unable to fetch disk information")
} else if res == 0 {
break // done
}
di := C.DiskInfo{}
if C.ReadDiskInfo((*C.DiskInfo)(unsafe.Pointer(&di))) == -1 {
return nil, errors.New("Unable to fetch disk properties")
}
// Used to only get the necessary part of the C string.
isRuneNull := func(r rune) bool {
return r == '\u0000'
}
// Map from the darwin-specific C struct to the Go type
//
// ### missing: IopsInProgress, WeightedIO, MergedReadCount,
// MergedWriteCount, SerialNumber
// IOKit can give us at least the serial number I think...
d := IOCountersStat{
// Note: The Go type wants unsigned values, but CFNumberGetValue
// doesn't appear to be able to give us unsigned values. So, we
// cast, and hope for the best.
ReadBytes: uint64(di.ReadBytes),
WriteBytes: uint64(di.WriteBytes),
ReadCount: uint64(di.Reads),
WriteCount: uint64(di.Writes),
ReadTime: uint64(di.ReadTime),
WriteTime: uint64(di.WriteTime),
IoTime: uint64(di.ReadTime + di.WriteTime),
Name: strings.TrimFunc(C.GoStringN(&di.DiskName[0], C.MAX_DISK_NAME), isRuneNull),
}
if len(names) > 0 && !common.StringsHas(names, d.Name) {
continue
}
ret[d.Name] = d
}
return ret, nil
}

View File

@ -0,0 +1,18 @@
// +build darwin
// +build !cgo
package disk
import (
"context"
"github.com/shirou/gopsutil/internal/common"
)
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}

View File

@ -1,17 +1,33 @@
// +build !darwin,!linux,!freebsd,!openbsd,!windows // +build !darwin,!linux,!freebsd,!openbsd,!windows,!solaris
package disk package disk
import "github.com/shirou/gopsutil/internal/common" import (
"context"
func IOCounters() (map[string]IOCountersStat, error) { "github.com/shirou/gopsutil/internal/common"
)
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func Partitions(all bool) ([]PartitionStat, error) { func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
return []PartitionStat{}, common.ErrNotImplementedError return []PartitionStat{}, common.ErrNotImplementedError
} }
func Usage(path string) (*UsageStat, error) { func Usage(path string) (*UsageStat, error) {
return UsageWithContext(context.Background(), path)
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }

View File

@ -4,26 +4,34 @@ package disk
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"path" "path"
"strconv" "strconv"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
func Partitions(all bool) ([]PartitionStat, error) { func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat var ret []PartitionStat
// get length // get length
count, err := syscall.Getfsstat(nil, MNT_WAIT) count, err := unix.Getfsstat(nil, MNT_WAIT)
if err != nil { if err != nil {
return ret, err return ret, err
} }
fs := make([]Statfs, count) fs := make([]Statfs, count)
_, err = Getfsstat(fs, MNT_WAIT) if _, err = Getfsstat(fs, MNT_WAIT); err != nil {
return ret, err
}
for _, stat := range fs { for _, stat := range fs {
opts := "rw" opts := "rw"
@ -94,12 +102,16 @@ func Partitions(all bool) ([]PartitionStat, error) {
return ret, nil return ret, nil
} }
func IOCounters() (map[string]IOCountersStat, error) { func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
// statinfo->devinfo->devstat // statinfo->devinfo->devstat
// /usr/include/devinfo.h // /usr/include/devinfo.h
ret := make(map[string]IOCountersStat) ret := make(map[string]IOCountersStat)
r, err := syscall.Sysctl("kern.devstat.all") r, err := unix.Sysctl("kern.devstat.all")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,6 +131,10 @@ func IOCounters() (map[string]IOCountersStat, error) {
un := strconv.Itoa(int(d.Unit_number)) un := strconv.Itoa(int(d.Unit_number))
name := common.IntToString(d.Device_name[:]) + un name := common.IntToString(d.Device_name[:]) + un
if len(names) > 0 && !common.StringsHas(names, name) {
continue
}
ds := IOCountersStat{ ds := IOCountersStat{
ReadCount: d.Operations[DEVSTAT_READ], ReadCount: d.Operations[DEVSTAT_READ],
WriteCount: d.Operations[DEVSTAT_WRITE], WriteCount: d.Operations[DEVSTAT_WRITE],
@ -145,13 +161,17 @@ func (b Bintime) Compute() float64 {
// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go // Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go
// change Statfs_t to Statfs in order to get more information // change Statfs_t to Statfs in order to get more information
func Getfsstat(buf []Statfs, flags int) (n int, err error) { func Getfsstat(buf []Statfs, flags int) (n int, err error) {
return GetfsstatWithContext(context.Background(), buf, flags)
}
func GetfsstatWithContext(ctx context.Context, buf []Statfs, flags int) (n int, err error) {
var _p0 unsafe.Pointer var _p0 unsafe.Pointer
var bufsize uintptr var bufsize uintptr
if len(buf) > 0 { if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0]) _p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf))
} }
r0, _, e1 := syscall.Syscall(syscall.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := unix.Syscall(unix.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
@ -171,6 +191,6 @@ func parseDevstat(buf []byte) (Devstat, error) {
return ds, nil return ds, nil
} }
func getFsType(stat syscall.Statfs_t) string { func getFsType(stat unix.Statfs_t) string {
return common.IntToString(stat.Fstypename[:]) return common.IntToString(stat.Fstypename[:])
} }

View File

@ -3,11 +3,16 @@
package disk package disk
import ( import (
"bufio"
"bytes"
"context"
"fmt" "fmt"
"os/exec" "io/ioutil"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"golang.org/x/sys/unix"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
@ -214,10 +219,12 @@ var fsTypeMap = map[int64]string{
// Partitions returns disk partitions. If all is false, returns // Partitions returns disk partitions. If all is false, returns
// physical devices only (e.g. hard disks, cd-rom drives, USB keys) // physical devices only (e.g. hard disks, cd-rom drives, USB keys)
// and ignore all others (e.g. memory partitions such as /dev/shm) // and ignore all others (e.g. memory partitions such as /dev/shm)
//
// should use setmntent(3) but this implement use /etc/mtab file
func Partitions(all bool) ([]PartitionStat, error) { func Partitions(all bool) ([]PartitionStat, error) {
filename := common.HostEtc("mtab") return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
filename := common.HostProc("self/mounts")
lines, err := common.ReadLines(filename) lines, err := common.ReadLines(filename)
if err != nil { if err != nil {
return nil, err return nil, err
@ -272,7 +279,11 @@ func getFileSystems() ([]string, error) {
return ret, nil return ret, nil
} }
func IOCounters() (map[string]IOCountersStat, error) { func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
filename := common.HostProc("diskstats") filename := common.HostProc("diskstats")
lines, err := common.ReadLines(filename) lines, err := common.ReadLines(filename)
if err != nil { if err != nil {
@ -281,6 +292,11 @@ func IOCounters() (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat, 0) ret := make(map[string]IOCountersStat, 0)
empty := IOCountersStat{} empty := IOCountersStat{}
// use only basename such as "/dev/sda1" to "sda1"
for i, name := range names {
names[i] = filepath.Base(name)
}
for _, line := range lines { for _, line := range lines {
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) < 14 { if len(fields) < 14 {
@ -288,6 +304,11 @@ func IOCounters() (map[string]IOCountersStat, error) {
continue continue
} }
name := fields[2] name := fields[2]
if len(names) > 0 && !common.StringsHas(names, name) {
continue
}
reads, err := strconv.ParseUint((fields[3]), 10, 64) reads, err := strconv.ParseUint((fields[3]), 10, 64)
if err != nil { if err != nil {
return ret, err return ret, err
@ -351,6 +372,8 @@ func IOCounters() (map[string]IOCountersStat, error) {
d.Name = name d.Name = name
d.SerialNumber = GetDiskSerialNumber(name) d.SerialNumber = GetDiskSerialNumber(name)
d.Label = GetLabel(name)
ret[name] = d ret[name] = d
} }
return ret, nil return ret, nil
@ -359,31 +382,62 @@ func IOCounters() (map[string]IOCountersStat, error) {
// GetDiskSerialNumber returns Serial Number of given device or empty string // GetDiskSerialNumber returns Serial Number of given device or empty string
// on error. Name of device is expected, eg. /dev/sda // on error. Name of device is expected, eg. /dev/sda
func GetDiskSerialNumber(name string) string { func GetDiskSerialNumber(name string) string {
n := fmt.Sprintf("--name=%s", name) return GetDiskSerialNumberWithContext(context.Background(), name)
udevadm, err := exec.LookPath("/sbin/udevadm") }
func GetDiskSerialNumberWithContext(ctx context.Context, name string) string {
var stat unix.Stat_t
err := unix.Stat(name, &stat)
if err != nil { if err != nil {
return "" return ""
} }
major := unix.Major(uint64(stat.Rdev))
minor := unix.Minor(uint64(stat.Rdev))
out, err := invoke.Command(udevadm, "info", "--query=property", n) // Try to get the serial from udev data
udevDataPath := common.HostRun(fmt.Sprintf("udev/data/b%d:%d", major, minor))
// does not return error, just an empty string if udevdata, err := ioutil.ReadFile(udevDataPath); err == nil {
if err != nil { scanner := bufio.NewScanner(bytes.NewReader(udevdata))
return "" for scanner.Scan() {
} values := strings.Split(scanner.Text(), "=")
lines := strings.Split(string(out), "\n") if len(values) == 2 && values[0] == "E:ID_SERIAL" {
for _, line := range lines { return values[1]
values := strings.Split(line, "=") }
if len(values) < 2 || values[0] != "ID_SERIAL" {
// only get ID_SERIAL, not ID_SERIAL_SHORT
continue
} }
return values[1] }
// Try to get the serial from sysfs, look at the disk device (minor 0) directly
// because if it is a partition it is not going to contain any device information
devicePath := common.HostSys(fmt.Sprintf("dev/block/%d:0/device", major))
model, _ := ioutil.ReadFile(filepath.Join(devicePath, "model"))
serial, _ := ioutil.ReadFile(filepath.Join(devicePath, "serial"))
if len(model) > 0 && len(serial) > 0 {
return fmt.Sprintf("%s_%s", string(model), string(serial))
} }
return "" return ""
} }
func getFsType(stat syscall.Statfs_t) string { // GetLabel returns label of given device or empty string on error.
// Name of device is expected, eg. /dev/sda
// Supports label based on devicemapper name
// See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm
func GetLabel(name string) string {
// Try label based on devicemapper name
dmname_filename := common.HostSys(fmt.Sprintf("block/%s/dm/name", name))
if !common.PathExists(dmname_filename) {
return ""
}
dmname, err := ioutil.ReadFile(dmname_filename)
if err != nil {
return ""
} else {
return string(dmname)
}
}
func getFsType(stat unix.Statfs_t) string {
t := int64(stat.Type) t := int64(stat.Type)
ret, ok := fsTypeMap[t] ret, ok := fsTypeMap[t]
if !ok { if !ok {

View File

@ -4,25 +4,32 @@ package disk
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"path" "path"
"syscall"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
) )
func Partitions(all bool) ([]PartitionStat, error) { func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat var ret []PartitionStat
// get length // get length
count, err := syscall.Getfsstat(nil, MNT_WAIT) count, err := unix.Getfsstat(nil, MNT_WAIT)
if err != nil { if err != nil {
return ret, err return ret, err
} }
fs := make([]Statfs, count) fs := make([]Statfs, count)
_, err = Getfsstat(fs, MNT_WAIT) if _, err = Getfsstat(fs, MNT_WAIT); err != nil {
return ret, err
}
for _, stat := range fs { for _, stat := range fs {
opts := "rw" opts := "rw"
@ -63,10 +70,14 @@ func Partitions(all bool) ([]PartitionStat, error) {
return ret, nil return ret, nil
} }
func IOCounters() (map[string]IOCountersStat, error) { func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat) ret := make(map[string]IOCountersStat)
r, err := syscall.Sysctl("hw.diskstats") r, err := unix.SysctlRaw("hw.diskstats")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,6 +95,10 @@ func IOCounters() (map[string]IOCountersStat, error) {
} }
name := common.IntToString(d.Name[:]) name := common.IntToString(d.Name[:])
if len(names) > 0 && !common.StringsHas(names, name) {
continue
}
ds := IOCountersStat{ ds := IOCountersStat{
ReadCount: d.Rxfer, ReadCount: d.Rxfer,
WriteCount: d.Wxfer, WriteCount: d.Wxfer,
@ -102,13 +117,17 @@ func IOCounters() (map[string]IOCountersStat, error) {
// Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go // Getfsstat is borrowed from pkg/syscall/syscall_freebsd.go
// change Statfs_t to Statfs in order to get more information // change Statfs_t to Statfs in order to get more information
func Getfsstat(buf []Statfs, flags int) (n int, err error) { func Getfsstat(buf []Statfs, flags int) (n int, err error) {
return GetfsstatWithContext(context.Background(), buf, flags)
}
func GetfsstatWithContext(ctx context.Context, buf []Statfs, flags int) (n int, err error) {
var _p0 unsafe.Pointer var _p0 unsafe.Pointer
var bufsize uintptr var bufsize uintptr
if len(buf) > 0 { if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0]) _p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf))
} }
r0, _, e1 := syscall.Syscall(syscall.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := unix.Syscall(unix.SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
@ -129,8 +148,12 @@ func parseDiskstats(buf []byte) (Diskstats, error) {
} }
func Usage(path string) (*UsageStat, error) { func Usage(path string) (*UsageStat, error) {
stat := syscall.Statfs_t{} return UsageWithContext(context.Background(), path)
err := syscall.Statfs(path, &stat) }
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
stat := unix.Statfs_t{}
err := unix.Statfs(path, &stat)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -153,6 +176,6 @@ func Usage(path string) (*UsageStat, error) {
return ret, nil return ret, nil
} }
func getFsType(stat syscall.Statfs_t) string { func getFsType(stat unix.Statfs_t) string {
return common.IntToString(stat.F_fstypename[:]) return common.IntToString(stat.F_fstypename[:])
} }

127
vendor/github.com/shirou/gopsutil/disk/disk_solaris.go generated vendored Normal file
View File

@ -0,0 +1,127 @@
// +build solaris
package disk
import (
"bufio"
"context"
"fmt"
"math"
"os"
"strings"
"github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
)
const (
// _DEFAULT_NUM_MOUNTS is set to `cat /etc/mnttab | wc -l` rounded up to the
// nearest power of two.
_DEFAULT_NUM_MOUNTS = 32
// _MNTTAB default place to read mount information
_MNTTAB = "/etc/mnttab"
)
var (
// A blacklist of read-only virtual filesystems. Writable filesystems are of
// operational concern and must not be included in this list.
fsTypeBlacklist = map[string]struct{}{
"ctfs": struct{}{},
"dev": struct{}{},
"fd": struct{}{},
"lofs": struct{}{},
"lxproc": struct{}{},
"mntfs": struct{}{},
"objfs": struct{}{},
"proc": struct{}{},
}
)
func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
ret := make([]PartitionStat, 0, _DEFAULT_NUM_MOUNTS)
// Scan mnttab(4)
f, err := os.Open(_MNTTAB)
if err != nil {
}
defer func() {
if err == nil {
err = f.Close()
} else {
f.Close()
}
}()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fields := strings.Split(scanner.Text(), "\t")
if _, found := fsTypeBlacklist[fields[2]]; found {
continue
}
ret = append(ret, PartitionStat{
// NOTE(seanc@): Device isn't exactly accurate: from mnttab(4): "The name
// of the resource that has been mounted." Ideally this value would come
// from Statvfs_t.Fsid but I'm leaving it to the caller to traverse
// unix.Statvfs().
Device: fields[0],
Mountpoint: fields[1],
Fstype: fields[2],
Opts: fields[3],
})
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("unable to scan %q: %v", _MNTTAB, err)
}
return ret, err
}
func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
return nil, common.ErrNotImplementedError
}
func Usage(path string) (*UsageStat, error) {
return UsageWithContext(context.Background(), path)
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
statvfs := unix.Statvfs_t{}
if err := unix.Statvfs(path, &statvfs); err != nil {
return nil, fmt.Errorf("unable to call statvfs(2) on %q: %v", path, err)
}
usageStat := &UsageStat{
Path: path,
Fstype: common.IntToString(statvfs.Basetype[:]),
Total: statvfs.Blocks * statvfs.Frsize,
Free: statvfs.Bfree * statvfs.Frsize,
Used: (statvfs.Blocks - statvfs.Bfree) * statvfs.Frsize,
// NOTE: ZFS (and FreeBZSD's UFS2) use dynamic inode/dnode allocation.
// Explicitly return a near-zero value for InodesUsedPercent so that nothing
// attempts to garbage collect based on a lack of available inodes/dnodes.
// Similarly, don't use the zero value to prevent divide-by-zero situations
// and inject a faux near-zero value. Filesystems evolve. Has your
// filesystem evolved? Probably not if you care about the number of
// available inodes.
InodesTotal: 1024.0 * 1024.0,
InodesUsed: 1024.0,
InodesFree: math.MaxUint64,
InodesUsedPercent: (1024.0 / (1024.0 * 1024.0)) * 100.0,
}
usageStat.UsedPercent = (float64(usageStat.Used) / float64(usageStat.Total)) * 100.0
return usageStat, nil
}

View File

@ -2,18 +2,30 @@
package disk package disk
import "syscall" import (
"context"
"strconv"
"golang.org/x/sys/unix"
)
// Usage returns a file system usage. path is a filessytem path such
// as "/", not device file path like "/dev/vda1". If you want to use
// a return value of disk.Partitions, use "Mountpoint" not "Device".
func Usage(path string) (*UsageStat, error) { func Usage(path string) (*UsageStat, error) {
stat := syscall.Statfs_t{} return UsageWithContext(context.Background(), path)
err := syscall.Statfs(path, &stat) }
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
stat := unix.Statfs_t{}
err := unix.Statfs(path, &stat)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bsize := stat.Bsize bsize := stat.Bsize
ret := &UsageStat{ ret := &UsageStat{
Path: path, Path: unescapeFstab(path),
Fstype: getFsType(stat), Fstype: getFsType(stat),
Total: (uint64(stat.Blocks) * uint64(bsize)), Total: (uint64(stat.Blocks) * uint64(bsize)),
Free: (uint64(stat.Bavail) * uint64(bsize)), Free: (uint64(stat.Bavail) * uint64(bsize)),
@ -35,11 +47,22 @@ func Usage(path string) (*UsageStat, error) {
ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0 ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
} }
if ret.Total == 0 { if (ret.Used + ret.Free) == 0 {
ret.UsedPercent = 0 ret.UsedPercent = 0
} else { } else {
ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0 // We don't use ret.Total to calculate percent.
// see https://github.com/shirou/gopsutil/issues/562
ret.UsedPercent = (float64(ret.Used) / float64(ret.Used+ret.Free)) * 100.0
} }
return ret, nil return ret, nil
} }
// Unescape escaped octal chars (like space 040, ampersand 046 and backslash 134) to their real value in fstab fields issue#555
func unescapeFstab(path string) string {
escaped, err := strconv.Unquote(`"` + path + `"`)
if err != nil {
return path
}
return escaped
}

View File

@ -4,12 +4,11 @@ package disk
import ( import (
"bytes" "bytes"
"syscall" "context"
"unsafe" "unsafe"
"github.com/StackExchange/wmi"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows"
) )
var ( var (
@ -37,13 +36,17 @@ type Win32_PerfFormattedData struct {
const WaitMSec = 500 const WaitMSec = 500
func Usage(path string) (*UsageStat, error) { func Usage(path string) (*UsageStat, error) {
return UsageWithContext(context.Background(), path)
}
func UsageWithContext(ctx context.Context, path string) (*UsageStat, error) {
ret := &UsageStat{} ret := &UsageStat{}
lpFreeBytesAvailable := int64(0) lpFreeBytesAvailable := int64(0)
lpTotalNumberOfBytes := int64(0) lpTotalNumberOfBytes := int64(0)
lpTotalNumberOfFreeBytes := int64(0) lpTotalNumberOfFreeBytes := int64(0)
diskret, _, err := procGetDiskFreeSpaceExW.Call( diskret, _, err := procGetDiskFreeSpaceExW.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))),
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
@ -65,6 +68,10 @@ func Usage(path string) (*UsageStat, error) {
} }
func Partitions(all bool) ([]PartitionStat, error) { func Partitions(all bool) ([]PartitionStat, error) {
return PartitionsWithContext(context.Background(), all)
}
func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, error) {
var ret []PartitionStat var ret []PartitionStat
lpBuffer := make([]byte, 254) lpBuffer := make([]byte, 254)
diskret, _, err := procGetLogicalDriveStringsW.Call( diskret, _, err := procGetLogicalDriveStringsW.Call(
@ -79,20 +86,20 @@ func Partitions(all bool) ([]PartitionStat, error) {
if path == "A:" || path == "B:" { // skip floppy drives if path == "A:" || path == "B:" { // skip floppy drives
continue continue
} }
typepath, _ := syscall.UTF16PtrFromString(path) typepath, _ := windows.UTF16PtrFromString(path)
typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath))) typeret, _, _ := procGetDriveType.Call(uintptr(unsafe.Pointer(typepath)))
if typeret == 0 { if typeret == 0 {
return ret, syscall.GetLastError() return ret, windows.GetLastError()
} }
// 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 5: DRIVE_CDROM // 2: DRIVE_REMOVABLE 3: DRIVE_FIXED 4: DRIVE_REMOTE 5: DRIVE_CDROM
if typeret == 2 || typeret == 3 || typeret == 5 { if typeret == 2 || typeret == 3 || typeret == 4 || typeret == 5 {
lpVolumeNameBuffer := make([]byte, 256) lpVolumeNameBuffer := make([]byte, 256)
lpVolumeSerialNumber := int64(0) lpVolumeSerialNumber := int64(0)
lpMaximumComponentLength := int64(0) lpMaximumComponentLength := int64(0)
lpFileSystemFlags := int64(0) lpFileSystemFlags := int64(0)
lpFileSystemNameBuffer := make([]byte, 256) lpFileSystemNameBuffer := make([]byte, 256)
volpath, _ := syscall.UTF16PtrFromString(string(v) + ":/") volpath, _ := windows.UTF16PtrFromString(string(v) + ":/")
driveret, _, err := provGetVolumeInformation.Call( driveret, _, err := provGetVolumeInformation.Call(
uintptr(unsafe.Pointer(volpath)), uintptr(unsafe.Pointer(volpath)),
uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])), uintptr(unsafe.Pointer(&lpVolumeNameBuffer[0])),
@ -103,7 +110,7 @@ func Partitions(all bool) ([]PartitionStat, error) {
uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])), uintptr(unsafe.Pointer(&lpFileSystemNameBuffer[0])),
uintptr(len(lpFileSystemNameBuffer))) uintptr(len(lpFileSystemNameBuffer)))
if driveret == 0 { if driveret == 0 {
if typeret == 5 { if typeret == 5 || typeret == 2 {
continue //device is not ready will happen if there is no disk in the drive continue //device is not ready will happen if there is no disk in the drive
} }
return ret, err return ret, err
@ -129,11 +136,15 @@ func Partitions(all bool) ([]PartitionStat, error) {
return ret, nil return ret, nil
} }
func IOCounters() (map[string]IOCountersStat, error) { func IOCounters(names ...string) (map[string]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), names...)
}
func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOCountersStat, error) {
ret := make(map[string]IOCountersStat, 0) ret := make(map[string]IOCountersStat, 0)
var dst []Win32_PerfFormattedData var dst []Win32_PerfFormattedData
err := wmi.Query("SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk ", &dst) err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst)
if err != nil { if err != nil {
return ret, err return ret, err
} }
@ -141,6 +152,11 @@ func IOCounters() (map[string]IOCountersStat, error) {
if len(d.Name) > 3 { // not get _Total or Harddrive if len(d.Name) > 3 { // not get _Total or Harddrive
continue continue
} }
if len(names) > 0 && !common.StringsHas(names, d.Name) {
continue
}
ret[d.Name] = IOCountersStat{ ret[d.Name] = IOCountersStat{
Name: d.Name, Name: d.Name,
ReadCount: uint64(d.AvgDiskReadQueueLength), ReadCount: uint64(d.AvgDiskReadQueueLength),

View File

@ -6,14 +6,7 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var ( var invoke common.Invoker = common.Invoke{}
invoke common.Invoker
cachedBootTime = uint64(0)
)
func init() {
invoke = common.Invoke{}
}
// A HostInfoStat describes the host status. // A HostInfoStat describes the host status.
// This is not in the psutil but it useful. // This is not in the psutil but it useful.
@ -39,6 +32,11 @@ type UserStat struct {
Started int `json:"started"` Started int `json:"started"`
} }
type TemperatureStat struct {
SensorKey string `json:"sensorKey"`
Temperature float64 `json:"sensorTemperature"`
}
func (h InfoStat) String() string { func (h InfoStat) String() string {
s, _ := json.Marshal(h) s, _ := json.Marshal(h)
return string(s) return string(s)
@ -48,3 +46,8 @@ func (u UserStat) String() string {
s, _ := json.Marshal(u) s, _ := json.Marshal(u)
return string(s) return string(s)
} }
func (t TemperatureStat) String() string {
s, _ := json.Marshal(t)
return string(s)
}

View File

@ -4,6 +4,7 @@ package host
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"io/ioutil" "io/ioutil"
"os" "os"
@ -11,6 +12,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
"unsafe" "unsafe"
@ -22,6 +24,10 @@ import (
const USER_PROCESS = 7 const USER_PROCESS = 7
func Info() (*InfoStat, error) { func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
ret := &InfoStat{ ret := &InfoStat{
OS: runtime.GOOS, OS: runtime.GOOS,
PlatformFamily: "darwin", PlatformFamily: "darwin",
@ -32,12 +38,16 @@ func Info() (*InfoStat, error) {
ret.Hostname = hostname ret.Hostname = hostname
} }
platform, family, pver, version, err := PlatformInformation() kernelVersion, err := KernelVersionWithContext(ctx)
if err == nil {
ret.KernelVersion = kernelVersion
}
platform, family, pver, err := PlatformInformation()
if err == nil { if err == nil {
ret.Platform = platform ret.Platform = platform
ret.PlatformFamily = family ret.PlatformFamily = family
ret.PlatformVersion = pver ret.PlatformVersion = pver
ret.KernelVersion = version
} }
system, role, err := Virtualization() system, role, err := Virtualization()
@ -57,7 +67,7 @@ func Info() (*InfoStat, error) {
ret.Procs = uint64(len(procs)) ret.Procs = uint64(len(procs))
} }
values, err := common.DoSysctrl("kern.uuid") values, err := common.DoSysctrlWithContext(ctx, "kern.uuid")
if err == nil && len(values) == 1 && values[0] != "" { if err == nil && len(values) == 1 && values[0] != "" {
ret.HostID = strings.ToLower(values[0]) ret.HostID = strings.ToLower(values[0])
} }
@ -65,11 +75,19 @@ func Info() (*InfoStat, error) {
return ret, nil return ret, nil
} }
// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
if cachedBootTime != 0 { return BootTimeWithContext(context.Background())
return cachedBootTime, nil }
func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
} }
values, err := common.DoSysctrl("kern.boottime") values, err := common.DoSysctrlWithContext(ctx, "kern.boottime")
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -79,9 +97,10 @@ func BootTime() (uint64, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
cachedBootTime = uint64(boottime) t = uint64(boottime)
atomic.StoreUint64(&cachedBootTime, t)
return cachedBootTime, nil return t, nil
} }
func uptime(boot uint64) uint64 { func uptime(boot uint64) uint64 {
@ -89,6 +108,10 @@ func uptime(boot uint64) uint64 {
} }
func Uptime() (uint64, error) { func Uptime() (uint64, error) {
return UptimeWithContext(context.Background())
}
func UptimeWithContext(ctx context.Context) (uint64, error) {
boot, err := BootTime() boot, err := BootTime()
if err != nil { if err != nil {
return 0, err return 0, err
@ -97,6 +120,10 @@ func Uptime() (uint64, error) {
} }
func Users() ([]UserStat, error) { func Users() ([]UserStat, error) {
return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
utmpfile := "/var/run/utmpx" utmpfile := "/var/run/utmpx"
var ret []UserStat var ret []UserStat
@ -104,6 +131,7 @@ func Users() ([]UserStat, error) {
if err != nil { if err != nil {
return ret, err return ret, err
} }
defer file.Close()
buf, err := ioutil.ReadAll(file) buf, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
@ -139,42 +167,66 @@ func Users() ([]UserStat, error) {
} }
func PlatformInformation() (string, string, string, string, error) { func PlatformInformation() (string, string, string, error) {
return PlatformInformationWithContext(context.Background())
}
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
platform := "" platform := ""
family := "" family := ""
version := ""
pver := "" pver := ""
sw_vers, err := exec.LookPath("sw_vers") sw_vers, err := exec.LookPath("sw_vers")
if err != nil { if err != nil {
return "", "", "", "", err return "", "", "", err
} }
uname, err := exec.LookPath("uname") uname, err := exec.LookPath("uname")
if err != nil { if err != nil {
return "", "", "", "", err return "", "", "", err
} }
out, err := invoke.Command(uname, "-s") out, err := invoke.CommandWithContext(ctx, uname, "-s")
if err == nil { if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out))) platform = strings.ToLower(strings.TrimSpace(string(out)))
} }
out, err = invoke.Command(sw_vers, "-productVersion") out, err = invoke.CommandWithContext(ctx, sw_vers, "-productVersion")
if err == nil { if err == nil {
pver = strings.ToLower(strings.TrimSpace(string(out))) pver = strings.ToLower(strings.TrimSpace(string(out)))
} }
out, err = invoke.Command(uname, "-r") return platform, family, pver, nil
if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out)))
}
return platform, family, pver, version, nil
} }
func Virtualization() (string, string, error) { func Virtualization() (string, string, error) {
system := "" return VirtualizationWithContext(context.Background())
role := "" }
return system, role, nil func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
}
func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (string, error) {
uname, err := exec.LookPath("uname")
if err != nil {
return "", err
}
out, err := invoke.CommandWithContext(ctx, uname, "-r")
if err != nil {
return "", err
}
version := strings.ToLower(strings.TrimSpace(string(out)))
return version, err
}
func SensorsTemperatures() ([]TemperatureStat, error) {
return SensorsTemperaturesWithContext(context.Background())
}
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
return []TemperatureStat{}, common.ErrNotImplementedError
} }

View File

@ -1,21 +1,65 @@
// +build !darwin,!linux,!freebsd,!openbsd,!windows // +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows
package host package host
import "github.com/shirou/gopsutil/internal/common" import (
"context"
"github.com/shirou/gopsutil/internal/common"
)
func Info() (*InfoStat, error) { func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
return BootTimeWithContext(context.Background())
}
func BootTimeWithContext(ctx context.Context) (uint64, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func Uptime() (uint64, error) { func Uptime() (uint64, error) {
return UptimeWithContext(context.Background())
}
func UptimeWithContext(ctx context.Context) (uint64, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func Users() ([]UserStat, error) { func Users() ([]UserStat, error) {
return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
return []UserStat{}, common.ErrNotImplementedError return []UserStat{}, common.ErrNotImplementedError
} }
func Virtualization() (string, string, error) {
return VirtualizationWithContext(context.Background())
}
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
}
func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError
}
func PlatformInformation() (string, string, string, error) {
return PlatformInformationWithContext(context.Background())
}
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
return "", "", "", common.ErrNotImplementedError
}

View File

@ -4,18 +4,20 @@ package host
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync/atomic"
"syscall"
"time" "time"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"github.com/shirou/gopsutil/process" "github.com/shirou/gopsutil/process"
"golang.org/x/sys/unix"
) )
const ( const (
@ -25,6 +27,10 @@ const (
) )
func Info() (*InfoStat, error) { func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
ret := &InfoStat{ ret := &InfoStat{
OS: runtime.GOOS, OS: runtime.GOOS,
PlatformFamily: "freebsd", PlatformFamily: "freebsd",
@ -60,32 +66,35 @@ func Info() (*InfoStat, error) {
ret.Procs = uint64(len(procs)) ret.Procs = uint64(len(procs))
} }
values, err := common.DoSysctrl("kern.hostuuid") hostid, err := unix.Sysctl("kern.hostuuid")
if err == nil && len(values) == 1 && values[0] != "" { if err == nil && hostid != "" {
ret.HostID = strings.ToLower(values[0]) ret.HostID = strings.ToLower(hostid)
} }
return ret, nil return ret, nil
} }
// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
if cachedBootTime != 0 { return BootTimeWithContext(context.Background())
return cachedBootTime, nil }
func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
} }
values, err := common.DoSysctrl("kern.boottime") buf, err := unix.SysctlRaw("kern.boottime")
if err != nil { if err != nil {
return 0, err return 0, err
} }
// ex: { sec = 1392261637, usec = 627534 } Thu Feb 13 12:20:37 2014
v := strings.Replace(values[2], ",", "", 1)
boottime, err := strconv.ParseUint(v, 10, 64) tv := *(*syscall.Timeval)(unsafe.Pointer((&buf[0])))
if err != nil { atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec))
return 0, err
}
cachedBootTime = boottime
return boottime, nil return t, nil
} }
func uptime(boot uint64) uint64 { func uptime(boot uint64) uint64 {
@ -93,6 +102,10 @@ func uptime(boot uint64) uint64 {
} }
func Uptime() (uint64, error) { func Uptime() (uint64, error) {
return UptimeWithContext(context.Background())
}
func UptimeWithContext(ctx context.Context) (uint64, error) {
boot, err := BootTime() boot, err := BootTime()
if err != nil { if err != nil {
return 0, err return 0, err
@ -101,6 +114,10 @@ func Uptime() (uint64, error) {
} }
func Users() ([]UserStat, error) { func Users() ([]UserStat, error) {
return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
utmpfile := "/var/run/utx.active" utmpfile := "/var/run/utx.active"
if !common.PathExists(utmpfile) { if !common.PathExists(utmpfile) {
utmpfile = "/var/run/utmp" // before 9.0 utmpfile = "/var/run/utmp" // before 9.0
@ -112,6 +129,7 @@ func Users() ([]UserStat, error) {
if err != nil { if err != nil {
return ret, err return ret, err
} }
defer file.Close()
buf, err := ioutil.ReadAll(file) buf, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
@ -145,32 +163,29 @@ func Users() ([]UserStat, error) {
} }
func PlatformInformation() (string, string, string, error) { func PlatformInformation() (string, string, string, error) {
platform := "" return PlatformInformationWithContext(context.Background())
family := "" }
version := ""
uname, err := exec.LookPath("uname") func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
platform, err := unix.Sysctl("kern.ostype")
if err != nil { if err != nil {
return "", "", "", err return "", "", "", err
} }
out, err := invoke.Command(uname, "-s") version, err := unix.Sysctl("kern.osrelease")
if err == nil { if err != nil {
platform = strings.ToLower(strings.TrimSpace(string(out))) return "", "", "", err
} }
out, err = invoke.Command(uname, "-r") return strings.ToLower(platform), "", strings.ToLower(version), nil
if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out)))
}
return platform, family, version, nil
} }
func Virtualization() (string, string, error) { func Virtualization() (string, string, error) {
system := "" return VirtualizationWithContext(context.Background())
role := "" }
return system, role, nil func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
} }
// before 9.0 // before 9.0
@ -180,6 +195,8 @@ func getUsersFromUtmp(utmpfile string) ([]UserStat, error) {
if err != nil { if err != nil {
return ret, err return ret, err
} }
defer file.Close()
buf, err := ioutil.ReadAll(file) buf, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
return ret, err return ret, err
@ -209,3 +226,20 @@ func getUsersFromUtmp(utmpfile string) ([]UserStat, error) {
return ret, nil return ret, nil
} }
func SensorsTemperatures() ([]TemperatureStat, error) {
return SensorsTemperaturesWithContext(context.Background())
}
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
return []TemperatureStat{}, common.ErrNotImplementedError
}
func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (string, error) {
_, _, version, err := PlatformInformation()
return version, err
}

View File

@ -4,15 +4,18 @@ package host
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
@ -29,6 +32,10 @@ type LSB struct {
const USER_PROCESS = 7 const USER_PROCESS = 7
func Info() (*InfoStat, error) { func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
ret := &InfoStat{ ret := &InfoStat{
OS: runtime.GOOS, OS: runtime.GOOS,
} }
@ -84,29 +91,68 @@ func Info() (*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) {
if cachedBootTime != 0 { return BootTimeWithContext(context.Background())
return cachedBootTime, nil }
func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
} }
filename := common.HostProc("stat")
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) lines, err := common.ReadLines(filename)
if err != nil { if err != nil {
return 0, err return 0, err
} }
for _, line := range lines {
if strings.HasPrefix(line, "btime") { if statFile == "stat" {
f := strings.Fields(line) for _, line := range lines {
if len(f) != 2 { if strings.HasPrefix(line, "btime") {
return 0, fmt.Errorf("wrong btime format") 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
} }
b, err := strconv.ParseInt(f[1], 10, 64)
if err != nil {
return 0, err
}
cachedBootTime = uint64(b)
return cachedBootTime, 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 0, fmt.Errorf("could not find btime")
@ -117,6 +163,10 @@ func uptime(boot uint64) uint64 {
} }
func Uptime() (uint64, error) { func Uptime() (uint64, error) {
return UptimeWithContext(context.Background())
}
func UptimeWithContext(ctx context.Context) (uint64, error) {
boot, err := BootTime() boot, err := BootTime()
if err != nil { if err != nil {
return 0, err return 0, err
@ -125,12 +175,17 @@ func Uptime() (uint64, error) {
} }
func Users() ([]UserStat, error) { func Users() ([]UserStat, error) {
utmpfile := "/var/run/utmp" return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
utmpfile := common.HostVar("run/utmp")
file, err := os.Open(utmpfile) file, err := os.Open(utmpfile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close()
buf, err := ioutil.ReadAll(file) buf, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
@ -241,6 +296,10 @@ func getLSB() (*LSB, error) {
} }
func PlatformInformation() (platform string, family string, version string, err error) { func PlatformInformation() (platform string, family string, version string, err error) {
return PlatformInformationWithContext(context.Background())
}
func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
lsb, err := getLSB() lsb, err := getLSB()
if err != nil { if err != nil {
@ -260,6 +319,12 @@ func PlatformInformation() (platform string, family string, version string, err
if err == nil { if err == nil {
version = getRedhatishVersion(contents) version = getRedhatishVersion(contents)
} }
} else if common.PathExists(common.HostEtc("slackware-version")) {
platform = "slackware"
contents, err := common.ReadLines(common.HostEtc("slackware-version"))
if err == nil {
version = getSlackwareVersion(contents)
}
} else if common.PathExists(common.HostEtc("debian_version")) { } else if common.PathExists(common.HostEtc("debian_version")) {
if lsb.ID == "Ubuntu" { if lsb.ID == "Ubuntu" {
platform = "ubuntu" platform = "ubuntu"
@ -363,6 +428,10 @@ func PlatformInformation() (platform string, family string, version string, err
} }
func KernelVersion() (version string, err error) { func KernelVersion() (version string, err error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (version string, err error) {
filename := common.HostProc("sys/kernel/osrelease") filename := common.HostProc("sys/kernel/osrelease")
if common.PathExists(filename) { if common.PathExists(filename) {
contents, err := common.ReadLines(filename) contents, err := common.ReadLines(filename)
@ -378,6 +447,12 @@ func KernelVersion() (version string, err error) {
return version, nil return version, nil
} }
func getSlackwareVersion(contents []string) string {
c := strings.ToLower(strings.Join(contents, ""))
c = strings.Replace(c, "slackware ", "", 1)
return c
}
func getRedhatishVersion(contents []string) string { func getRedhatishVersion(contents []string) string {
c := strings.ToLower(strings.Join(contents, "")) c := strings.ToLower(strings.Join(contents, ""))
@ -422,6 +497,10 @@ func getSusePlatform(contents []string) string {
} }
func Virtualization() (string, string, error) { func Virtualization() (string, string, error) {
return VirtualizationWithContext(context.Background())
}
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
var system string var system string
var role string var role string
@ -430,8 +509,8 @@ func Virtualization() (string, string, error) {
system = "xen" system = "xen"
role = "guest" // assume guest role = "guest" // assume guest
if common.PathExists(filename + "/capabilities") { if common.PathExists(filepath.Join(filename, "capabilities")) {
contents, err := common.ReadLines(filename + "/capabilities") contents, err := common.ReadLines(filepath.Join(filename, "capabilities"))
if err == nil { if err == nil {
if common.StringsContains(contents, "control_d") { if common.StringsContains(contents, "control_d") {
role = "host" role = "host"
@ -453,6 +532,9 @@ func Virtualization() (string, string, error) {
} else if common.StringsContains(contents, "vboxguest") { } else if common.StringsContains(contents, "vboxguest") {
system = "vbox" system = "vbox"
role = "guest" role = "guest"
} else if common.StringsContains(contents, "vmware") {
system = "vmware"
role = "guest"
} }
} }
} }
@ -471,17 +553,17 @@ func Virtualization() (string, string, error) {
} }
filename = common.HostProc() filename = common.HostProc()
if common.PathExists(filename + "/bc/0") { if common.PathExists(filepath.Join(filename, "bc", "0")) {
system = "openvz" system = "openvz"
role = "host" role = "host"
} else if common.PathExists(filename + "/vz") { } else if common.PathExists(filepath.Join(filename, "vz")) {
system = "openvz" system = "openvz"
role = "guest" role = "guest"
} }
// not use dmidecode because it requires root // not use dmidecode because it requires root
if common.PathExists(filename + "/self/status") { if common.PathExists(filepath.Join(filename, "self", "status")) {
contents, err := common.ReadLines(filename + "/self/status") contents, err := common.ReadLines(filepath.Join(filename, "self", "status"))
if err == nil { if err == nil {
if common.StringsContains(contents, "s_context:") || if common.StringsContains(contents, "s_context:") ||
@ -492,8 +574,8 @@ func Virtualization() (string, string, error) {
} }
} }
if common.PathExists(filename + "/self/cgroup") { if common.PathExists(filepath.Join(filename, "self", "cgroup")) {
contents, err := common.ReadLines(filename + "/self/cgroup") contents, err := common.ReadLines(filepath.Join(filename, "self", "cgroup"))
if err == nil { if err == nil {
if common.StringsContains(contents, "lxc") { if common.StringsContains(contents, "lxc") {
system = "lxc" system = "lxc"
@ -520,3 +602,68 @@ func Virtualization() (string, string, error) {
} }
return system, role, nil return system, role, nil
} }
func SensorsTemperatures() ([]TemperatureStat, error) {
return SensorsTemperaturesWithContext(context.Background())
}
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
var temperatures []TemperatureStat
files, err := filepath.Glob(common.HostSys("/class/hwmon/hwmon*/temp*_*"))
if err != nil {
return temperatures, err
}
if len(files) == 0 {
// CentOS has an intermediate /device directory:
// https://github.com/giampaolo/psutil/issues/971
files, err = filepath.Glob(common.HostSys("/class/hwmon/hwmon*/device/temp*_*"))
if err != nil {
return temperatures, err
}
}
// example directory
// device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm
// name temp1_input temp2_input temp3_input temp4_input temp5_input temp6_input temp7_input
// power/ temp1_label temp2_label temp3_label temp4_label temp5_label temp6_label temp7_label
// subsystem/ temp1_max temp2_max temp3_max temp4_max temp5_max temp6_max temp7_max
// temp1_crit temp2_crit temp3_crit temp4_crit temp5_crit temp6_crit temp7_crit uevent
for _, file := range files {
filename := strings.Split(filepath.Base(file), "_")
if filename[1] == "label" {
// Do not try to read the temperature of the label file
continue
}
// Get the label of the temperature you are reading
var label string
c, _ := ioutil.ReadFile(filepath.Join(filepath.Dir(file), filename[0]+"_label"))
if c != nil {
//format the label from "Core 0" to "core0_"
label = fmt.Sprintf("%s_", strings.Join(strings.Split(strings.TrimSpace(strings.ToLower(string(c))), " "), ""))
}
// Get the name of the tempearture you are reading
name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name"))
if err != nil {
return temperatures, err
}
// Get the temperature reading
current, err := ioutil.ReadFile(file)
if err != nil {
return temperatures, err
}
temperature, err := strconv.ParseFloat(strings.TrimSpace(string(current)), 64)
if err != nil {
continue
}
tempName := strings.TrimSpace(strings.ToLower(string(strings.Join(filename[1:], ""))))
temperatures = append(temperatures, TemperatureStat{
SensorKey: fmt.Sprintf("%s_%s%s", strings.TrimSpace(string(name)), label, tempName),
Temperature: temperature / 1000.0,
})
}
return temperatures, nil
}

View File

@ -0,0 +1,43 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
package host
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
sizeOfUtmp = 0x180
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
)
type utmp struct {
Type int16
Pad_cgo_0 [2]byte
Pid int32
Line [32]int8
Id [4]int8
User [32]int8
Host [256]int8
Exit exit_status
Session int32
Tv timeval
Addr_v6 [4]int32
X__unused [20]int8
}
type exit_status struct {
Termination int16
Exit int16
}
type timeval struct {
Sec int32
Usec int32
}

View File

@ -0,0 +1,43 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
package host
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
sizeOfUtmp = 0x180
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
)
type utmp struct {
Type int16
Pad_cgo_0 [2]byte
Pid int32
Line [32]int8
Id [4]int8
User [32]int8
Host [256]int8
Exit exit_status
Session int32
Tv timeval
Addr_v6 [4]int32
X__unused [20]int8
}
type exit_status struct {
Termination int16
Exit int16
}
type timeval struct {
Sec int32
Usec int32
}

View File

@ -0,0 +1,43 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
package host
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
sizeOfUtmp = 0x180
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
)
type utmp struct {
Type int16
Pad_cgo_0 [2]byte
Pid int32
Line [32]int8
Id [4]int8
User [32]int8
Host [256]int8
Exit exit_status
Session int32
Tv timeval
Addr_v6 [4]int32
X__unused [20]int8
}
type exit_status struct {
Termination int16
Exit int16
}
type timeval struct {
Sec int32
Usec int32
}

View File

@ -0,0 +1,43 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_linux.go
package host
const (
sizeofPtr = 0x4
sizeofShort = 0x2
sizeofInt = 0x4
sizeofLong = 0x4
sizeofLongLong = 0x8
sizeOfUtmp = 0x180
)
type (
_C_short int16
_C_int int32
_C_long int32
_C_long_long int64
)
type utmp struct {
Type int16
Pad_cgo_0 [2]byte
Pid int32
Line [32]int8
Id [4]int8
User [32]int8
Host [256]int8
Exit exit_status
Session int32
Tv timeval
Addr_v6 [4]int32
X__unused [20]int8
}
type exit_status struct {
Termination int16
Exit int16
}
type timeval struct {
Sec int32
Usec int32
}

View File

@ -4,6 +4,7 @@ package host
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"io/ioutil" "io/ioutil"
"os" "os"
@ -25,6 +26,10 @@ const (
) )
func Info() (*InfoStat, error) { func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
ret := &InfoStat{ ret := &InfoStat{
OS: runtime.GOOS, OS: runtime.GOOS,
PlatformFamily: "openbsd", PlatformFamily: "openbsd",
@ -62,6 +67,10 @@ func Info() (*InfoStat, error) {
} }
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
return BootTimeWithContext(context.Background())
}
func BootTimeWithContext(ctx context.Context) (uint64, error) {
val, err := common.DoSysctrl("kern.boottime") val, err := common.DoSysctrl("kern.boottime")
if err != nil { if err != nil {
return 0, err return 0, err
@ -80,6 +89,10 @@ func uptime(boot uint64) uint64 {
} }
func Uptime() (uint64, error) { func Uptime() (uint64, error) {
return UptimeWithContext(context.Background())
}
func UptimeWithContext(ctx context.Context) (uint64, error) {
boot, err := BootTime() boot, err := BootTime()
if err != nil { if err != nil {
return 0, err return 0, err
@ -88,6 +101,10 @@ func Uptime() (uint64, error) {
} }
func PlatformInformation() (string, string, string, error) { func PlatformInformation() (string, string, string, error) {
return PlatformInformationWithContext(context.Background())
}
func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) {
platform := "" platform := ""
family := "" family := ""
version := "" version := ""
@ -96,12 +113,12 @@ func PlatformInformation() (string, string, string, error) {
return "", "", "", err return "", "", "", err
} }
out, err := invoke.Command(uname, "-s") out, err := invoke.CommandWithContext(ctx, uname, "-s")
if err == nil { if err == nil {
platform = strings.ToLower(strings.TrimSpace(string(out))) platform = strings.ToLower(strings.TrimSpace(string(out)))
} }
out, err = invoke.Command(uname, "-r") out, err = invoke.CommandWithContext(ctx, uname, "-r")
if err == nil { if err == nil {
version = strings.ToLower(strings.TrimSpace(string(out))) version = strings.ToLower(strings.TrimSpace(string(out)))
} }
@ -110,19 +127,26 @@ func PlatformInformation() (string, string, string, error) {
} }
func Virtualization() (string, string, error) { func Virtualization() (string, string, error) {
system := "" return VirtualizationWithContext(context.Background())
role := "" }
return system, role, nil func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
} }
func Users() ([]UserStat, error) { func Users() ([]UserStat, error) {
return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
var ret []UserStat var ret []UserStat
utmpfile := "/var/run/utmp" utmpfile := "/var/run/utmp"
file, err := os.Open(utmpfile) file, err := os.Open(utmpfile)
if err != nil { if err != nil {
return ret, err return ret, err
} }
defer file.Close()
buf, err := ioutil.ReadAll(file) buf, err := ioutil.ReadAll(file)
if err != nil { if err != nil {
return ret, err return ret, err
@ -152,3 +176,20 @@ func Users() ([]UserStat, error) {
return ret, nil return ret, nil
} }
func SensorsTemperatures() ([]TemperatureStat, error) {
return SensorsTemperaturesWithContext(context.Background())
}
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
return []TemperatureStat{}, common.ErrNotImplementedError
}
func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (string, error) {
_, _, version, err := PlatformInformation()
return version, err
}

248
vendor/github.com/shirou/gopsutil/host/host_solaris.go generated vendored Normal file
View File

@ -0,0 +1,248 @@
package host
import (
"bufio"
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"
"time"
"github.com/shirou/gopsutil/internal/common"
)
func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
result := &InfoStat{
OS: runtime.GOOS,
}
hostname, err := os.Hostname()
if err != nil {
return nil, err
}
result.Hostname = hostname
// Parse versions from output of `uname(1)`
uname, err := exec.LookPath("/usr/bin/uname")
if err != nil {
return nil, err
}
out, err := invoke.CommandWithContext(ctx, uname, "-srv")
if err != nil {
return nil, err
}
fields := strings.Fields(string(out))
if len(fields) >= 1 {
result.PlatformFamily = fields[0]
}
if len(fields) >= 2 {
result.KernelVersion = fields[1]
}
if len(fields) == 3 {
result.PlatformVersion = fields[2]
}
// Find distribution name from /etc/release
fh, err := os.Open("/etc/release")
if err != nil {
return nil, err
}
defer fh.Close()
sc := bufio.NewScanner(fh)
if sc.Scan() {
line := strings.TrimSpace(sc.Text())
switch {
case strings.HasPrefix(line, "SmartOS"):
result.Platform = "SmartOS"
case strings.HasPrefix(line, "OpenIndiana"):
result.Platform = "OpenIndiana"
case strings.HasPrefix(line, "OmniOS"):
result.Platform = "OmniOS"
case strings.HasPrefix(line, "Open Storage"):
result.Platform = "NexentaStor"
case strings.HasPrefix(line, "Solaris"):
result.Platform = "Solaris"
case strings.HasPrefix(line, "Oracle Solaris"):
result.Platform = "Solaris"
default:
result.Platform = strings.Fields(line)[0]
}
}
switch result.Platform {
case "SmartOS":
// If everything works, use the current zone ID as the HostID if present.
zonename, err := exec.LookPath("/usr/bin/zonename")
if err == nil {
out, err := invoke.CommandWithContext(ctx, zonename)
if err == nil {
sc := bufio.NewScanner(bytes.NewReader(out))
for sc.Scan() {
line := sc.Text()
// If we're in the global zone, rely on the hostname.
if line == "global" {
hostname, err := os.Hostname()
if err == nil {
result.HostID = hostname
}
} else {
result.HostID = strings.TrimSpace(line)
break
}
}
}
}
}
// If HostID is still empty, use hostid(1), which can lie to callers but at
// this point there are no hardware facilities available. This behavior
// matches that of other supported OSes.
if result.HostID == "" {
hostID, err := exec.LookPath("/usr/bin/hostid")
if err == nil {
out, err := invoke.CommandWithContext(ctx, hostID)
if err == nil {
sc := bufio.NewScanner(bytes.NewReader(out))
for sc.Scan() {
line := sc.Text()
result.HostID = strings.TrimSpace(line)
break
}
}
}
}
// Find the boot time and calculate uptime relative to it
bootTime, err := BootTime()
if err != nil {
return nil, err
}
result.BootTime = bootTime
result.Uptime = uptimeSince(bootTime)
// Count number of processes based on the number of entries in /proc
dirs, err := ioutil.ReadDir("/proc")
if err != nil {
return nil, err
}
result.Procs = uint64(len(dirs))
return result, nil
}
var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`)
func BootTime() (uint64, error) {
return BootTimeWithContext(context.Background())
}
func BootTimeWithContext(ctx context.Context) (uint64, error) {
kstat, err := exec.LookPath("/usr/bin/kstat")
if err != nil {
return 0, err
}
out, err := invoke.CommandWithContext(ctx, kstat, "-p", "unix:0:system_misc:boot_time")
if err != nil {
return 0, err
}
kstats := kstatMatch.FindAllStringSubmatch(string(out), -1)
if len(kstats) != 1 {
return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats))
}
return strconv.ParseUint(kstats[0][2], 10, 64)
}
func Uptime() (uint64, error) {
return UptimeWithContext(context.Background())
}
func UptimeWithContext(ctx context.Context) (uint64, error) {
bootTime, err := BootTime()
if err != nil {
return 0, err
}
return uptimeSince(bootTime), nil
}
func uptimeSince(since uint64) uint64 {
return uint64(time.Now().Unix()) - since
}
func Users() ([]UserStat, error) {
return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
return []UserStat{}, common.ErrNotImplementedError
}
func SensorsTemperatures() ([]TemperatureStat, error) {
return SensorsTemperaturesWithContext(context.Background())
}
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
return []TemperatureStat{}, common.ErrNotImplementedError
}
func Virtualization() (string, string, error) {
return VirtualizationWithContext(context.Background())
}
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
}
func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (string, error) {
// Parse versions from output of `uname(1)`
uname, err := exec.LookPath("/usr/bin/uname")
if err != nil {
return "", err
}
out, err := invoke.CommandWithContext(ctx, uname, "-srv")
if err != nil {
return "", err
}
fields := strings.Fields(string(out))
if len(fields) >= 2 {
return fields[1], nil
}
return "", fmt.Errorf("could not get kernel version")
}
func PlatformInformation() (platform string, family string, version string, err error) {
return PlatformInformationWithContext(context.Background())
}
func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
/* This is not finished yet at all. Please contribute! */
version, err = KernelVersion()
if err != nil {
return "", "", "", err
}
return "solaris", "solaris", version, nil
}

View File

@ -3,34 +3,57 @@
package host package host
import ( import (
"context"
"fmt" "fmt"
"math"
"os" "os"
"runtime" "runtime"
"strings" "strings"
"sync/atomic"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
"github.com/StackExchange/wmi" "github.com/StackExchange/wmi"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
process "github.com/shirou/gopsutil/process" process "github.com/shirou/gopsutil/process"
"golang.org/x/sys/windows"
) )
var ( var (
procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime") procGetSystemTimeAsFileTime = common.Modkernel32.NewProc("GetSystemTimeAsFileTime")
osInfo *Win32_OperatingSystem procGetTickCount32 = common.Modkernel32.NewProc("GetTickCount")
procGetTickCount64 = common.Modkernel32.NewProc("GetTickCount64")
procRtlGetVersion = common.ModNt.NewProc("RtlGetVersion")
) )
type Win32_OperatingSystem struct { // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_osversioninfoexw
Version string type osVersionInfoExW struct {
Caption string dwOSVersionInfoSize uint32
ProductType uint32 dwMajorVersion uint32
BuildNumber string dwMinorVersion uint32
LastBootUpTime time.Time dwBuildNumber uint32
dwPlatformId uint32
szCSDVersion [128]uint16
wServicePackMajor uint16
wServicePackMinor uint16
wSuiteMask uint16
wProductType uint8
wReserved uint8
}
type msAcpi_ThermalZoneTemperature struct {
Active bool
CriticalTripPoint uint32
CurrentTemperature uint32
InstanceName string
} }
func Info() (*InfoStat, error) { func Info() (*InfoStat, error) {
return InfoWithContext(context.Background())
}
func InfoWithContext(ctx context.Context) (*InfoStat, error) {
ret := &InfoStat{ ret := &InfoStat{
OS: runtime.GOOS, OS: runtime.GOOS,
} }
@ -43,7 +66,7 @@ func Info() (*InfoStat, error) {
} }
{ {
platform, family, version, err := PlatformInformation() platform, family, version, err := PlatformInformationWithContext(ctx)
if err == nil { if err == nil {
ret.Platform = platform ret.Platform = platform
ret.PlatformFamily = family ret.PlatformFamily = family
@ -79,12 +102,14 @@ func Info() (*InfoStat, error) {
} }
func getMachineGuid() (string, error) { func getMachineGuid() (string, error) {
var h syscall.Handle // there has been reports of issues on 32bit using golang.org/x/sys/windows/registry, see https://github.com/shirou/gopsutil/pull/312#issuecomment-277422612
err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, syscall.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, syscall.KEY_READ, &h) // for rationale of using windows.RegOpenKeyEx/RegQueryValueEx instead of registry.OpenKey/GetStringValue
var h windows.Handle
err := windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Cryptography`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
if err != nil { if err != nil {
return "", err return "", err
} }
defer syscall.RegCloseKey(h) defer windows.RegCloseKey(h)
const windowsRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16 const windowsRegBufLen = 74 // len(`{`) + len(`abcdefgh-1234-456789012-123345456671` * 2) + len(`}`) // 2 == bytes/UTF16
const uuidLen = 36 const uuidLen = 36
@ -92,12 +117,12 @@ func getMachineGuid() (string, error) {
var regBuf [windowsRegBufLen]uint16 var regBuf [windowsRegBufLen]uint16
bufLen := uint32(windowsRegBufLen) bufLen := uint32(windowsRegBufLen)
var valType uint32 var valType uint32
err = syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen) err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`MachineGuid`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
if err != nil { if err != nil {
return "", err return "", err
} }
hostID := syscall.UTF16ToString(regBuf[:]) hostID := windows.UTF16ToString(regBuf[:])
hostIDLen := len(hostID) hostIDLen := len(hostID)
if hostIDLen != uuidLen { if hostIDLen != uuidLen {
return "", fmt.Errorf("HostID incorrect: %q\n", hostID) return "", fmt.Errorf("HostID incorrect: %q\n", hostID)
@ -106,60 +131,97 @@ func getMachineGuid() (string, error) {
return hostID, nil return hostID, nil
} }
func GetOSInfo() (Win32_OperatingSystem, error) {
var dst []Win32_OperatingSystem
q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil {
return Win32_OperatingSystem{}, err
}
osInfo = &dst[0]
return dst[0], nil
}
func Uptime() (uint64, error) { func Uptime() (uint64, error) {
if osInfo == nil { return UptimeWithContext(context.Background())
_, err := GetOSInfo()
if err != nil {
return 0, err
}
}
now := time.Now()
t := osInfo.LastBootUpTime.Local()
return uint64(now.Sub(t).Seconds()), nil
} }
func bootTime(up uint64) uint64 { func UptimeWithContext(ctx context.Context) (uint64, error) {
procGetTickCount := procGetTickCount64
err := procGetTickCount64.Find()
if err != nil {
procGetTickCount = procGetTickCount32 // handle WinXP, but keep in mind that "the time will wrap around to zero if the system is run continuously for 49.7 days." from MSDN
}
r1, _, lastErr := syscall.Syscall(procGetTickCount.Addr(), 0, 0, 0, 0)
if lastErr != 0 {
return 0, lastErr
}
return uint64((time.Duration(r1) * time.Millisecond).Seconds()), nil
}
func bootTimeFromUptime(up uint64) uint64 {
return uint64(time.Now().Unix()) - up return uint64(time.Now().Unix()) - up
} }
// cachedBootTime must be accessed via atomic.Load/StoreUint64
var cachedBootTime uint64
func BootTime() (uint64, error) { func BootTime() (uint64, error) {
if cachedBootTime != 0 { return BootTimeWithContext(context.Background())
return cachedBootTime, nil }
func BootTimeWithContext(ctx context.Context) (uint64, error) {
t := atomic.LoadUint64(&cachedBootTime)
if t != 0 {
return t, nil
} }
up, err := Uptime() up, err := Uptime()
if err != nil { if err != nil {
return 0, err return 0, err
} }
cachedBootTime = bootTime(up) t = bootTimeFromUptime(up)
return cachedBootTime, nil atomic.StoreUint64(&cachedBootTime, t)
return t, nil
} }
func PlatformInformation() (platform string, family string, version string, err error) { func PlatformInformation() (platform string, family string, version string, err error) {
if osInfo == nil { return PlatformInformationWithContext(context.Background())
_, err = GetOSInfo() }
if err != nil {
return func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) {
} // GetVersionEx lies on Windows 8.1 and returns as Windows 8 if we don't declare compatibility in manifest
// RtlGetVersion bypasses this lying layer and returns the true Windows version
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlgetversion
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_osversioninfoexw
var osInfo osVersionInfoExW
osInfo.dwOSVersionInfoSize = uint32(unsafe.Sizeof(osInfo))
ret, _, err := procRtlGetVersion.Call(uintptr(unsafe.Pointer(&osInfo)))
if ret != 0 {
return
} }
// Platform // Platform
platform = strings.Trim(osInfo.Caption, " ") var h windows.Handle // like getMachineGuid(), we query the registry using the raw windows.RegOpenKeyEx/RegQueryValueEx
err = windows.RegOpenKeyEx(windows.HKEY_LOCAL_MACHINE, windows.StringToUTF16Ptr(`SOFTWARE\Microsoft\Windows NT\CurrentVersion`), 0, windows.KEY_READ|windows.KEY_WOW64_64KEY, &h)
if err != nil {
return
}
defer windows.RegCloseKey(h)
var bufLen uint32
var valType uint32
err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`ProductName`), nil, &valType, nil, &bufLen)
if err != nil {
return
}
regBuf := make([]uint16, bufLen/2+1)
err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`ProductName`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
if err != nil {
return
}
platform = windows.UTF16ToString(regBuf[:])
if !strings.HasPrefix(platform, "Microsoft") {
platform = "Microsoft " + platform
}
err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CSDVersion`), nil, &valType, nil, &bufLen) // append Service Pack number, only on success
if err == nil { // don't return an error if only the Service Pack retrieval fails
regBuf = make([]uint16, bufLen/2+1)
err = windows.RegQueryValueEx(h, windows.StringToUTF16Ptr(`CSDVersion`), nil, &valType, (*byte)(unsafe.Pointer(&regBuf[0])), &bufLen)
if err == nil {
platform += " " + windows.UTF16ToString(regBuf[:])
}
}
// PlatformFamily // PlatformFamily
switch osInfo.ProductType { switch osInfo.wProductType {
case 1: case 1:
family = "Standalone Workstation" family = "Standalone Workstation"
case 2: case 2:
@ -169,13 +231,65 @@ func PlatformInformation() (platform string, family string, version string, err
} }
// Platform Version // Platform Version
version = fmt.Sprintf("%s Build %s", osInfo.Version, osInfo.BuildNumber) version = fmt.Sprintf("%d.%d.%d Build %d", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.dwBuildNumber)
return return platform, family, version, nil
} }
func Users() ([]UserStat, error) { func Users() ([]UserStat, error) {
return UsersWithContext(context.Background())
}
func UsersWithContext(ctx context.Context) ([]UserStat, error) {
var ret []UserStat var ret []UserStat
return ret, nil return ret, nil
} }
func SensorsTemperatures() ([]TemperatureStat, error) {
return SensorsTemperaturesWithContext(context.Background())
}
func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) {
var ret []TemperatureStat
var dst []msAcpi_ThermalZoneTemperature
q := wmi.CreateQuery(&dst, "")
if err := common.WMIQueryWithContext(ctx, q, &dst, nil, "root/wmi"); err != nil {
return ret, err
}
for _, v := range dst {
ts := TemperatureStat{
SensorKey: v.InstanceName,
Temperature: kelvinToCelsius(v.CurrentTemperature, 2),
}
ret = append(ret, ts)
}
return ret, nil
}
func kelvinToCelsius(temp uint32, n int) float64 {
// wmi return temperature Kelvin * 10, so need to divide the result by 10,
// and then minus 273.15 to get °Celsius.
t := float64(temp/10) - 273.15
n10 := math.Pow10(n)
return math.Trunc((t+0.5/n10)*n10) / n10
}
func Virtualization() (string, string, error) {
return VirtualizationWithContext(context.Background())
}
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
return "", "", common.ErrNotImplementedError
}
func KernelVersion() (string, error) {
return KernelVersionWithContext(context.Background())
}
func KernelVersionWithContext(ctx context.Context) (string, error) {
_, _, version, err := PlatformInformation()
return version, err
}

View File

@ -1,17 +0,0 @@
// +build ignore
// plus hand editing about timeval
/*
Input to cgo -godefs.
*/
package host
/*
#include <sys/time.h>
#include <utmpx.h>
*/
import "C"
type Utmpx C.struct_utmpx
type Timeval C.struct_timeval

View File

@ -1,44 +0,0 @@
// +build ignore
/*
Input to cgo -godefs.
*/
package host
/*
#define KERNEL
#include <sys/types.h>
#include <sys/time.h>
#include <utmpx.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
sizeOfUtmpx = C.sizeof_struct_utmpx
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type Utmp C.struct_utmp
type Utmpx C.struct_utmpx
type Timeval C.struct_timeval

View File

@ -1,42 +0,0 @@
// +build ignore
/*
Input to cgo -godefs.
*/
package host
/*
#include <sys/types.h>
#include <utmp.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
sizeOfUtmp = C.sizeof_struct_utmp
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type utmp C.struct_utmp
type exit_status C.struct_exit_status
type timeval C.struct_timeval

View File

@ -1,43 +0,0 @@
// +build ignore
/*
Input to cgo -godefs.
*/
package host
/*
#define KERNEL
#include <sys/types.h>
#include <sys/time.h>
#include <utmp.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
sizeOfUtmp = C.sizeof_struct_utmp
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
type Utmp C.struct_utmp
type Timeval C.struct_timeval

View File

@ -9,9 +9,10 @@ package common
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"log"
"net/url" "net/url"
"os" "os"
"os/exec" "os/exec"
@ -26,24 +27,43 @@ import (
var ( var (
Timeout = 3 * time.Second Timeout = 3 * time.Second
ErrTimeout = errors.New("Command timed out.") ErrTimeout = errors.New("command timed out")
) )
type Invoker interface { type Invoker interface {
Command(string, ...string) ([]byte, error) Command(string, ...string) ([]byte, error)
CommandWithContext(context.Context, string, ...string) ([]byte, error)
} }
type Invoke struct{} type Invoke struct{}
func (i Invoke) Command(name string, arg ...string) ([]byte, error) { func (i Invoke) Command(name string, arg ...string) ([]byte, error) {
cmd := exec.Command(name, arg...) ctx, cancel := context.WithTimeout(context.Background(), Timeout)
return CombinedOutputTimeout(cmd, Timeout) defer cancel()
return i.CommandWithContext(ctx, name, arg...)
}
func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
cmd := exec.CommandContext(ctx, name, arg...)
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
if err := cmd.Start(); err != nil {
return buf.Bytes(), err
}
if err := cmd.Wait(); err != nil {
return buf.Bytes(), err
}
return buf.Bytes(), nil
} }
type FakeInvoke struct { type FakeInvoke struct {
CommandExpectedDir string // CommandExpectedDir specifies dir which includes expected outputs. Suffix string // Suffix species expected file name suffix such as "fail"
Suffix string // Suffix species expected file name suffix such as "fail" Error error // If Error specfied, return the error.
Error error // If Error specfied, return the error.
} }
// Command in FakeInvoke returns from expected file if exists. // Command in FakeInvoke returns from expected file if exists.
@ -54,22 +74,22 @@ func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) {
arch := runtime.GOOS arch := runtime.GOOS
fname := strings.Join(append([]string{name}, arg...), "") commandName := filepath.Base(name)
fname := strings.Join(append([]string{commandName}, arg...), "")
fname = url.QueryEscape(fname) fname = url.QueryEscape(fname)
var dir string fpath := path.Join("testdata", arch, fname)
if i.CommandExpectedDir == "" {
dir = "expected"
} else {
dir = i.CommandExpectedDir
}
fpath := path.Join(dir, arch, fname)
if i.Suffix != "" { if i.Suffix != "" {
fpath += "_" + i.Suffix fpath += "_" + i.Suffix
} }
if PathExists(fpath) { if PathExists(fpath) {
return ioutil.ReadFile(fpath) return ioutil.ReadFile(fpath)
} }
return exec.Command(name, arg...).Output() return []byte{}, fmt.Errorf("could not find testdata: %s", fpath)
}
func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
return i.Command(name, arg...)
} }
var ErrNotImplementedError = errors.New("not implemented yet") var ErrNotImplementedError = errors.New("not implemented yet")
@ -304,42 +324,12 @@ func HostEtc(combineWith ...string) string {
return GetEnv("HOST_ETC", "/etc", combineWith...) return GetEnv("HOST_ETC", "/etc", combineWith...)
} }
// CombinedOutputTimeout runs the given command with the given timeout and func HostVar(combineWith ...string) string {
// returns the combined output of stdout and stderr. return GetEnv("HOST_VAR", "/var", combineWith...)
// If the command times out, it attempts to kill the process.
// copied from https://github.com/influxdata/telegraf
func CombinedOutputTimeout(c *exec.Cmd, timeout time.Duration) ([]byte, error) {
var b bytes.Buffer
c.Stdout = &b
c.Stderr = &b
if err := c.Start(); err != nil {
return nil, err
}
err := WaitTimeout(c, timeout)
return b.Bytes(), err
} }
// WaitTimeout waits for the given command to finish with a timeout. func HostRun(combineWith ...string) string {
// It assumes the command has already been started. return GetEnv("HOST_RUN", "/run", combineWith...)
// If the command times out, it attempts to kill the process.
// copied from https://github.com/influxdata/telegraf
func WaitTimeout(c *exec.Cmd, timeout time.Duration) error {
timer := time.NewTimer(timeout)
done := make(chan error)
go func() { done <- c.Wait() }()
select {
case err := <-done:
timer.Stop()
return err
case <-timer.C:
if err := c.Process.Kill(); err != nil {
log.Printf("FATAL error killing process: %s", err)
return err
}
// wait for the command to return after killing it
<-done
return ErrTimeout
}
} }
// https://gist.github.com/kylelemons/1525278 // https://gist.github.com/kylelemons/1525278
@ -384,3 +374,19 @@ func Pipeline(cmds ...*exec.Cmd) ([]byte, []byte, error) {
// Return the pipeline output and the collected standard error // Return the pipeline output and the collected standard error
return output.Bytes(), stderr.Bytes(), nil return output.Bytes(), stderr.Bytes(), nil
} }
// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
// sysctl commands (see DoSysctrl).
func getSysctrlEnv(env []string) []string {
foundLC := false
for i, line := range env {
if strings.HasPrefix(line, "LC_ALL") {
env[i] = "LC_ALL=C"
foundLC = true
}
}
if !foundLC {
env = append(env, "LC_ALL=C")
}
return env
}

View File

@ -3,24 +3,23 @@
package common package common
import ( import (
"context"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
func DoSysctrl(mib string) ([]string, error) { func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) {
err := os.Setenv("LC_ALL", "C")
if err != nil {
return []string{}, err
}
sysctl, err := exec.LookPath("/usr/sbin/sysctl") sysctl, err := exec.LookPath("/usr/sbin/sysctl")
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
out, err := exec.Command(sysctl, "-n", mib).Output() cmd := exec.CommandContext(ctx, sysctl, "-n", mib)
cmd.Env = getSysctrlEnv(os.Environ())
out, err := cmd.Output()
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@ -36,8 +35,8 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
// get required buffer size // get required buffer size
length := uint64(0) length := uint64(0)
_, _, err := syscall.Syscall6( _, _, err := unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])), uintptr(unsafe.Pointer(&mib[0])),
uintptr(miblen), uintptr(miblen),
0, 0,
@ -54,8 +53,8 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
} }
// get proc info itself // get proc info itself
buf := make([]byte, length) buf := make([]byte, length)
_, _, err = syscall.Syscall6( _, _, err = unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])), uintptr(unsafe.Pointer(&mib[0])),
uintptr(miblen), uintptr(miblen),
uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&buf[0])),

View File

@ -6,20 +6,19 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
func DoSysctrl(mib string) ([]string, error) { func DoSysctrl(mib string) ([]string, error) {
err := os.Setenv("LC_ALL", "C")
if err != nil {
return []string{}, err
}
sysctl, err := exec.LookPath("/sbin/sysctl") sysctl, err := exec.LookPath("/sbin/sysctl")
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
out, err := exec.Command(sysctl, "-n", mib).Output() cmd := exec.Command(sysctl, "-n", mib)
cmd.Env = getSysctrlEnv(os.Environ())
out, err := cmd.Output()
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@ -36,8 +35,8 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
// get required buffer size // get required buffer size
length := uint64(0) length := uint64(0)
_, _, err := syscall.Syscall6( _, _, err := unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(mibptr), uintptr(mibptr),
uintptr(miblen), uintptr(miblen),
0, 0,
@ -54,8 +53,8 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
} }
// get proc info itself // get proc info itself
buf := make([]byte, length) buf := make([]byte, length)
_, _, err = syscall.Syscall6( _, _, err = unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(mibptr), uintptr(mibptr),
uintptr(miblen), uintptr(miblen),
uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&buf[0])),

View File

@ -9,15 +9,13 @@ import (
) )
func DoSysctrl(mib string) ([]string, error) { func DoSysctrl(mib string) ([]string, error) {
err := os.Setenv("LC_ALL", "C")
if err != nil {
return []string{}, err
}
sysctl, err := exec.LookPath("/sbin/sysctl") sysctl, err := exec.LookPath("/sbin/sysctl")
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
out, err := exec.Command(sysctl, "-n", mib).Output() cmd := exec.Command(sysctl, "-n", mib)
cmd.Env = getSysctrlEnv(os.Environ())
out, err := cmd.Output()
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@ -33,9 +31,9 @@ func NumProcs() (uint64, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
list, err := f.Readdir(-1)
defer f.Close() defer f.Close()
list, err := f.Readdirnames(-1)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -6,20 +6,19 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
func DoSysctrl(mib string) ([]string, error) { func DoSysctrl(mib string) ([]string, error) {
err := os.Setenv("LC_ALL", "C")
if err != nil {
return []string{}, err
}
sysctl, err := exec.LookPath("/sbin/sysctl") sysctl, err := exec.LookPath("/sbin/sysctl")
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
out, err := exec.Command(sysctl, "-n", mib).Output() cmd := exec.Command(sysctl, "-n", mib)
cmd.Env = getSysctrlEnv(os.Environ())
out, err := cmd.Output()
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@ -36,8 +35,8 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
// get required buffer size // get required buffer size
length := uint64(0) length := uint64(0)
_, _, err := syscall.Syscall6( _, _, err := unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(mibptr), uintptr(mibptr),
uintptr(miblen), uintptr(miblen),
0, 0,
@ -54,8 +53,8 @@ func CallSyscall(mib []int32) ([]byte, uint64, error) {
} }
// get proc info itself // get proc info itself
buf := make([]byte, length) buf := make([]byte, length)
_, _, err = syscall.Syscall6( _, _, err = unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(mibptr), uintptr(mibptr),
uintptr(miblen), uintptr(miblen),
uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&buf[0])),

View File

@ -3,12 +3,13 @@
package common package common
import ( import (
"context"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
) )
func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ...string) ([]string, error) {
var cmd []string var cmd []string
if pid == 0 { // will get from all processes. if pid == 0 { // will get from all processes.
cmd = []string{"-a", "-n", "-P"} cmd = []string{"-a", "-n", "-P"}
@ -20,7 +21,7 @@ func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) {
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
out, err := invoke.Command(lsof, cmd...) out, err := invoke.CommandWithContext(ctx, lsof, cmd...)
if err != nil { if err != nil {
// if no pid found, lsof returnes code 1. // if no pid found, lsof returnes code 1.
if err.Error() == "exit status 1" && len(out) == 0 { if err.Error() == "exit status 1" && len(out) == 0 {
@ -39,14 +40,14 @@ func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) {
return ret, nil return ret, nil
} }
func CallPgrep(invoke Invoker, pid int32) ([]int32, error) { func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
var cmd []string var cmd []string
cmd = []string{"-P", strconv.Itoa(int(pid))} cmd = []string{"-P", strconv.Itoa(int(pid))}
pgrep, err := exec.LookPath("pgrep") pgrep, err := exec.LookPath("pgrep")
if err != nil { if err != nil {
return []int32{}, err return []int32{}, err
} }
out, err := invoke.Command(pgrep, cmd...) out, err := invoke.CommandWithContext(ctx, pgrep, cmd...)
if err != nil { if err != nil {
return []int32{}, err return []int32{}, err
} }

View File

@ -3,8 +3,11 @@
package common package common
import ( import (
"syscall" "context"
"unsafe" "unsafe"
"github.com/StackExchange/wmi"
"golang.org/x/sys/windows"
) )
// for double values // for double values
@ -44,9 +47,10 @@ const (
) )
var ( var (
Modkernel32 = syscall.NewLazyDLL("kernel32.dll") Modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
ModNt = syscall.NewLazyDLL("ntdll.dll") ModNt = windows.NewLazySystemDLL("ntdll.dll")
ModPdh = syscall.NewLazyDLL("pdh.dll") ModPdh = windows.NewLazySystemDLL("pdh.dll")
ModPsapi = windows.NewLazySystemDLL("psapi.dll")
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes") ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation") ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
@ -77,13 +81,13 @@ func BytePtrToString(p *uint8) string {
type CounterInfo struct { type CounterInfo struct {
PostName string PostName string
CounterName string CounterName string
Counter syscall.Handle Counter windows.Handle
} }
// CreateQuery XXX // CreateQuery XXX
// copied from https://github.com/mackerelio/mackerel-agent/ // copied from https://github.com/mackerelio/mackerel-agent/
func CreateQuery() (syscall.Handle, error) { func CreateQuery() (windows.Handle, error) {
var query syscall.Handle var query windows.Handle
r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query))) r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
if r != 0 { if r != 0 {
return 0, err return 0, err
@ -92,11 +96,11 @@ func CreateQuery() (syscall.Handle, error) {
} }
// CreateCounter XXX // CreateCounter XXX
func CreateCounter(query syscall.Handle, pname, cname string) (*CounterInfo, error) { func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) {
var counter syscall.Handle var counter windows.Handle
r, _, err := PdhAddCounter.Call( r, _, err := PdhAddCounter.Call(
uintptr(query), uintptr(query),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(cname))), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))),
0, 0,
uintptr(unsafe.Pointer(&counter))) uintptr(unsafe.Pointer(&counter)))
if r != 0 { if r != 0 {
@ -108,3 +112,24 @@ func CreateCounter(query syscall.Handle, pname, cname string) (*CounterInfo, err
Counter: counter, Counter: counter,
}, nil }, nil
} }
// WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging
func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
if _, ok := ctx.Deadline(); !ok {
ctxTimeout, cancel := context.WithTimeout(ctx, Timeout)
defer cancel()
ctx = ctxTimeout
}
errChan := make(chan error, 1)
go func() {
errChan <- wmi.Query(query, dst, connectServerArgs...)
}()
select {
case <-ctx.Done():
return ctx.Err()
case err := <-errChan:
return err
}
}

View File

@ -6,11 +6,7 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var invoke common.Invoker var invoke common.Invoker = common.Invoke{}
func init() {
invoke = common.Invoke{}
}
// Memory usage statistics. Total, Available and Used contain numbers of bytes // Memory usage statistics. Total, Available and Used contain numbers of bytes
// for human consumption. // for human consumption.
@ -46,18 +42,38 @@ type VirtualMemoryStat struct {
Inactive uint64 `json:"inactive"` Inactive uint64 `json:"inactive"`
Wired uint64 `json:"wired"` Wired uint64 `json:"wired"`
// FreeBSD specific numbers:
// https://reviews.freebsd.org/D8467
Laundry uint64 `json:"laundry"`
// Linux specific numbers // Linux specific numbers
// https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html // https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html
// https://www.kernel.org/doc/Documentation/filesystems/proc.txt // https://www.kernel.org/doc/Documentation/filesystems/proc.txt
Buffers uint64 `json:"buffers"` // https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
Cached uint64 `json:"cached"` Buffers uint64 `json:"buffers"`
Writeback uint64 `json:"writeback"` Cached uint64 `json:"cached"`
Dirty uint64 `json:"dirty"` Writeback uint64 `json:"writeback"`
WritebackTmp uint64 `json:"writebacktmp"` Dirty uint64 `json:"dirty"`
Shared uint64 `json:"shared"` WritebackTmp uint64 `json:"writebacktmp"`
Slab uint64 `json:"slab"` Shared uint64 `json:"shared"`
PageTables uint64 `json:"pagetables"` Slab uint64 `json:"slab"`
SwapCached uint64 `json:"swapcached"` PageTables uint64 `json:"pagetables"`
SwapCached uint64 `json:"swapcached"`
CommitLimit uint64 `json:"commitlimit"`
CommittedAS uint64 `json:"committedas"`
HighTotal uint64 `json:"hightotal"`
HighFree uint64 `json:"highfree"`
LowTotal uint64 `json:"lowtotal"`
LowFree uint64 `json:"lowfree"`
SwapTotal uint64 `json:"swaptotal"`
SwapFree uint64 `json:"swapfree"`
Mapped uint64 `json:"mapped"`
VMallocTotal uint64 `json:"vmalloctotal"`
VMallocUsed uint64 `json:"vmallocused"`
VMallocChunk uint64 `json:"vmallocchunk"`
HugePagesTotal uint64 `json:"hugepagestotal"`
HugePagesFree uint64 `json:"hugepagesfree"`
HugePageSize uint64 `json:"hugepagesize"`
} }
type SwapMemoryStat struct { type SwapMemoryStat struct {

View File

@ -3,21 +3,22 @@
package mem package mem
import ( import (
"context"
"encoding/binary" "encoding/binary"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
) )
func getHwMemsize() (uint64, error) { func getHwMemsize() (uint64, error) {
totalString, err := syscall.Sysctl("hw.memsize") totalString, err := unix.Sysctl("hw.memsize")
if err != nil { if err != nil {
return 0, err return 0, err
} }
// syscall.sysctl() helpfully assumes the result is a null-terminated string and // unix.sysctl() helpfully assumes the result is a null-terminated string and
// removes the last byte of the result if it's 0 :/ // removes the last byte of the result if it's 0 :/
totalString += "\x00" totalString += "\x00"
@ -28,9 +29,13 @@ func getHwMemsize() (uint64, error) {
// SwapMemory returns swapinfo. // SwapMemory returns swapinfo.
func SwapMemory() (*SwapMemoryStat, error) { func SwapMemory() (*SwapMemoryStat, error) {
return SwapMemoryWithContext(context.Background())
}
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
var ret *SwapMemoryStat var ret *SwapMemoryStat
swapUsage, err := common.DoSysctrl("vm.swapusage") swapUsage, err := common.DoSysctrlWithContext(ctx, "vm.swapusage")
if err != nil { if err != nil {
return ret, err return ret, err
} }

View File

@ -9,13 +9,19 @@ package mem
import "C" import "C"
import ( import (
"context"
"fmt" "fmt"
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/unix"
) )
// VirtualMemory returns VirtualmemoryStat. // VirtualMemory returns VirtualmemoryStat.
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT) count := C.mach_msg_type_number_t(C.HOST_VM_INFO_COUNT)
var vmstat C.vm_statistics_data_t var vmstat C.vm_statistics_data_t
@ -28,7 +34,7 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
return nil, fmt.Errorf("host_statistics error=%d", status) return nil, fmt.Errorf("host_statistics error=%d", status)
} }
pageSize := uint64(syscall.Getpagesize()) pageSize := uint64(unix.Getpagesize())
total, err := getHwMemsize() total, err := getHwMemsize()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -4,10 +4,12 @@
package mem package mem
import ( import (
"context"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
"syscall"
"golang.org/x/sys/unix"
) )
// Runs vm_stat and returns Free and inactive pages // Runs vm_stat and returns Free and inactive pages
@ -27,7 +29,7 @@ func parseVMStat(out string, vms *VirtualMemoryStat) error {
var err error var err error
lines := strings.Split(out, "\n") lines := strings.Split(out, "\n")
pagesize := uint64(syscall.Getpagesize()) pagesize := uint64(unix.Getpagesize())
for _, line := range lines { for _, line := range lines {
fields := strings.Split(line, ":") fields := strings.Split(line, ":")
if len(fields) < 2 { if len(fields) < 2 {
@ -67,6 +69,10 @@ func parseVMStat(out string, vms *VirtualMemoryStat) error {
// VirtualMemory returns VirtualmemoryStat. // VirtualMemory returns VirtualmemoryStat.
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
ret := &VirtualMemoryStat{} ret := &VirtualMemoryStat{}
total, err := getHwMemsize() total, err := getHwMemsize()

View File

@ -1,13 +1,25 @@
// +build !darwin,!linux,!freebsd,!openbsd,!windows // +build !darwin,!linux,!freebsd,!openbsd,!solaris,!windows
package mem package mem
import "github.com/shirou/gopsutil/internal/common" import (
"context"
"github.com/shirou/gopsutil/internal/common"
)
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func SwapMemory() (*SwapMemoryStat, error) { func SwapMemory() (*SwapMemoryStat, error) {
return SwapMemoryWithContext(context.Background())
}
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }

View File

@ -3,82 +3,73 @@
package mem package mem
import ( import (
"context"
"errors" "errors"
"os/exec" "unsafe"
"strconv"
"strings"
"github.com/shirou/gopsutil/internal/common" "golang.org/x/sys/unix"
) )
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
pageSize, err := common.DoSysctrl("vm.stats.vm.v_page_size") return VirtualMemoryWithContext(context.Background())
if err != nil { }
return nil, err
}
p, err := strconv.ParseUint(pageSize[0], 10, 64)
if err != nil {
return nil, err
}
pageCount, err := common.DoSysctrl("vm.stats.vm.v_page_count") func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
pageSize, err := unix.SysctlUint32("vm.stats.vm.v_page_size")
if err != nil { if err != nil {
return nil, err return nil, err
} }
free, err := common.DoSysctrl("vm.stats.vm.v_free_count") physmem, err := unix.SysctlUint64("hw.physmem")
if err != nil { if err != nil {
return nil, err return nil, err
} }
active, err := common.DoSysctrl("vm.stats.vm.v_active_count") free, err := unix.SysctlUint32("vm.stats.vm.v_free_count")
if err != nil { if err != nil {
return nil, err return nil, err
} }
inactive, err := common.DoSysctrl("vm.stats.vm.v_inactive_count") active, err := unix.SysctlUint32("vm.stats.vm.v_active_count")
if err != nil { if err != nil {
return nil, err return nil, err
} }
cache, err := common.DoSysctrl("vm.stats.vm.v_cache_count") inactive, err := unix.SysctlUint32("vm.stats.vm.v_inactive_count")
if err != nil { if err != nil {
return nil, err return nil, err
} }
buffer, err := common.DoSysctrl("vfs.bufspace") buffers, err := unix.SysctlUint64("vfs.bufspace")
if err != nil { if err != nil {
return nil, err return nil, err
} }
wired, err := common.DoSysctrl("vm.stats.vm.v_wire_count") wired, err := unix.SysctlUint32("vm.stats.vm.v_wire_count")
if err != nil { if err != nil {
return nil, err return nil, err
} }
var cached, laundry uint32
parsed := make([]uint64, 0, 7) osreldate, _ := unix.SysctlUint32("kern.osreldate")
vv := []string{ if osreldate < 1102000 {
pageCount[0], cached, err = unix.SysctlUint32("vm.stats.vm.v_cache_count")
free[0], if err != nil {
active[0], return nil, err
inactive[0], }
cache[0], } else {
buffer[0], laundry, err = unix.SysctlUint32("vm.stats.vm.v_laundry_count")
wired[0],
}
for _, target := range vv {
t, err := strconv.ParseUint(target, 10, 64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
parsed = append(parsed, t)
} }
p := uint64(pageSize)
ret := &VirtualMemoryStat{ ret := &VirtualMemoryStat{
Total: parsed[0] * p, Total: uint64(physmem),
Free: parsed[1] * p, Free: uint64(free) * p,
Active: parsed[2] * p, Active: uint64(active) * p,
Inactive: parsed[3] * p, Inactive: uint64(inactive) * p,
Cached: parsed[4] * p, Cached: uint64(cached) * p,
Buffers: parsed[5], Buffers: uint64(buffers),
Wired: parsed[6] * p, Wired: uint64(wired) * p,
Laundry: uint64(laundry) * p,
} }
ret.Available = ret.Inactive + ret.Cached + ret.Free ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Laundry
ret.Used = ret.Total - ret.Available ret.Used = ret.Total - ret.Available
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
@ -86,49 +77,66 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
} }
// Return swapinfo // Return swapinfo
// FreeBSD can have multiple swap devices. but use only first device
func SwapMemory() (*SwapMemoryStat, error) { func SwapMemory() (*SwapMemoryStat, error) {
swapinfo, err := exec.LookPath("swapinfo") return SwapMemoryWithContext(context.Background())
if err != nil { }
return nil, err
} // Constants from vm/vm_param.h
// nolint: golint
out, err := invoke.Command(swapinfo) const (
if err != nil { XSWDEV_VERSION = 1
return nil, err )
}
for _, line := range strings.Split(string(out), "\n") { // Types from vm/vm_param.h
values := strings.Fields(line) type xswdev struct {
// skip title line Version uint32 // Version is the version
if len(values) == 0 || values[0] == "Device" { Dev uint32 // Dev is the device identifier
continue Flags int32 // Flags is the swap flags applied to the device
} NBlks int32 // NBlks is the total number of blocks
Used int32 // Used is the number of blocks used
u := strings.Replace(values[4], "%", "", 1) }
total_v, err := strconv.ParseUint(values[1], 10, 64)
if err != nil { func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
return nil, err // FreeBSD can have multiple swap devices so we total them up
} i, err := unix.SysctlUint32("vm.nswapdev")
used_v, err := strconv.ParseUint(values[2], 10, 64) if err != nil {
if err != nil { return nil, err
return nil, err }
}
free_v, err := strconv.ParseUint(values[3], 10, 64) if i == 0 {
if err != nil { return nil, errors.New("no swap devices found")
return nil, err }
}
up_v, err := strconv.ParseFloat(u, 64) c := int(i)
if err != nil {
return nil, err i, err = unix.SysctlUint32("vm.stats.vm.v_page_size")
} if err != nil {
return nil, err
return &SwapMemoryStat{ }
Total: total_v, pageSize := uint64(i)
Used: used_v,
Free: free_v, var buf []byte
UsedPercent: up_v, s := &SwapMemoryStat{}
}, nil for n := 0; n < c; n++ {
} buf, err = unix.SysctlRaw("vm.swap_info", n)
if err != nil {
return nil, errors.New("no swap devices found") return nil, err
}
xsw := (*xswdev)(unsafe.Pointer(&buf[0]))
if xsw.Version != XSWDEV_VERSION {
return nil, errors.New("xswdev version mismatch")
}
s.Total += uint64(xsw.NBlks)
s.Used += uint64(xsw.Used)
}
if s.Total != 0 {
s.UsedPercent = float64(s.Used) / float64(s.Total) * 100
}
s.Total *= pageSize
s.Used *= pageSize
s.Free = s.Total - s.Used
return s, nil
} }

View File

@ -3,14 +3,19 @@
package mem package mem
import ( import (
"context"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/unix"
) )
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
filename := common.HostProc("meminfo") filename := common.HostProc("meminfo")
lines, _ := common.ReadLines(filename) lines, _ := common.ReadLines(filename)
// flag if MemAvailable is in /proc/meminfo (kernel 3.14+) // flag if MemAvailable is in /proc/meminfo (kernel 3.14+)
@ -60,21 +65,55 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
ret.PageTables = t * 1024 ret.PageTables = t * 1024
case "SwapCached": case "SwapCached":
ret.SwapCached = t * 1024 ret.SwapCached = t * 1024
case "CommitLimit":
ret.CommitLimit = t * 1024
case "Committed_AS":
ret.CommittedAS = t * 1024
case "HighTotal":
ret.HighTotal = t * 1024
case "HighFree":
ret.HighFree = t * 1024
case "LowTotal":
ret.LowTotal = t * 1024
case "LowFree":
ret.LowFree = t * 1024
case "SwapTotal":
ret.SwapTotal = t * 1024
case "SwapFree":
ret.SwapFree = t * 1024
case "Mapped":
ret.Mapped = t * 1024
case "VmallocTotal":
ret.VMallocTotal = t * 1024
case "VmallocUsed":
ret.VMallocUsed = t * 1024
case "VmallocChunk":
ret.VMallocChunk = t * 1024
case "HugePages_Total":
ret.HugePagesTotal = t
case "HugePages_Free":
ret.HugePagesFree = t
case "Hugepagesize":
ret.HugePageSize = t * 1024
} }
} }
if !memavail { if !memavail {
ret.Available = ret.Free + ret.Buffers + ret.Cached ret.Available = ret.Free + ret.Buffers + ret.Cached
} }
ret.Used = ret.Total - ret.Available ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached
ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
return ret, nil return ret, nil
} }
func SwapMemory() (*SwapMemoryStat, error) { func SwapMemory() (*SwapMemoryStat, error) {
sysinfo := &syscall.Sysinfo_t{} return SwapMemoryWithContext(context.Background())
}
if err := syscall.Sysinfo(sysinfo); err != nil { func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
sysinfo := &unix.Sysinfo_t{}
if err := unix.Sysinfo(sysinfo); err != nil {
return nil, err return nil, err
} }
ret := &SwapMemoryStat{ ret := &SwapMemoryStat{

View File

@ -4,14 +4,20 @@ package mem
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"github.com/shirou/gopsutil/internal/common"
"os/exec" "os/exec"
"github.com/shirou/gopsutil/internal/common"
) )
func GetPageSize() (uint64, error) { func GetPageSize() (uint64, error) {
return GetPageSizeWithContext(context.Background())
}
func GetPageSizeWithContext(ctx context.Context) (uint64, error) {
mib := []int32{CTLVm, VmUvmexp} mib := []int32{CTLVm, VmUvmexp}
buf, length, err := common.CallSyscall(mib) buf, length, err := common.CallSyscall(mib)
if err != nil { if err != nil {
@ -30,6 +36,10 @@ func GetPageSize() (uint64, error) {
} }
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
mib := []int32{CTLVm, VmUvmexp} mib := []int32{CTLVm, VmUvmexp}
buf, length, err := common.CallSyscall(mib) buf, length, err := common.CallSyscall(mib)
if err != nil { if err != nil {
@ -80,12 +90,16 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
// Return swapctl summary info // Return swapctl summary info
func SwapMemory() (*SwapMemoryStat, error) { func SwapMemory() (*SwapMemoryStat, error) {
return SwapMemoryWithContext(context.Background())
}
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
swapctl, err := exec.LookPath("swapctl") swapctl, err := exec.LookPath("swapctl")
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := invoke.Command(swapctl, "-sk") out, err := invoke.CommandWithContext(ctx, swapctl, "-sk")
if err != nil { if err != nil {
return &SwapMemoryStat{}, nil return &SwapMemoryStat{}, nil
} }

121
vendor/github.com/shirou/gopsutil/mem/mem_solaris.go generated vendored Normal file
View File

@ -0,0 +1,121 @@
package mem
import (
"context"
"errors"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/shirou/gopsutil/internal/common"
)
// VirtualMemory for Solaris is a minimal implementation which only returns
// what Nomad needs. It does take into account global vs zone, however.
func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
result := &VirtualMemoryStat{}
zoneName, err := zoneName()
if err != nil {
return nil, err
}
if zoneName == "global" {
cap, err := globalZoneMemoryCapacity()
if err != nil {
return nil, err
}
result.Total = cap
} else {
cap, err := nonGlobalZoneMemoryCapacity()
if err != nil {
return nil, err
}
result.Total = cap
}
return result, nil
}
func SwapMemory() (*SwapMemoryStat, error) {
return SwapMemoryWithContext(context.Background())
}
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
return nil, common.ErrNotImplementedError
}
func zoneName() (string, error) {
zonename, err := exec.LookPath("/usr/bin/zonename")
if err != nil {
return "", err
}
ctx := context.Background()
out, err := invoke.CommandWithContext(ctx, zonename)
if err != nil {
return "", err
}
return strings.TrimSpace(string(out)), nil
}
var globalZoneMemoryCapacityMatch = regexp.MustCompile(`memory size: ([\d]+) Megabytes`)
func globalZoneMemoryCapacity() (uint64, error) {
prtconf, err := exec.LookPath("/usr/sbin/prtconf")
if err != nil {
return 0, err
}
ctx := context.Background()
out, err := invoke.CommandWithContext(ctx, prtconf)
if err != nil {
return 0, err
}
match := globalZoneMemoryCapacityMatch.FindAllStringSubmatch(string(out), -1)
if len(match) != 1 {
return 0, errors.New("memory size not contained in output of /usr/sbin/prtconf")
}
totalMB, err := strconv.ParseUint(match[0][1], 10, 64)
if err != nil {
return 0, err
}
return totalMB * 1024 * 1024, nil
}
var kstatMatch = regexp.MustCompile(`([^\s]+)[\s]+([^\s]*)`)
func nonGlobalZoneMemoryCapacity() (uint64, error) {
kstat, err := exec.LookPath("/usr/bin/kstat")
if err != nil {
return 0, err
}
ctx := context.Background()
out, err := invoke.CommandWithContext(ctx, kstat, "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap")
if err != nil {
return 0, err
}
kstats := kstatMatch.FindAllStringSubmatch(string(out), -1)
if len(kstats) != 1 {
return 0, fmt.Errorf("expected 1 kstat, found %d", len(kstats))
}
memSizeBytes, err := strconv.ParseUint(kstats[0][2], 10, 64)
if err != nil {
return 0, err
}
return memSizeBytes, nil
}

View File

@ -3,14 +3,16 @@
package mem package mem
import ( import (
"syscall" "context"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows"
) )
var ( var (
procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx") procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx")
procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo")
) )
type memoryStatusEx struct { type memoryStatusEx struct {
@ -26,11 +28,15 @@ type memoryStatusEx struct {
} }
func VirtualMemory() (*VirtualMemoryStat, error) { func VirtualMemory() (*VirtualMemoryStat, error) {
return VirtualMemoryWithContext(context.Background())
}
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
var memInfo memoryStatusEx var memInfo memoryStatusEx
memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) memInfo.cbSize = uint32(unsafe.Sizeof(memInfo))
mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo)))
if mem == 0 { if mem == 0 {
return nil, syscall.GetLastError() return nil, windows.GetLastError()
} }
ret := &VirtualMemoryStat{ ret := &VirtualMemoryStat{
@ -43,8 +49,49 @@ func VirtualMemory() (*VirtualMemoryStat, error) {
return ret, nil return ret, nil
} }
type performanceInformation struct {
cb uint32
commitTotal uint64
commitLimit uint64
commitPeak uint64
physicalTotal uint64
physicalAvailable uint64
systemCache uint64
kernelTotal uint64
kernelPaged uint64
kernelNonpaged uint64
pageSize uint64
handleCount uint32
processCount uint32
threadCount uint32
}
func SwapMemory() (*SwapMemoryStat, error) { func SwapMemory() (*SwapMemoryStat, error) {
ret := &SwapMemoryStat{} return SwapMemoryWithContext(context.Background())
}
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
var perfInfo performanceInformation
perfInfo.cb = uint32(unsafe.Sizeof(perfInfo))
mem, _, _ := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&perfInfo)), uintptr(perfInfo.cb))
if mem == 0 {
return nil, windows.GetLastError()
}
tot := perfInfo.commitLimit * perfInfo.pageSize
used := perfInfo.commitTotal * perfInfo.pageSize
free := tot - used
var usedPercent float64
if tot == 0 {
usedPercent = 0
} else {
usedPercent = float64(used) / float64(tot)
}
ret := &SwapMemoryStat{
Total: tot,
Used: used,
Free: free,
UsedPercent: usedPercent,
}
return ret, nil return ret, nil
} }

View File

@ -1,34 +0,0 @@
// +build ignore
/*
Input to cgo -godefs.
*/
package mem
/*
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#include <uvm/uvmexp.h>
*/
import "C"
// Machine characteristics; for internal use.
const (
CTLVm = 2
CTLVfs = 10
VmUvmexp = 4 // get uvmexp
VfsGeneric = 0
VfsBcacheStat = 3
)
const (
sizeOfUvmexp = C.sizeof_struct_uvmexp
sizeOfBcachestats = C.sizeof_struct_bcachestats
)
type Uvmexp C.struct_uvmexp
type Bcachestats C.struct_bcachestats

View File

@ -1,6 +1,7 @@
package net package net
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
@ -11,11 +12,7 @@ import (
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var invoke common.Invoker var invoke common.Invoker = common.Invoke{}
func init() {
invoke = common.Invoke{}
}
type IOCountersStat struct { type IOCountersStat struct {
Name string `json:"name"` // interface name Name string `json:"name"` // interface name
@ -74,6 +71,7 @@ type FilterStat struct {
} }
var constMap = map[string]int{ var constMap = map[string]int{
"unix": syscall.AF_UNIX,
"TCP": syscall.SOCK_STREAM, "TCP": syscall.SOCK_STREAM,
"UDP": syscall.SOCK_DGRAM, "UDP": syscall.SOCK_DGRAM,
"IPv4": syscall.AF_INET, "IPv4": syscall.AF_INET,
@ -111,6 +109,10 @@ func (n InterfaceAddr) String() string {
} }
func Interfaces() ([]InterfaceStat, error) { func Interfaces() ([]InterfaceStat, error) {
return InterfacesWithContext(context.Background())
}
func InterfacesWithContext(ctx context.Context) ([]InterfaceStat, error) {
is, err := net.Interfaces() is, err := net.Interfaces()
if err != nil { if err != nil {
return nil, err return nil, err
@ -177,10 +179,15 @@ func getIOCountersAll(n []IOCountersStat) ([]IOCountersStat, error) {
func parseNetLine(line string) (ConnectionStat, error) { func parseNetLine(line string) (ConnectionStat, error) {
f := strings.Fields(line) f := strings.Fields(line)
if len(f) < 9 { if len(f) < 8 {
return ConnectionStat{}, fmt.Errorf("wrong line,%s", line) return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
} }
if len(f) == 8 {
f = append(f, f[7])
f[7] = "unix"
}
pid, err := strconv.Atoi(f[1]) pid, err := strconv.Atoi(f[1])
if err != nil { if err != nil {
return ConnectionStat{}, err return ConnectionStat{}, err
@ -198,9 +205,14 @@ func parseNetLine(line string) (ConnectionStat, error) {
return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7]) return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7])
} }
laddr, raddr, err := parseNetAddr(f[8]) var laddr, raddr Addr
if err != nil { if f[7] == "unix" {
return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8]) laddr.IP = f[8]
} else {
laddr, raddr, err = parseNetAddr(f[8])
if err != nil {
return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s", f[8])
}
} }
n := ConnectionStat{ n := ConnectionStat{

View File

@ -3,6 +3,7 @@
package net package net
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os/exec" "os/exec"
@ -18,7 +19,7 @@ var (
const endOfLine = "\n" const endOfLine = "\n"
func parseNetstatLine(line string) (stat *IOCountersStat, linkId *uint, err error) { func parseNetstatLine(line string) (stat *IOCountersStat, linkID *uint, err error) {
var ( var (
numericValue uint64 numericValue uint64
columns = strings.Fields(line) columns = strings.Fields(line)
@ -35,8 +36,8 @@ func parseNetstatLine(line string) (stat *IOCountersStat, linkId *uint, err erro
if err != nil { if err != nil {
return return
} }
linkIdUint := uint(numericValue) linkIDUint := uint(numericValue)
linkId = &linkIdUint linkID = &linkIDUint
} }
base := 1 base := 1
@ -91,7 +92,7 @@ func parseNetstatLine(line string) (stat *IOCountersStat, linkId *uint, err erro
} }
type netstatInterface struct { type netstatInterface struct {
linkId *uint linkID *uint
stat *IOCountersStat stat *IOCountersStat
} }
@ -112,7 +113,7 @@ func parseNetstatOutput(output string) ([]netstatInterface, error) {
for index := 0; index < numberInterfaces; index++ { for index := 0; index < numberInterfaces; index++ {
nsIface := netstatInterface{} nsIface := netstatInterface{}
if nsIface.stat, nsIface.linkId, err = parseNetstatLine(lines[index+1]); err != nil { if nsIface.stat, nsIface.linkID, err = parseNetstatLine(lines[index+1]); err != nil {
return nil, err return nil, err
} }
interfaces[index] = nsIface interfaces[index] = nsIface
@ -126,7 +127,7 @@ type mapInterfaceNameUsage map[string]uint
func newMapInterfaceNameUsage(ifaces []netstatInterface) mapInterfaceNameUsage { func newMapInterfaceNameUsage(ifaces []netstatInterface) mapInterfaceNameUsage {
output := make(mapInterfaceNameUsage) output := make(mapInterfaceNameUsage)
for index := range ifaces { for index := range ifaces {
if ifaces[index].linkId != nil { if ifaces[index].linkID != nil {
ifaceName := ifaces[index].stat.Name ifaceName := ifaces[index].stat.Name
usage, ok := output[ifaceName] usage, ok := output[ifaceName]
if ok { if ok {
@ -164,6 +165,10 @@ func (min mapInterfaceNameUsage) notTruncated() []string {
// lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - - // lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - -
// lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - - // lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - -
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), pernic)
}
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
var ( var (
ret []IOCountersStat ret []IOCountersStat
retIndex int retIndex int
@ -175,7 +180,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
} }
// try to get all interface metrics, and hope there won't be any truncated // try to get all interface metrics, and hope there won't be any truncated
out, err := invoke.Command(netstat, "-ibdnW") out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -192,7 +197,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
if !ifaceUsage.isTruncated() { if !ifaceUsage.isTruncated() {
// no truncated interface name, return stats of all interface with <Link#...> // no truncated interface name, return stats of all interface with <Link#...>
for index := range nsInterfaces { for index := range nsInterfaces {
if nsInterfaces[index].linkId != nil { if nsInterfaces[index].linkID != nil {
ret[retIndex] = *nsInterfaces[index].stat ret[retIndex] = *nsInterfaces[index].stat
retIndex++ retIndex++
} }
@ -203,7 +208,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if out, err = invoke.Command(ifconfig, "-l"); err != nil { if out, err = invoke.CommandWithContext(ctx, ifconfig, "-l"); err != nil {
return nil, err return nil, err
} }
interfaceNames := strings.Fields(strings.TrimRight(string(out), endOfLine)) interfaceNames := strings.Fields(strings.TrimRight(string(out), endOfLine))
@ -212,7 +217,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
for _, interfaceName := range interfaceNames { for _, interfaceName := range interfaceNames {
truncated := true truncated := true
for index := range nsInterfaces { for index := range nsInterfaces {
if nsInterfaces[index].linkId != nil && nsInterfaces[index].stat.Name == interfaceName { if nsInterfaces[index].linkID != nil && nsInterfaces[index].stat.Name == interfaceName {
// handle the non truncated name to avoid execute netstat for them again // handle the non truncated name to avoid execute netstat for them again
ret[retIndex] = *nsInterfaces[index].stat ret[retIndex] = *nsInterfaces[index].stat
retIndex++ retIndex++
@ -222,7 +227,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
} }
if truncated { if truncated {
// run netstat with -I$ifacename // run netstat with -I$ifacename
if out, err = invoke.Command(netstat, "-ibdnWI"+interfaceName); err != nil { if out, err = invoke.CommandWithContext(ctx, netstat, "-ibdnWI"+interfaceName); err != nil {
return nil, err return nil, err
} }
parsedIfaces, err := parseNetstatOutput(string(out)) parsedIfaces, err := parseNetstatOutput(string(out))
@ -234,7 +239,7 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
continue continue
} }
for index := range parsedIfaces { for index := range parsedIfaces {
if parsedIfaces[index].linkId != nil { if parsedIfaces[index].linkID != nil {
ret = append(ret, *parsedIfaces[index].stat) ret = append(ret, *parsedIfaces[index].stat)
break break
} }
@ -251,10 +256,18 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
// NetIOCountersByFile is an method which is added just a compatibility for linux. // NetIOCountersByFile is an method which is added just a compatibility for linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
return IOCounters(pernic) return IOCounters(pernic)
} }
func FilterCounters() ([]FilterStat, error) { func FilterCounters() ([]FilterStat, error) {
return FilterCountersWithContext(context.Background())
}
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")
} }
@ -263,5 +276,9 @@ func FilterCounters() ([]FilterStat, error) {
// just the protocols in the list are returned. // just the protocols in the list are returned.
// Not Implemented for Darwin // Not Implemented for Darwin
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for darwin") return nil, errors.New("NetProtoCounters not implemented for darwin")
} }

View File

@ -2,24 +2,48 @@
package net package net
import "github.com/shirou/gopsutil/internal/common" import (
"context"
"github.com/shirou/gopsutil/internal/common"
)
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), pernic)
}
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
return []IOCountersStat{}, common.ErrNotImplementedError return []IOCountersStat{}, common.ErrNotImplementedError
} }
func FilterCounters() ([]FilterStat, error) { func FilterCounters() ([]FilterStat, error) {
return FilterCountersWithContext(context.Background())
}
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
return []FilterStat{}, common.ErrNotImplementedError return []FilterStat{}, common.ErrNotImplementedError
} }
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
return []ProtoCountersStat{}, common.ErrNotImplementedError return []ProtoCountersStat{}, common.ErrNotImplementedError
} }
func Connections(kind string) ([]ConnectionStat, error) { func Connections(kind string) ([]ConnectionStat, error) {
return ConnectionsWithContext(context.Background(), kind)
}
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
return []ConnectionStat{}, common.ErrNotImplementedError return []ConnectionStat{}, common.ErrNotImplementedError
} }
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
return ConnectionsMaxWithContext(context.Background(), kind, max)
}
func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
return []ConnectionStat{}, common.ErrNotImplementedError return []ConnectionStat{}, common.ErrNotImplementedError
} }

View File

@ -3,6 +3,7 @@
package net package net
import ( import (
"context"
"errors" "errors"
"os/exec" "os/exec"
"strconv" "strconv"
@ -12,11 +13,15 @@ import (
) )
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), pernic)
}
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
netstat, err := exec.LookPath("/usr/bin/netstat") netstat, err := exec.LookPath("/usr/bin/netstat")
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := invoke.Command(netstat, "-ibdnW") out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -92,10 +97,18 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
// NetIOCountersByFile is an method which is added just a compatibility for linux. // NetIOCountersByFile is an method which is added just a compatibility for linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
return IOCounters(pernic) return IOCounters(pernic)
} }
func FilterCounters() ([]FilterStat, error) { func FilterCounters() ([]FilterStat, error) {
return FilterCountersWithContext(context.Background())
}
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")
} }
@ -104,5 +117,9 @@ func FilterCounters() ([]FilterStat, error) {
// just the protocols in the list are returned. // just the protocols in the list are returned.
// Not Implemented for FreeBSD // Not Implemented for FreeBSD
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for freebsd") return nil, errors.New("NetProtoCounters not implemented for freebsd")
} }

View File

@ -3,6 +3,8 @@
package net package net
import ( import (
"bytes"
"context"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
@ -22,25 +24,38 @@ import (
// every network interface installed on the system is returned // every network interface installed on the system is returned
// separately. // separately.
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), pernic)
}
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
filename := common.HostProc("net/dev") filename := common.HostProc("net/dev")
return IOCountersByFile(pernic, filename) return IOCountersByFile(pernic, filename)
} }
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
lines, err := common.ReadLines(filename) lines, err := common.ReadLines(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
parts := make([]string, 2)
statlen := len(lines) - 1 statlen := len(lines) - 1
ret := make([]IOCountersStat, 0, statlen) ret := make([]IOCountersStat, 0, statlen)
for _, line := range lines[2:] { for _, line := range lines[2:] {
parts := strings.SplitN(line, ":", 2) separatorPos := strings.LastIndex(line, ":")
if len(parts) != 2 { if separatorPos == -1 {
continue continue
} }
parts[0] = line[0:separatorPos]
parts[1] = line[separatorPos+1:]
interfaceName := strings.TrimSpace(parts[0]) interfaceName := strings.TrimSpace(parts[0])
if interfaceName == "" { if interfaceName == "" {
continue continue
@ -126,6 +141,10 @@ var netProtocols = []string{
// Available protocols: // Available protocols:
// ip,icmp,icmpmsg,tcp,udp,udplite // ip,icmp,icmpmsg,tcp,udp,udplite
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
if len(protocols) == 0 { if len(protocols) == 0 {
protocols = netProtocols protocols = netProtocols
} }
@ -185,6 +204,10 @@ func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
// the currently in use conntrack count and the max. // the currently in use conntrack count and the max.
// If the file does not exist or is invalid it will return nil. // If the file does not exist or is invalid it will return nil.
func FilterCounters() ([]FilterStat, error) { func FilterCounters() ([]FilterStat, error) {
return FilterCountersWithContext(context.Background())
}
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count") countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count")
maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max") maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max")
@ -256,17 +279,17 @@ var kindUNIX = netConnectionKindType{
} }
var netConnectionKindMap = map[string][]netConnectionKindType{ var netConnectionKindMap = map[string][]netConnectionKindType{
"all": []netConnectionKindType{kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX}, "all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX},
"tcp": []netConnectionKindType{kindTCP4, kindTCP6}, "tcp": {kindTCP4, kindTCP6},
"tcp4": []netConnectionKindType{kindTCP4}, "tcp4": {kindTCP4},
"tcp6": []netConnectionKindType{kindTCP6}, "tcp6": {kindTCP6},
"udp": []netConnectionKindType{kindUDP4, kindUDP6}, "udp": {kindUDP4, kindUDP6},
"udp4": []netConnectionKindType{kindUDP4}, "udp4": {kindUDP4},
"udp6": []netConnectionKindType{kindUDP6}, "udp6": {kindUDP6},
"unix": []netConnectionKindType{kindUNIX}, "unix": {kindUNIX},
"inet": []netConnectionKindType{kindTCP4, kindTCP6, kindUDP4, kindUDP6}, "inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
"inet4": []netConnectionKindType{kindTCP4, kindUDP4}, "inet4": {kindTCP4, kindUDP4},
"inet6": []netConnectionKindType{kindTCP6, kindUDP6}, "inet6": {kindTCP6, kindUDP6},
} }
type inodeMap struct { type inodeMap struct {
@ -288,17 +311,29 @@ type connTmp struct {
// Return a list of network connections opened. // Return a list of network connections opened.
func Connections(kind string) ([]ConnectionStat, error) { func Connections(kind string) ([]ConnectionStat, error) {
return ConnectionsWithContext(context.Background(), kind)
}
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
return ConnectionsPid(kind, 0) return ConnectionsPid(kind, 0)
} }
// Return a list of network connections opened returning at most `max` // Return a list of network connections opened returning at most `max`
// connections for each running process. // connections for each running process.
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
return ConnectionsMaxWithContext(context.Background(), kind, max)
}
func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
return ConnectionsPidMax(kind, 0, max) return ConnectionsPidMax(kind, 0, max)
} }
// Return a list of network connections opened by a process. // Return a list of network connections opened by a process.
func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
return ConnectionsPidWithContext(context.Background(), kind, pid)
}
func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind] tmap, ok := netConnectionKindMap[kind]
if !ok { if !ok {
return nil, fmt.Errorf("invalid kind, %s", kind) return nil, fmt.Errorf("invalid kind, %s", kind)
@ -316,13 +351,17 @@ func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
} }
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("cound not get pid(s), %d", pid) return nil, fmt.Errorf("cound not get pid(s), %d: %s", pid, err)
} }
return statsFromInodes(root, pid, tmap, inodes) return statsFromInodes(root, pid, tmap, inodes)
} }
// Return up to `max` network connections opened by a process. // Return up to `max` network connections opened by a process.
func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
}
func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind] tmap, ok := netConnectionKindMap[kind]
if !ok { if !ok {
return nil, fmt.Errorf("invalid kind, %s", kind) return nil, fmt.Errorf("invalid kind, %s", kind)
@ -346,18 +385,17 @@ func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error
} }
func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap) ([]ConnectionStat, error) { func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap) ([]ConnectionStat, error) {
dupCheckMap := make(map[connTmp]struct{}) dupCheckMap := make(map[string]struct{})
var ret []ConnectionStat var ret []ConnectionStat
var err error var err error
for _, t := range tmap { for _, t := range tmap {
var path string var path string
var connKey string
var ls []connTmp var ls []connTmp
path = fmt.Sprintf("%s/net/%s", root, t.filename) path = fmt.Sprintf("%s/net/%s", root, t.filename)
switch t.family { switch t.family {
case syscall.AF_INET: case syscall.AF_INET, syscall.AF_INET6:
fallthrough
case syscall.AF_INET6:
ls, err = processInet(path, t, inodes, pid) ls, err = processInet(path, t, inodes, pid)
case syscall.AF_UNIX: case syscall.AF_UNIX:
ls, err = processUnix(path, t, inodes, pid) ls, err = processUnix(path, t, inodes, pid)
@ -366,7 +404,11 @@ func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inode
return nil, err return nil, err
} }
for _, c := range ls { for _, c := range ls {
if _, ok := dupCheckMap[c]; ok { // Build TCP key to id the connection uniquely
// socket type, src ip, src port, dst ip, dst port and state should be enough
// to prevent duplications.
connKey = fmt.Sprintf("%d-%s:%d-%s:%d-%s", c.sockType, c.laddr.IP, c.laddr.Port, c.raddr.IP, c.raddr.Port, c.status)
if _, ok := dupCheckMap[connKey]; ok {
continue continue
} }
@ -390,7 +432,7 @@ func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inode
conn.Uids, _ = proc.getUids() conn.Uids, _ = proc.getUids()
ret = append(ret, conn) ret = append(ret, conn)
dupCheckMap[c] = struct{}{} dupCheckMap[connKey] = struct{}{}
} }
} }
@ -405,11 +447,12 @@ func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, erro
dir := fmt.Sprintf("%s/%d/fd", root, pid) dir := fmt.Sprintf("%s/%d/fd", root, pid)
f, err := os.Open(dir) f, err := os.Open(dir)
if err != nil { if err != nil {
return ret, nil return ret, err
} }
defer f.Close()
files, err := f.Readdir(max) files, err := f.Readdir(max)
if err != nil { if err != nil {
return ret, nil return ret, err
} }
for _, fd := range files { for _, fd := range files {
inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name()) inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
@ -447,6 +490,10 @@ func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, erro
// FIXME: Import process occures import cycle. // FIXME: Import process occures import cycle.
// move to common made other platform breaking. Need consider. // move to common made other platform breaking. Need consider.
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32 var ret []int32
d, err := os.Open(common.HostProc()) d, err := os.Open(common.HostProc())
@ -529,6 +576,10 @@ func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
for _, pid := range pids { for _, pid := range pids {
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
if os.IsPermission(err) || os.IsNotExist(err) {
continue
}
return ret, err return ret, err
} }
if len(t) == 0 { if len(t) == 0 {
@ -576,6 +627,10 @@ func decodeAddress(family uint32, src string) (Addr, error) {
// Reverse reverses array of bytes. // Reverse reverses array of bytes.
func Reverse(s []byte) []byte { func Reverse(s []byte) []byte {
return ReverseWithContext(context.Background(), s)
}
func ReverseWithContext(ctx context.Context, s []byte) []byte {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i] s[i], s[j] = s[j], s[i]
} }
@ -602,14 +657,22 @@ func processInet(file string, kind netConnectionKindType, inodes map[string][]in
// IPv6 not supported, return empty. // IPv6 not supported, return empty.
return []connTmp{}, nil return []connTmp{}, nil
} }
lines, err := common.ReadLines(file)
// Read the contents of the /proc file with a single read sys call.
// This minimizes duplicates in the returned connections
// For more info:
// https://github.com/shirou/gopsutil/pull/361
contents, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
lines := bytes.Split(contents, []byte("\n"))
var ret []connTmp var ret []connTmp
// skip first line // skip first line
for _, line := range lines[1:] { for _, line := range lines[1:] {
l := strings.Fields(line) l := strings.Fields(string(line))
if len(l) < 10 { if len(l) < 10 {
continue continue
} }
@ -656,15 +719,21 @@ func processInet(file string, kind netConnectionKindType, inodes map[string][]in
} }
func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) { func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
lines, err := common.ReadLines(file) // Read the contents of the /proc file with a single read sys call.
// This minimizes duplicates in the returned connections
// For more info:
// https://github.com/shirou/gopsutil/pull/361
contents, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
lines := bytes.Split(contents, []byte("\n"))
var ret []connTmp var ret []connTmp
// skip first line // skip first line
for _, line := range lines[1:] { for _, line := range lines[1:] {
tokens := strings.Fields(line) tokens := strings.Fields(string(line))
if len(tokens) < 6 { if len(tokens) < 6 {
continue continue
} }
@ -679,7 +748,7 @@ func processUnix(file string, kind netConnectionKindType, inodes map[string][]in
pairs, exists := inodes[inode] pairs, exists := inodes[inode]
if !exists { if !exists {
pairs = []inodeMap{ pairs = []inodeMap{
inodeMap{}, {},
} }
} }
for _, pair := range pairs { for _, pair := range pairs {

View File

@ -3,14 +3,20 @@
package net package net
import ( import (
"context"
"errors" "errors"
"fmt"
"os/exec" "os/exec"
"regexp"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
) )
var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
func ParseNetstat(output string, mode string, func ParseNetstat(output string, mode string,
iocs map[string]IOCountersStat) error { iocs map[string]IOCountersStat) error {
lines := strings.Split(output, "\n") lines := strings.Split(output, "\n")
@ -92,15 +98,19 @@ func ParseNetstat(output string, mode string,
} }
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
netstat, err := exec.LookPath("/usr/bin/netstat") return IOCountersWithContext(context.Background(), pernic)
}
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
netstat, err := exec.LookPath("netstat")
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := invoke.Command(netstat, "-inb") out, err := invoke.CommandWithContext(ctx, netstat, "-inb")
if err != nil { if err != nil {
return nil, err return nil, err
} }
out2, err := invoke.Command(netstat, "-ind") out2, err := invoke.CommandWithContext(ctx, netstat, "-ind")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -131,10 +141,18 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
// NetIOCountersByFile is an method which is added just a compatibility for linux. // NetIOCountersByFile is an method which is added just a compatibility for linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
return IOCounters(pernic) return IOCounters(pernic)
} }
func FilterCounters() ([]FilterStat, error) { func FilterCounters() ([]FilterStat, error) {
return FilterCountersWithContext(context.Background())
}
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")
} }
@ -143,11 +161,152 @@ func FilterCounters() ([]FilterStat, error) {
// just the protocols in the list are returned. // just the protocols in the list are returned.
// Not Implemented for OpenBSD // Not Implemented for OpenBSD
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for openbsd") return nil, errors.New("NetProtoCounters not implemented for openbsd")
} }
// Return a list of network connections opened. func parseNetstatLine(line string) (ConnectionStat, error) {
// Not Implemented for OpenBSD f := strings.Fields(line)
func Connections(kind string) ([]ConnectionStat, error) { if len(f) < 5 {
return nil, errors.New("Connections not implemented for openbsd") return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
}
var netType, netFamily uint32
switch f[0] {
case "tcp":
netType = syscall.SOCK_STREAM
netFamily = syscall.AF_INET
case "udp":
netType = syscall.SOCK_DGRAM
netFamily = syscall.AF_INET
case "tcp6":
netType = syscall.SOCK_STREAM
netFamily = syscall.AF_INET6
case "udp6":
netType = syscall.SOCK_DGRAM
netFamily = syscall.AF_INET6
default:
return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[0])
}
laddr, raddr, err := parseNetstatAddr(f[3], f[4], netFamily)
if err != nil {
return ConnectionStat{}, fmt.Errorf("failed to parse netaddr, %s %s", f[3], f[4])
}
n := ConnectionStat{
Fd: uint32(0), // not supported
Family: uint32(netFamily),
Type: uint32(netType),
Laddr: laddr,
Raddr: raddr,
Pid: int32(0), // not supported
}
if len(f) == 6 {
n.Status = f[5]
}
return n, nil
}
func parseNetstatAddr(local string, remote string, family uint32) (laddr Addr, raddr Addr, err error) {
parse := func(l string) (Addr, error) {
matches := portMatch.FindStringSubmatch(l)
if matches == nil {
return Addr{}, fmt.Errorf("wrong addr, %s", l)
}
host := matches[1]
port := matches[2]
if host == "*" {
switch family {
case syscall.AF_INET:
host = "0.0.0.0"
case syscall.AF_INET6:
host = "::"
default:
return Addr{}, fmt.Errorf("unknown family, %d", family)
}
}
lport, err := strconv.Atoi(port)
if err != nil {
return Addr{}, err
}
return Addr{IP: host, Port: uint32(lport)}, nil
}
laddr, err = parse(local)
if remote != "*.*" { // remote addr exists
raddr, err = parse(remote)
if err != nil {
return laddr, raddr, err
}
}
return laddr, raddr, err
}
// Return a list of network connections opened.
func Connections(kind string) ([]ConnectionStat, error) {
return ConnectionsWithContext(context.Background(), kind)
}
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
var ret []ConnectionStat
args := []string{"-na"}
switch strings.ToLower(kind) {
default:
fallthrough
case "":
fallthrough
case "all":
fallthrough
case "inet":
// nothing to add
case "inet4":
args = append(args, "-finet")
case "inet6":
args = append(args, "-finet6")
case "tcp":
args = append(args, "-ptcp")
case "tcp4":
args = append(args, "-ptcp", "-finet")
case "tcp6":
args = append(args, "-ptcp", "-finet6")
case "udp":
args = append(args, "-pudp")
case "udp4":
args = append(args, "-pudp", "-finet")
case "udp6":
args = append(args, "-pudp", "-finet6")
case "unix":
return ret, common.ErrNotImplementedError
}
netstat, err := exec.LookPath("netstat")
if err != nil {
return nil, err
}
out, err := invoke.CommandWithContext(ctx, netstat, args...)
if err != nil {
return nil, err
}
lines := strings.Split(string(out), "\n")
for _, line := range lines {
if !(strings.HasPrefix(line, "tcp") || strings.HasPrefix(line, "udp")) {
continue
}
n, err := parseNetstatLine(line)
if err != nil {
continue
}
ret = append(ret, n)
}
return ret, nil
} }

View File

@ -3,6 +3,7 @@
package net package net
import ( import (
"context"
"strings" "strings"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
@ -10,17 +11,29 @@ import (
// Return a list of network connections opened. // Return a list of network connections opened.
func Connections(kind string) ([]ConnectionStat, error) { func Connections(kind string) ([]ConnectionStat, error) {
return ConnectionsWithContext(context.Background(), kind)
}
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
return ConnectionsPid(kind, 0) return ConnectionsPid(kind, 0)
} }
// Return a list of network connections opened returning at most `max` // Return a list of network connections opened returning at most `max`
// connections for each running process. // connections for each running process.
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
return ConnectionsMaxWithContext(context.Background(), kind, max)
}
func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
return []ConnectionStat{}, common.ErrNotImplementedError return []ConnectionStat{}, common.ErrNotImplementedError
} }
// Return a list of network connections opened by a process. // Return a list of network connections opened by a process.
func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
return ConnectionsPidWithContext(context.Background(), kind, pid)
}
func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
var ret []ConnectionStat var ret []ConnectionStat
args := []string{"-i"} args := []string{"-i"}
@ -50,10 +63,10 @@ func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
case "udp6": case "udp6":
args = append(args, "6udp") args = append(args, "6udp")
case "unix": case "unix":
return ret, common.ErrNotImplementedError args = []string{"-U"}
} }
r, err := common.CallLsof(invoke, pid, args...) r, err := common.CallLsofWithContext(ctx, invoke, pid, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -75,5 +88,9 @@ func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
// Return up to `max` network connections opened by a process. // Return up to `max` network connections opened by a process.
func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) { func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
}
func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
return []ConnectionStat{}, common.ErrNotImplementedError return []ConnectionStat{}, common.ErrNotImplementedError
} }

View File

@ -3,16 +3,20 @@
package net package net
import ( import (
"context"
"errors" "errors"
"fmt"
"net" "net"
"os" "os"
"syscall" "syscall"
"unsafe"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
"golang.org/x/sys/windows"
) )
var ( var (
modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll") modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable") procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable")
procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable")
) )
@ -29,7 +33,51 @@ const (
TCPTableOwnerModuleAll TCPTableOwnerModuleAll
) )
type netConnectionKindType struct {
family uint32
sockType uint32
filename string
}
var kindTCP4 = netConnectionKindType{
family: syscall.AF_INET,
sockType: syscall.SOCK_STREAM,
filename: "tcp",
}
var kindTCP6 = netConnectionKindType{
family: syscall.AF_INET6,
sockType: syscall.SOCK_STREAM,
filename: "tcp6",
}
var kindUDP4 = netConnectionKindType{
family: syscall.AF_INET,
sockType: syscall.SOCK_DGRAM,
filename: "udp",
}
var kindUDP6 = netConnectionKindType{
family: syscall.AF_INET6,
sockType: syscall.SOCK_DGRAM,
filename: "udp6",
}
var netConnectionKindMap = map[string][]netConnectionKindType{
"all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
"tcp": {kindTCP4, kindTCP6},
"tcp4": {kindTCP4},
"tcp6": {kindTCP6},
"udp": {kindUDP4, kindUDP6},
"udp4": {kindUDP4},
"udp6": {kindUDP6},
"inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
"inet4": {kindTCP4, kindUDP4},
"inet6": {kindTCP6, kindUDP6},
}
func IOCounters(pernic bool) ([]IOCountersStat, error) { func IOCounters(pernic bool) ([]IOCountersStat, error) {
return IOCountersWithContext(context.Background(), pernic)
}
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
ifs, err := net.Interfaces() ifs, err := net.Interfaces()
if err != nil { if err != nil {
return nil, err return nil, err
@ -41,8 +89,8 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
Name: ifi.Name, Name: ifi.Name,
} }
row := syscall.MibIfRow{Index: uint32(ifi.Index)} row := windows.MibIfRow{Index: uint32(ifi.Index)}
e := syscall.GetIfEntry(&row) e := windows.GetIfEntry(&row)
if e != nil { if e != nil {
return nil, os.NewSyscallError("GetIfEntry", e) return nil, os.NewSyscallError("GetIfEntry", e)
} }
@ -66,23 +114,95 @@ func IOCounters(pernic bool) ([]IOCountersStat, error) {
// NetIOCountersByFile is an method which is added just a compatibility for linux. // NetIOCountersByFile is an method which is added just a compatibility for linux.
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) { func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
return IOCountersByFileWithContext(context.Background(), pernic, filename)
}
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
return IOCounters(pernic) return IOCounters(pernic)
} }
// Return a list of network connections opened by a process // Return a list of network connections
// Available kind:
// reference to netConnectionKindMap
func Connections(kind string) ([]ConnectionStat, error) { func Connections(kind string) ([]ConnectionStat, error) {
var ret []ConnectionStat return ConnectionsWithContext(context.Background(), kind)
}
return ret, common.ErrNotImplementedError func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
return ConnectionsPidWithContext(ctx, kind, 0)
}
// ConnectionsPid Return a list of network connections opened by a process
func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
return ConnectionsPidWithContext(context.Background(), kind, pid)
}
func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind]
if !ok {
return nil, fmt.Errorf("invalid kind, %s", kind)
}
return getProcInet(tmap, pid)
}
func getProcInet(kinds []netConnectionKindType, pid int32) ([]ConnectionStat, error) {
stats := make([]ConnectionStat, 0)
for _, kind := range kinds {
s, err := getNetStatWithKind(kind)
if err != nil {
continue
}
if pid == 0 {
stats = append(stats, s...)
} else {
for _, ns := range s {
if ns.Pid != pid {
continue
}
stats = append(stats, ns)
}
}
}
return stats, nil
}
func getNetStatWithKind(kindType netConnectionKindType) ([]ConnectionStat, error) {
if kindType.filename == "" {
return nil, fmt.Errorf("kind filename must be required")
}
switch kindType.filename {
case kindTCP4.filename:
return getTCPConnections(kindTCP4.family)
case kindTCP6.filename:
return getTCPConnections(kindTCP6.family)
case kindUDP4.filename:
return getUDPConnections(kindUDP4.family)
case kindUDP6.filename:
return getUDPConnections(kindUDP6.family)
}
return nil, fmt.Errorf("invalid kind filename, %s", kindType.filename)
} }
// Return a list of network connections opened returning at most `max` // Return a list of network connections opened returning at most `max`
// connections for each running process. // connections for each running process.
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) { func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
return ConnectionsMaxWithContext(context.Background(), kind, max)
}
func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
return []ConnectionStat{}, common.ErrNotImplementedError return []ConnectionStat{}, common.ErrNotImplementedError
} }
func FilterCounters() ([]FilterStat, error) { func FilterCounters() ([]FilterStat, error) {
return FilterCountersWithContext(context.Background())
}
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")
} }
@ -91,5 +211,435 @@ func FilterCounters() ([]FilterStat, error) {
// just the protocols in the list are returned. // just the protocols in the list are returned.
// Not Implemented for Windows // Not Implemented for Windows
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
return ProtoCountersWithContext(context.Background(), protocols)
}
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
return nil, errors.New("NetProtoCounters not implemented for windows") return nil, errors.New("NetProtoCounters not implemented for windows")
} }
func getTableUintptr(family uint32, buf []byte) uintptr {
var (
pmibTCPTable pmibTCPTableOwnerPidAll
pmibTCP6Table pmibTCP6TableOwnerPidAll
p uintptr
)
switch family {
case kindTCP4.family:
if len(buf) > 0 {
pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0]))
p = uintptr(unsafe.Pointer(pmibTCPTable))
} else {
p = uintptr(unsafe.Pointer(pmibTCPTable))
}
case kindTCP6.family:
if len(buf) > 0 {
pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
p = uintptr(unsafe.Pointer(pmibTCP6Table))
} else {
p = uintptr(unsafe.Pointer(pmibTCP6Table))
}
}
return p
}
func getTableInfo(filename string, table interface{}) (index, step, length int) {
switch filename {
case kindTCP4.filename:
index = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).DwNumEntries))
step = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).Table))
length = int(table.(pmibTCPTableOwnerPidAll).DwNumEntries)
case kindTCP6.filename:
index = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).DwNumEntries))
step = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).Table))
length = int(table.(pmibTCP6TableOwnerPidAll).DwNumEntries)
case kindUDP4.filename:
index = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).DwNumEntries))
step = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).Table))
length = int(table.(pmibUDPTableOwnerPid).DwNumEntries)
case kindUDP6.filename:
index = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).DwNumEntries))
step = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).Table))
length = int(table.(pmibUDP6TableOwnerPid).DwNumEntries)
}
return
}
func getTCPConnections(family uint32) ([]ConnectionStat, error) {
var (
p uintptr
buf []byte
size uint32
pmibTCPTable pmibTCPTableOwnerPidAll
pmibTCP6Table pmibTCP6TableOwnerPidAll
)
if family == 0 {
return nil, fmt.Errorf("faimly must be required")
}
for {
switch family {
case kindTCP4.family:
if len(buf) > 0 {
pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0]))
p = uintptr(unsafe.Pointer(pmibTCPTable))
} else {
p = uintptr(unsafe.Pointer(pmibTCPTable))
}
case kindTCP6.family:
if len(buf) > 0 {
pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
p = uintptr(unsafe.Pointer(pmibTCP6Table))
} else {
p = uintptr(unsafe.Pointer(pmibTCP6Table))
}
}
err := getExtendedTcpTable(p,
&size,
true,
family,
tcpTableOwnerPidAll,
0)
if err == nil {
break
}
if err != windows.ERROR_INSUFFICIENT_BUFFER {
return nil, err
}
buf = make([]byte, size)
}
var (
index, step int
length int
)
stats := make([]ConnectionStat, 0)
switch family {
case kindTCP4.family:
index, step, length = getTableInfo(kindTCP4.filename, pmibTCPTable)
case kindTCP6.family:
index, step, length = getTableInfo(kindTCP6.filename, pmibTCP6Table)
}
if length == 0 {
return nil, nil
}
for i := 0; i < length; i++ {
switch family {
case kindTCP4.family:
mibs := (*mibTCPRowOwnerPid)(unsafe.Pointer(&buf[index]))
ns := mibs.convertToConnectionStat()
stats = append(stats, ns)
case kindTCP6.family:
mibs := (*mibTCP6RowOwnerPid)(unsafe.Pointer(&buf[index]))
ns := mibs.convertToConnectionStat()
stats = append(stats, ns)
}
index += step
}
return stats, nil
}
func getUDPConnections(family uint32) ([]ConnectionStat, error) {
var (
p uintptr
buf []byte
size uint32
pmibUDPTable pmibUDPTableOwnerPid
pmibUDP6Table pmibUDP6TableOwnerPid
)
if family == 0 {
return nil, fmt.Errorf("faimly must be required")
}
for {
switch family {
case kindUDP4.family:
if len(buf) > 0 {
pmibUDPTable = (*mibUDPTableOwnerPid)(unsafe.Pointer(&buf[0]))
p = uintptr(unsafe.Pointer(pmibUDPTable))
} else {
p = uintptr(unsafe.Pointer(pmibUDPTable))
}
case kindUDP6.family:
if len(buf) > 0 {
pmibUDP6Table = (*mibUDP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
p = uintptr(unsafe.Pointer(pmibUDP6Table))
} else {
p = uintptr(unsafe.Pointer(pmibUDP6Table))
}
}
err := getExtendedUdpTable(
p,
&size,
true,
family,
udpTableOwnerPid,
0,
)
if err == nil {
break
}
if err != windows.ERROR_INSUFFICIENT_BUFFER {
return nil, err
}
buf = make([]byte, size)
}
var (
index, step, length int
)
stats := make([]ConnectionStat, 0)
switch family {
case kindUDP4.family:
index, step, length = getTableInfo(kindUDP4.filename, pmibUDPTable)
case kindUDP6.family:
index, step, length = getTableInfo(kindUDP6.filename, pmibUDP6Table)
}
if length == 0 {
return nil, nil
}
for i := 0; i < length; i++ {
switch family {
case kindUDP4.family:
mibs := (*mibUDPRowOwnerPid)(unsafe.Pointer(&buf[index]))
ns := mibs.convertToConnectionStat()
stats = append(stats, ns)
case kindUDP4.family:
mibs := (*mibUDP6RowOwnerPid)(unsafe.Pointer(&buf[index]))
ns := mibs.convertToConnectionStat()
stats = append(stats, ns)
}
index += step
}
return stats, nil
}
// tcpStatuses https://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx
var tcpStatuses = map[mibTCPState]string{
1: "CLOSED",
2: "LISTEN",
3: "SYN_SENT",
4: "SYN_RECEIVED",
5: "ESTABLISHED",
6: "FIN_WAIT_1",
7: "FIN_WAIT_2",
8: "CLOSE_WAIT",
9: "CLOSING",
10: "LAST_ACK",
11: "TIME_WAIT",
12: "DELETE",
}
func getExtendedTcpTable(pTcpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass tcpTableClass, reserved uint32) (errcode error) {
r1, _, _ := syscall.Syscall6(procGetExtendedTCPTable.Addr(), 6, pTcpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
if r1 != 0 {
errcode = syscall.Errno(r1)
}
return
}
func getExtendedUdpTable(pUdpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass udpTableClass, reserved uint32) (errcode error) {
r1, _, _ := syscall.Syscall6(procGetExtendedUDPTable.Addr(), 6, pUdpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
if r1 != 0 {
errcode = syscall.Errno(r1)
}
return
}
func getUintptrFromBool(b bool) uintptr {
if b {
return 1
}
return 0
}
const anySize = 1
// type MIB_TCP_STATE int32
type mibTCPState int32
type tcpTableClass int32
const (
tcpTableBasicListener tcpTableClass = iota
tcpTableBasicConnections
tcpTableBasicAll
tcpTableOwnerPidListener
tcpTableOwnerPidConnections
tcpTableOwnerPidAll
tcpTableOwnerModuleListener
tcpTableOwnerModuleConnections
tcpTableOwnerModuleAll
)
type udpTableClass int32
const (
udpTableBasic udpTableClass = iota
udpTableOwnerPid
udpTableOwnerModule
)
// TCP
type mibTCPRowOwnerPid struct {
DwState uint32
DwLocalAddr uint32
DwLocalPort uint32
DwRemoteAddr uint32
DwRemotePort uint32
DwOwningPid uint32
}
func (m *mibTCPRowOwnerPid) convertToConnectionStat() ConnectionStat {
ns := ConnectionStat{
Family: kindTCP4.family,
Type: kindTCP4.sockType,
Laddr: Addr{
IP: parseIPv4HexString(m.DwLocalAddr),
Port: uint32(decodePort(m.DwLocalPort)),
},
Raddr: Addr{
IP: parseIPv4HexString(m.DwRemoteAddr),
Port: uint32(decodePort(m.DwRemotePort)),
},
Pid: int32(m.DwOwningPid),
Status: tcpStatuses[mibTCPState(m.DwState)],
}
return ns
}
type mibTCPTableOwnerPid struct {
DwNumEntries uint32
Table [anySize]mibTCPRowOwnerPid
}
type mibTCP6RowOwnerPid struct {
UcLocalAddr [16]byte
DwLocalScopeId uint32
DwLocalPort uint32
UcRemoteAddr [16]byte
DwRemoteScopeId uint32
DwRemotePort uint32
DwState uint32
DwOwningPid uint32
}
func (m *mibTCP6RowOwnerPid) convertToConnectionStat() ConnectionStat {
ns := ConnectionStat{
Family: kindTCP6.family,
Type: kindTCP6.sockType,
Laddr: Addr{
IP: parseIPv6HexString(m.UcLocalAddr),
Port: uint32(decodePort(m.DwLocalPort)),
},
Raddr: Addr{
IP: parseIPv6HexString(m.UcRemoteAddr),
Port: uint32(decodePort(m.DwRemotePort)),
},
Pid: int32(m.DwOwningPid),
Status: tcpStatuses[mibTCPState(m.DwState)],
}
return ns
}
type mibTCP6TableOwnerPid struct {
DwNumEntries uint32
Table [anySize]mibTCP6RowOwnerPid
}
type pmibTCPTableOwnerPidAll *mibTCPTableOwnerPid
type pmibTCP6TableOwnerPidAll *mibTCP6TableOwnerPid
// UDP
type mibUDPRowOwnerPid struct {
DwLocalAddr uint32
DwLocalPort uint32
DwOwningPid uint32
}
func (m *mibUDPRowOwnerPid) convertToConnectionStat() ConnectionStat {
ns := ConnectionStat{
Family: kindUDP4.family,
Type: kindUDP4.sockType,
Laddr: Addr{
IP: parseIPv4HexString(m.DwLocalAddr),
Port: uint32(decodePort(m.DwLocalPort)),
},
Pid: int32(m.DwOwningPid),
}
return ns
}
type mibUDPTableOwnerPid struct {
DwNumEntries uint32
Table [anySize]mibUDPRowOwnerPid
}
type mibUDP6RowOwnerPid struct {
UcLocalAddr [16]byte
DwLocalScopeId uint32
DwLocalPort uint32
DwOwningPid uint32
}
func (m *mibUDP6RowOwnerPid) convertToConnectionStat() ConnectionStat {
ns := ConnectionStat{
Family: kindUDP6.family,
Type: kindUDP6.sockType,
Laddr: Addr{
IP: parseIPv6HexString(m.UcLocalAddr),
Port: uint32(decodePort(m.DwLocalPort)),
},
Pid: int32(m.DwOwningPid),
}
return ns
}
type mibUDP6TableOwnerPid struct {
DwNumEntries uint32
Table [anySize]mibUDP6RowOwnerPid
}
type pmibUDPTableOwnerPid *mibUDPTableOwnerPid
type pmibUDP6TableOwnerPid *mibUDP6TableOwnerPid
func decodePort(port uint32) uint16 {
return syscall.Ntohs(uint16(port))
}
func parseIPv4HexString(addr uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", addr&255, addr>>8&255, addr>>16&255, addr>>24&255)
}
func parseIPv6HexString(addr [16]byte) string {
var ret [16]byte
for i := 0; i < 16; i++ {
ret[i] = uint8(addr[i])
}
// convert []byte to net.IP
ip := net.IP(ret[:])
return ip.String()
}

View File

@ -1,7 +1,9 @@
package process package process
import ( import (
"context"
"encoding/json" "encoding/json"
"errors"
"runtime" "runtime"
"time" "time"
@ -10,11 +12,10 @@ import (
"github.com/shirou/gopsutil/mem" "github.com/shirou/gopsutil/mem"
) )
var invoke common.Invoker var (
invoke common.Invoker = common.Invoke{}
func init() { ErrorNoChildren = errors.New("process does not have children")
invoke = common.Invoke{} )
}
type Process struct { type Process struct {
Pid int32 `json:"pid"` Pid int32 `json:"pid"`
@ -26,9 +27,12 @@ type Process struct {
gids []int32 gids []int32
numThreads int32 numThreads int32
memInfo *MemoryInfoStat memInfo *MemoryInfoStat
sigInfo *SignalInfoStat
lastCPUTimes *cpu.TimesStat lastCPUTimes *cpu.TimesStat
lastCPUTime time.Time lastCPUTime time.Time
tgid int32
} }
type OpenFilesStat struct { type OpenFilesStat struct {
@ -37,15 +41,27 @@ type OpenFilesStat struct {
} }
type MemoryInfoStat struct { type MemoryInfoStat struct {
RSS uint64 `json:"rss"` // bytes RSS uint64 `json:"rss"` // bytes
VMS uint64 `json:"vms"` // bytes VMS uint64 `json:"vms"` // bytes
Swap uint64 `json:"swap"` // bytes Data uint64 `json:"data"` // bytes
Stack uint64 `json:"stack"` // bytes
Locked uint64 `json:"locked"` // bytes
Swap uint64 `json:"swap"` // bytes
}
type SignalInfoStat struct {
PendingProcess uint64 `json:"pending_process"`
PendingThread uint64 `json:"pending_thread"`
Blocked uint64 `json:"blocked"`
Ignored uint64 `json:"ignored"`
Caught uint64 `json:"caught"`
} }
type RlimitStat struct { type RlimitStat struct {
Resource int32 `json:"resource"` Resource int32 `json:"resource"`
Soft int32 `json:"soft"` Soft int32 `json:"soft"` //TODO too small. needs to be uint64
Hard int32 `json:"hard"` Hard int32 `json:"hard"` //TODO too small. needs to be uint64
Used uint64 `json:"used"`
} }
type IOCountersStat struct { type IOCountersStat struct {
@ -112,6 +128,10 @@ func (p NumCtxSwitchesStat) String() string {
} }
func PidExists(pid int32) (bool, error) { func PidExists(pid int32) (bool, error) {
return PidExistsWithContext(context.Background(), pid)
}
func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) {
pids, err := Pids() pids, err := Pids()
if err != nil { if err != nil {
return false, err return false, err
@ -129,6 +149,10 @@ func PidExists(pid int32) (bool, error) {
// If interval is 0, return difference from last call(non-blocking). // If interval is 0, return difference from last call(non-blocking).
// If interval > 0, wait interval sec and return diffrence between start and end. // If interval > 0, wait interval sec and return diffrence between start and end.
func (p *Process) Percent(interval time.Duration) (float64, error) { func (p *Process) Percent(interval time.Duration) (float64, error) {
return p.PercentWithContext(context.Background(), interval)
}
func (p *Process) PercentWithContext(ctx context.Context, interval time.Duration) (float64, error) {
cpuTimes, err := p.Times() cpuTimes, err := p.Times()
if err != nil { if err != nil {
return 0, err return 0, err
@ -172,6 +196,10 @@ func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64
// MemoryPercent returns how many percent of the total RAM this process uses // MemoryPercent returns how many percent of the total RAM this process uses
func (p *Process) MemoryPercent() (float32, error) { func (p *Process) MemoryPercent() (float32, error) {
return p.MemoryPercentWithContext(context.Background())
}
func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) {
machineMemory, err := mem.VirtualMemory() machineMemory, err := mem.VirtualMemory()
if err != nil { if err != nil {
return 0, err return 0, err
@ -186,3 +214,28 @@ func (p *Process) MemoryPercent() (float32, error) {
return (100 * float32(used) / float32(total)), nil return (100 * float32(used) / float32(total)), nil
} }
// CPU_Percent returns how many percent of the CPU time this process uses
func (p *Process) CPUPercent() (float64, error) {
return p.CPUPercentWithContext(context.Background())
}
func (p *Process) CPUPercentWithContext(ctx context.Context) (float64, error) {
crt_time, err := p.CreateTime()
if err != nil {
return 0, err
}
cput, err := p.Times()
if err != nil {
return 0, err
}
created := time.Unix(0, crt_time*int64(time.Millisecond))
totalTime := time.Since(created).Seconds()
if totalTime <= 0 {
return 0, nil
}
return 100 * cput.Total() / totalTime, nil
}

View File

@ -4,18 +4,19 @@ package process
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"os/exec" "os/exec"
"strconv" "strconv"
"strings" "strings"
"syscall"
"time" "time"
"unsafe" "unsafe"
"github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/cpu"
"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"
) )
// copied from sys/sysctl.h // copied from sys/sysctl.h
@ -44,9 +45,13 @@ type MemoryMapsStat struct {
} }
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32 var ret []int32
pids, err := callPs("pid", 0, false) pids, err := callPsWithContext(ctx, "pid", 0, false)
if err != nil { if err != nil {
return ret, err return ret, err
} }
@ -63,7 +68,15 @@ func Pids() ([]int32, error) {
} }
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
r, err := callPs("ppid", p.Pid, false) return p.PpidWithContext(context.Background())
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "ppid", p.Pid, false)
if err != nil {
return 0, err
}
v, err := strconv.Atoi(r[0][0]) v, err := strconv.Atoi(r[0][0])
if err != nil { if err != nil {
return 0, err return 0, err
@ -72,6 +85,10 @@ func (p *Process) Ppid() (int32, error) {
return int32(v), err return int32(v), err
} }
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
return p.NameWithContext(context.Background())
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -79,7 +96,14 @@ func (p *Process) Name() (string, error) {
return common.IntToString(k.Proc.P_comm[:]), nil return common.IntToString(k.Proc.P_comm[:]), nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.ExeWithContext(context.Background())
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
lsof_bin, err := exec.LookPath("lsof") lsof_bin, err := exec.LookPath("lsof")
if err != nil { if err != nil {
return "", err return "", err
@ -95,9 +119,9 @@ func (p *Process) Exe() (string, error) {
return "", err return "", err
} }
lsof := exec.Command(lsof_bin, "-p", strconv.Itoa(int(p.Pid)), "-Fn") lsof := exec.CommandContext(ctx, lsof_bin, "-p", strconv.Itoa(int(p.Pid)), "-Fpfn")
awk := exec.Command(awk_bin, "NR==3{print}") awk := exec.CommandContext(ctx, awk_bin, "NR==5{print}")
sed := exec.Command(sed_bin, "s/n\\//\\//") sed := exec.CommandContext(ctx, sed_bin, "s/n\\//\\//")
output, _, err := common.Pipeline(lsof, awk, sed) output, _, err := common.Pipeline(lsof, awk, sed)
@ -113,7 +137,11 @@ func (p *Process) Exe() (string, error) {
// Cmdline returns the command line arguments of the process as a string with // Cmdline returns the command line arguments of the process as a string with
// each argument separated by 0x20 ascii character. // each argument separated by 0x20 ascii character.
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
r, err := callPs("command", p.Pid, false) return p.CmdlineWithContext(context.Background())
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -126,14 +154,22 @@ func (p *Process) Cmdline() (string, error) {
// reported as two separate items. In order to do something better CGO would be needed // reported as two separate items. In order to do something better CGO would be needed
// to use the native darwin functions. // to use the native darwin functions.
func (p *Process) CmdlineSlice() ([]string, error) { func (p *Process) CmdlineSlice() ([]string, error) {
r, err := callPs("command", p.Pid, false) return p.CmdlineSliceWithContext(context.Background())
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
r, err := callPsWithContext(ctx, "command", p.Pid, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return r[0], err return r[0], err
} }
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
r, err := callPs("etime", p.Pid, false) return p.CreateTimeWithContext(context.Background())
}
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
r, err := callPsWithContext(ctx, "etime", p.Pid, false)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -148,7 +184,7 @@ func (p *Process) CreateTime() (int64, error) {
elapsedDurations = append(elapsedDurations, time.Duration(p)) elapsedDurations = append(elapsedDurations, time.Duration(p))
} }
var elapsed time.Duration = time.Duration(elapsedDurations[0]) * time.Second var elapsed = time.Duration(elapsedDurations[0]) * time.Second
if len(elapsedDurations) > 1 { if len(elapsedDurations) > 1 {
elapsed += time.Duration(elapsedDurations[1]) * time.Minute elapsed += time.Duration(elapsedDurations[1]) * time.Minute
} }
@ -163,10 +199,18 @@ func (p *Process) CreateTime() (int64, error) {
return start.Unix() * 1000, nil return start.Unix() * 1000, nil
} }
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return p.CwdWithContext(context.Background())
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Parent() (*Process, error) { func (p *Process) Parent() (*Process, error) {
rr, err := common.CallLsof(invoke, p.Pid, "-FR") return p.ParentWithContext(context.Background())
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
rr, err := common.CallLsofWithContext(ctx, invoke, p.Pid, "-FR")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -184,7 +228,11 @@ func (p *Process) Parent() (*Process, error) {
return nil, fmt.Errorf("could not find parent line") return nil, fmt.Errorf("could not find parent line")
} }
func (p *Process) Status() (string, error) { func (p *Process) Status() (string, error) {
r, err := callPs("state", p.Pid, false) return p.StatusWithContext(context.Background())
}
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
r, err := callPsWithContext(ctx, "state", p.Pid, false)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -192,6 +240,10 @@ func (p *Process) Status() (string, error) {
return r[0][0], err return r[0][0], err
} }
func (p *Process) Uids() ([]int32, error) { func (p *Process) Uids() ([]int32, error) {
return p.UidsWithContext(context.Background())
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -203,6 +255,10 @@ func (p *Process) Uids() ([]int32, error) {
return []int32{userEffectiveUID}, nil return []int32{userEffectiveUID}, nil
} }
func (p *Process) Gids() ([]int32, error) { func (p *Process) Gids() ([]int32, error) {
return p.GidsWithContext(context.Background())
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -214,6 +270,10 @@ func (p *Process) Gids() ([]int32, error) {
return gids, nil return gids, nil
} }
func (p *Process) Terminal() (string, error) { func (p *Process) Terminal() (string, error) {
return p.TerminalWithContext(context.Background())
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
/* /*
k, err := p.getKProc() k, err := p.getKProc()
@ -231,6 +291,10 @@ func (p *Process) Terminal() (string, error) {
*/ */
} }
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
return p.NiceWithContext(context.Background())
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return 0, err return 0, err
@ -238,30 +302,66 @@ func (p *Process) Nice() (int32, error) {
return int32(k.Proc.P_nice), nil return int32(k.Proc.P_nice), nil
} }
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Rlimit() ([]RlimitStat, error) { func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.RlimitWithContext(context.Background())
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
var rlimit []RlimitStat var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError return rlimit, common.ErrNotImplementedError
} }
func (p *Process) IOCounters() (*IOCountersStat, error) { func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.IOCountersWithContext(context.Background())
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.NumCtxSwitchesWithContext(context.Background())
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumFDs() (int32, error) { func (p *Process) NumFDs() (int32, error) {
return p.NumFDsWithContext(context.Background())
}
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
r, err := callPs("utime,stime", p.Pid, true) return p.NumThreadsWithContext(context.Background())
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return int32(len(r)), nil return int32(len(r)), nil
} }
func (p *Process) Threads() (map[string]string, error) { func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
ret := make(map[string]string, 0) return p.ThreadsWithContext(context.Background())
}
func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
ret := make(map[int32]*cpu.TimesStat)
return ret, common.ErrNotImplementedError return ret, common.ErrNotImplementedError
} }
@ -291,7 +391,11 @@ func convertCPUTimes(s string) (ret float64, err error) {
return float64(t) / ClockTicks, nil return float64(t) / ClockTicks, nil
} }
func (p *Process) Times() (*cpu.TimesStat, error) { func (p *Process) Times() (*cpu.TimesStat, error) {
r, err := callPs("utime,stime", p.Pid, false) return p.TimesWithContext(context.Background())
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false)
if err != nil { if err != nil {
return nil, err return nil, err
@ -314,10 +418,18 @@ func (p *Process) Times() (*cpu.TimesStat, error) {
return ret, nil return ret, nil
} }
func (p *Process) CPUAffinity() ([]int32, error) { func (p *Process) CPUAffinity() ([]int32, error) {
return p.CPUAffinityWithContext(context.Background())
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
r, err := callPs("rss,vsize,pagein", p.Pid, false) return p.MemoryInfoWithContext(context.Background())
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -343,11 +455,19 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return ret, nil return ret, nil
} }
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return p.MemoryInfoExWithContext(context.Background())
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
pids, err := common.CallPgrep(invoke, p.Pid) return p.ChildrenWithContext(context.Background())
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -363,61 +483,66 @@ func (p *Process) Children() ([]*Process, error) {
} }
func (p *Process) OpenFiles() ([]OpenFilesStat, error) { func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return p.OpenFilesWithContext(context.Background())
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Connections() ([]net.ConnectionStat, error) { func (p *Process) Connections() ([]net.ConnectionStat, error) {
return p.ConnectionsWithContext(context.Background())
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return net.ConnectionsPid("all", p.Pid) return net.ConnectionsPid("all", p.Pid)
} }
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
return p.NetIOCountersWithContext(context.Background(), pernic)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) IsRunning() (bool, error) { func (p *Process) IsRunning() (bool, error) {
return p.IsRunningWithContext(context.Background())
}
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
return true, common.ErrNotImplementedError return true, common.ErrNotImplementedError
} }
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return p.MemoryMapsWithContext(context.Background(), grouped)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
var ret []MemoryMapsStat var ret []MemoryMapsStat
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
func processes() ([]Process, error) { func Processes() ([]*Process, error) {
results := make([]Process, 0, 50) return ProcessesWithContext(context.Background())
}
mib := []int32{CTLKern, KernProc, KernProcAll, 0} func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
buf, length, err := common.CallSyscall(mib) out := []*Process{}
pids, err := PidsWithContext(ctx)
if err != nil { if err != nil {
return results, err return out, err
} }
// get kinfo_proc size for _, pid := range pids {
k := KinfoProc{} p, err := NewProcess(pid)
procinfoLen := int(unsafe.Sizeof(k))
count := int(length / uint64(procinfoLen))
/*
fmt.Println(length, procinfoLen, count)
b := buf[0*procinfoLen : 0*procinfoLen+procinfoLen]
fmt.Println(b)
kk, err := parseKinfoProc(b)
fmt.Printf("%#v", kk)
*/
// parse buf to procs
for i := 0; i < count; i++ {
b := buf[i*procinfoLen : i*procinfoLen+procinfoLen]
k, err := parseKinfoProc(b)
if err != nil { if err != nil {
continue continue
} }
p, err := NewProcess(int32(k.Proc.P_pid)) out = append(out, p)
if err != nil {
continue
}
results = append(results, *p)
} }
return results, nil return out, nil
} }
func parseKinfoProc(buf []byte) (KinfoProc, error) { func parseKinfoProc(buf []byte) (KinfoProc, error) {
@ -435,12 +560,16 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) {
// Returns a proc as defined here: // Returns a proc as defined here:
// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html // http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html
func (p *Process) getKProc() (*KinfoProc, error) { func (p *Process) getKProc() (*KinfoProc, error) {
return p.getKProcWithContext(context.Background())
}
func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) {
mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
procK := KinfoProc{} procK := KinfoProc{}
length := uint64(unsafe.Sizeof(procK)) length := uint64(unsafe.Sizeof(procK))
buf := make([]byte, length) buf := make([]byte, length)
_, _, syserr := syscall.Syscall6( _, _, syserr := unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])), uintptr(unsafe.Pointer(&mib[0])),
uintptr(len(mib)), uintptr(len(mib)),
uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&buf[0])),
@ -468,7 +597,7 @@ func NewProcess(pid int32) (*Process, error) {
// Return value deletes Header line(you must not input wrong arg). // Return value deletes Header line(you must not input wrong arg).
// And splited by Space. Caller have responsibility to manage. // And splited by Space. Caller have responsibility to manage.
// If passed arg pid is 0, get information from all process. // If passed arg pid is 0, get information from all process.
func callPs(arg string, pid int32, threadOption bool) ([][]string, error) { func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) {
bin, err := exec.LookPath("ps") bin, err := exec.LookPath("ps")
if err != nil { if err != nil {
return [][]string{}, err return [][]string{}, err
@ -482,7 +611,7 @@ func callPs(arg string, pid int32, threadOption bool) ([][]string, error) {
} else { } else {
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
} }
out, err := invoke.Command(bin, cmd...) out, err := invoke.CommandWithContext(ctx, bin, cmd...)
if err != nil { if err != nil {
return [][]string{}, err return [][]string{}, err
} }

View File

@ -3,6 +3,7 @@
package process package process
import ( import (
"context"
"syscall" "syscall"
"github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/cpu"
@ -28,118 +29,284 @@ type MemoryInfoExStat struct {
} }
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
return []int32{}, common.ErrNotImplementedError return []int32{}, common.ErrNotImplementedError
} }
func Processes() ([]*Process, error) {
return nil, common.ErrNotImplementedError
}
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
return nil, common.ErrNotImplementedError
}
func NewProcess(pid int32) (*Process, error) { func NewProcess(pid int32) (*Process, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
return p.PpidWithContext(context.Background())
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
return p.NameWithContext(context.Background())
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.ExeWithContext(context.Background())
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
return p.CmdlineWithContext(context.Background())
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) CmdlineSlice() ([]string, error) { func (p *Process) CmdlineSlice() ([]string, error) {
return p.CmdlineSliceWithContext(context.Background())
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
return []string{}, common.ErrNotImplementedError return []string{}, common.ErrNotImplementedError
} }
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
return p.CreateTimeWithContext(context.Background())
}
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return p.CwdWithContext(context.Background())
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Parent() (*Process, error) { func (p *Process) Parent() (*Process, error) {
return p.ParentWithContext(context.Background())
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Status() (string, error) { func (p *Process) Status() (string, error) {
return p.StatusWithContext(context.Background())
}
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Uids() ([]int32, error) { func (p *Process) Uids() ([]int32, error) {
return p.UidsWithContext(context.Background())
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
return []int32{}, common.ErrNotImplementedError return []int32{}, common.ErrNotImplementedError
} }
func (p *Process) Gids() ([]int32, error) { func (p *Process) Gids() ([]int32, error) {
return p.GidsWithContext(context.Background())
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
return []int32{}, common.ErrNotImplementedError return []int32{}, common.ErrNotImplementedError
} }
func (p *Process) Terminal() (string, error) { func (p *Process) Terminal() (string, error) {
return p.TerminalWithContext(context.Background())
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
return p.NiceWithContext(context.Background())
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Rlimit() ([]RlimitStat, error) { func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.RlimitWithContext(context.Background())
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
return nil, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) IOCounters() (*IOCountersStat, error) { func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.IOCountersWithContext(context.Background())
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.NumCtxSwitchesWithContext(context.Background())
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumFDs() (int32, error) { func (p *Process) NumFDs() (int32, error) {
return p.NumFDsWithContext(context.Background())
}
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
return p.NumThreadsWithContext(context.Background())
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Threads() (map[string]string, error) { func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
return p.ThreadsWithContext(context.Background())
}
func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Times() (*cpu.TimesStat, error) { func (p *Process) Times() (*cpu.TimesStat, error) {
return p.TimesWithContext(context.Background())
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) CPUAffinity() ([]int32, error) { func (p *Process) CPUAffinity() ([]int32, error) {
return p.CPUAffinityWithContext(context.Background())
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return p.MemoryInfoWithContext(context.Background())
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return p.MemoryInfoExWithContext(context.Background())
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
return p.ChildrenWithContext(context.Background())
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) OpenFiles() ([]OpenFilesStat, error) { func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return p.OpenFilesWithContext(context.Background())
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
return []OpenFilesStat{}, common.ErrNotImplementedError return []OpenFilesStat{}, common.ErrNotImplementedError
} }
func (p *Process) Connections() ([]net.ConnectionStat, error) { func (p *Process) Connections() ([]net.ConnectionStat, error) {
return p.ConnectionsWithContext(context.Background())
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return []net.ConnectionStat{}, common.ErrNotImplementedError return []net.ConnectionStat{}, common.ErrNotImplementedError
} }
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
return p.NetIOCountersWithContext(context.Background(), pernic)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
return []net.IOCountersStat{}, common.ErrNotImplementedError return []net.IOCountersStat{}, common.ErrNotImplementedError
} }
func (p *Process) IsRunning() (bool, error) { func (p *Process) IsRunning() (bool, error) {
return p.IsRunningWithContext(context.Background())
}
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
return true, common.ErrNotImplementedError return true, common.ErrNotImplementedError
} }
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return p.MemoryMapsWithContext(context.Background(), grouped)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) SendSignal(sig syscall.Signal) error { func (p *Process) SendSignal(sig syscall.Signal) error {
return p.SendSignalWithContext(context.Background(), sig)
}
func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Suspend() error { func (p *Process) Suspend() error {
return p.SuspendWithContext(context.Background())
}
func (p *Process) SuspendWithContext(ctx context.Context) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Resume() error { func (p *Process) Resume() error {
return p.ResumeWithContext(context.Background())
}
func (p *Process) ResumeWithContext(ctx context.Context) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Terminate() error { func (p *Process) Terminate() error {
return p.TerminateWithContext(context.Background())
}
func (p *Process) TerminateWithContext(ctx context.Context) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Kill() error { func (p *Process) Kill() error {
return p.KillWithContext(context.Background())
}
func (p *Process) KillWithContext(ctx context.Context) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Username() (string, error) { func (p *Process) Username() (string, error) {
return p.UsernameWithContext(context.Background())
}
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }

View File

@ -4,13 +4,14 @@ package process
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"strings" "strings"
"syscall"
cpu "github.com/shirou/gopsutil/cpu" cpu "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
net "github.com/shirou/gopsutil/net" net "github.com/shirou/gopsutil/net"
"golang.org/x/sys/unix"
) )
// MemoryInfoExStat is different between OSes // MemoryInfoExStat is different between OSes
@ -21,8 +22,12 @@ type MemoryMapsStat struct {
} }
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32 var ret []int32
procs, err := processes() procs, err := Processes()
if err != nil { if err != nil {
return ret, nil return ret, nil
} }
@ -35,6 +40,10 @@ func Pids() ([]int32, error) {
} }
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
return p.PpidWithContext(context.Background())
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return 0, err return 0, err
@ -43,6 +52,10 @@ func (p *Process) Ppid() (int32, error) {
return k.Ppid, nil return k.Ppid, nil
} }
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
return p.NameWithContext(context.Background())
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -50,11 +63,22 @@ func (p *Process) Name() (string, error) {
return common.IntToString(k.Comm[:]), nil return common.IntToString(k.Comm[:]), nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.ExeWithContext(context.Background())
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
return p.CmdlineWithContext(context.Background())
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid}
buf, _, err := common.CallSyscall(mib) buf, _, err := common.CallSyscall(mib)
if err != nil { if err != nil {
@ -71,6 +95,10 @@ func (p *Process) Cmdline() (string, error) {
} }
func (p *Process) CmdlineSlice() ([]string, error) { func (p *Process) CmdlineSlice() ([]string, error) {
return p.CmdlineSliceWithContext(context.Background())
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid} mib := []int32{CTLKern, KernProc, KernProcArgs, p.Pid}
buf, _, err := common.CallSyscall(mib) buf, _, err := common.CallSyscall(mib)
if err != nil { if err != nil {
@ -91,15 +119,31 @@ func (p *Process) CmdlineSlice() ([]string, error) {
return strParts, nil return strParts, nil
} }
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
return p.CreateTimeWithContext(context.Background())
}
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return p.CwdWithContext(context.Background())
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Parent() (*Process, error) { func (p *Process) Parent() (*Process, error) {
return p.ParentWithContext(context.Background())
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return p, common.ErrNotImplementedError return p, common.ErrNotImplementedError
} }
func (p *Process) Status() (string, error) { func (p *Process) Status() (string, error) {
return p.StatusWithContext(context.Background())
}
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -125,6 +169,10 @@ func (p *Process) Status() (string, error) {
return s, nil return s, nil
} }
func (p *Process) Uids() ([]int32, error) { func (p *Process) Uids() ([]int32, error) {
return p.UidsWithContext(context.Background())
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -137,6 +185,10 @@ func (p *Process) Uids() ([]int32, error) {
return uids, nil return uids, nil
} }
func (p *Process) Gids() ([]int32, error) { func (p *Process) Gids() ([]int32, error) {
return p.GidsWithContext(context.Background())
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -148,6 +200,10 @@ func (p *Process) Gids() ([]int32, error) {
return gids, nil return gids, nil
} }
func (p *Process) Terminal() (string, error) { func (p *Process) Terminal() (string, error) {
return p.TerminalWithContext(context.Background())
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -163,6 +219,10 @@ func (p *Process) Terminal() (string, error) {
return termmap[ttyNr], nil return termmap[ttyNr], nil
} }
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
return p.NiceWithContext(context.Background())
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return 0, err return 0, err
@ -170,13 +230,33 @@ func (p *Process) Nice() (int32, error) {
return int32(k.Nice), nil return int32(k.Nice), nil
} }
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Rlimit() ([]RlimitStat, error) { func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.RlimitWithContext(context.Background())
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
var rlimit []RlimitStat var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError return rlimit, common.ErrNotImplementedError
} }
func (p *Process) IOCounters() (*IOCountersStat, error) { func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.IOCountersWithContext(context.Background())
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -187,12 +267,24 @@ func (p *Process) IOCounters() (*IOCountersStat, error) {
}, nil }, nil
} }
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.NumCtxSwitchesWithContext(context.Background())
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumFDs() (int32, error) { func (p *Process) NumFDs() (int32, error) {
return p.NumFDsWithContext(context.Background())
}
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
return p.NumThreadsWithContext(context.Background())
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return 0, err return 0, err
@ -200,11 +292,19 @@ func (p *Process) NumThreads() (int32, error) {
return k.Numthreads, nil return k.Numthreads, nil
} }
func (p *Process) Threads() (map[string]string, error) { func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
ret := make(map[string]string, 0) return p.ThreadsWithContext(context.Background())
}
func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
ret := make(map[int32]*cpu.TimesStat)
return ret, common.ErrNotImplementedError return ret, common.ErrNotImplementedError
} }
func (p *Process) Times() (*cpu.TimesStat, error) { func (p *Process) Times() (*cpu.TimesStat, error) {
return p.TimesWithContext(context.Background())
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -216,14 +316,22 @@ func (p *Process) Times() (*cpu.TimesStat, error) {
}, nil }, nil
} }
func (p *Process) CPUAffinity() ([]int32, error) { func (p *Process) CPUAffinity() ([]int32, error) {
return p.CPUAffinityWithContext(context.Background())
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return p.MemoryInfoWithContext(context.Background())
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
} }
v, err := syscall.Sysctl("vm.stats.vm.v_page_size") v, err := unix.Sysctl("vm.stats.vm.v_page_size")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -235,11 +343,19 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
}, nil }, nil
} }
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return p.MemoryInfoExWithContext(context.Background())
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
pids, err := common.CallPgrep(invoke, p.Pid) return p.ChildrenWithContext(context.Background())
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -255,27 +371,51 @@ func (p *Process) Children() ([]*Process, error) {
} }
func (p *Process) OpenFiles() ([]OpenFilesStat, error) { func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return p.OpenFilesWithContext(context.Background())
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Connections() ([]net.ConnectionStat, error) { func (p *Process) Connections() ([]net.ConnectionStat, error) {
return p.ConnectionsWithContext(context.Background())
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
return p.NetIOCountersWithContext(context.Background(), pernic)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) IsRunning() (bool, error) { func (p *Process) IsRunning() (bool, error) {
return p.IsRunningWithContext(context.Background())
}
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
return true, common.ErrNotImplementedError return true, common.ErrNotImplementedError
} }
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return p.MemoryMapsWithContext(context.Background(), grouped)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
var ret []MemoryMapsStat var ret []MemoryMapsStat
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
func processes() ([]Process, error) { func Processes() ([]*Process, error) {
results := make([]Process, 0, 50) return ProcessesWithContext(context.Background())
}
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
results := []*Process{}
mib := []int32{CTLKern, KernProc, KernProcProc, 0} mib := []int32{CTLKern, KernProc, KernProcProc, 0}
buf, length, err := common.CallSyscall(mib) buf, length, err := common.CallSyscall(mib)
@ -298,7 +438,7 @@ func processes() ([]Process, error) {
continue continue
} }
results = append(results, *p) results = append(results, p)
} }
return results, nil return results, nil
@ -312,6 +452,10 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) {
} }
func (p *Process) getKProc() (*KinfoProc, error) { func (p *Process) getKProc() (*KinfoProc, error) {
return p.getKProcWithContext(context.Background())
}
func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) {
mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid}
buf, length, err := common.CallSyscall(mib) buf, length, err := common.CallSyscall(mib)

View File

@ -5,8 +5,8 @@ package process
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math" "math"
@ -14,18 +14,15 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/host" "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"
) )
var ( var PageSize = uint64(os.Getpagesize())
ErrorNoChildren = errors.New("process does not have children")
PageSize = uint64(os.Getpagesize())
)
const ( const (
PrioProcess = 0 // linux/resource.h PrioProcess = 0 // linux/resource.h
@ -83,7 +80,11 @@ func NewProcess(pid int32) (*Process, error) {
// Ppid returns Parent Process ID of the process. // Ppid returns Parent Process ID of the process.
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
_, ppid, _, _, _, err := p.fillFromStat() return p.PpidWithContext(context.Background())
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
_, ppid, _, _, _, _, err := p.fillFromStat()
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -92,6 +93,10 @@ func (p *Process) Ppid() (int32, error) {
// Name returns name of the process. // Name returns name of the process.
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
return p.NameWithContext(context.Background())
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
if p.name == "" { if p.name == "" {
if err := p.fillFromStatus(); err != nil { if err := p.fillFromStatus(); err != nil {
return "", err return "", err
@ -100,26 +105,52 @@ func (p *Process) Name() (string, error) {
return p.name, nil return p.name, nil
} }
// Tgid returns tgid, a Linux-synonym for user-space Pid
func (p *Process) Tgid() (int32, error) {
if p.tgid == 0 {
if err := p.fillFromStatus(); err != nil {
return 0, err
}
}
return p.tgid, nil
}
// Exe returns executable path of the process. // Exe returns executable path of the process.
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.ExeWithContext(context.Background())
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
return p.fillFromExe() return p.fillFromExe()
} }
// Cmdline returns the command line arguments of the process as a string with // Cmdline returns the command line arguments of the process as a string with
// each argument separated by 0x20 ascii character. // each argument separated by 0x20 ascii character.
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
return p.CmdlineWithContext(context.Background())
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
return p.fillFromCmdline() return p.fillFromCmdline()
} }
// CmdlineSlice returns the command line arguments of the process as a slice with each // CmdlineSlice returns the command line arguments of the process as a slice with each
// element being an argument. // element being an argument.
func (p *Process) CmdlineSlice() ([]string, error) { func (p *Process) CmdlineSlice() ([]string, error) {
return p.CmdlineSliceWithContext(context.Background())
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
return p.fillSliceFromCmdline() return p.fillSliceFromCmdline()
} }
// CreateTime returns created time of the process in seconds since the epoch, in UTC. // CreateTime returns created time of the process in milliseconds since the epoch, in UTC.
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
_, _, _, createTime, _, err := p.fillFromStat() return p.CreateTimeWithContext(context.Background())
}
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
_, _, _, createTime, _, _, err := p.fillFromStat()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -128,11 +159,19 @@ func (p *Process) CreateTime() (int64, error) {
// Cwd returns current working directory of the process. // Cwd returns current working directory of the process.
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return p.CwdWithContext(context.Background())
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return p.fillFromCwd() return p.fillFromCwd()
} }
// Parent returns parent Process of the process. // Parent returns parent Process of the process.
func (p *Process) Parent() (*Process, error) { func (p *Process) Parent() (*Process, error) {
return p.ParentWithContext(context.Background())
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
err := p.fillFromStatus() err := p.fillFromStatus()
if err != nil { if err != nil {
return nil, err return nil, err
@ -149,6 +188,10 @@ func (p *Process) Parent() (*Process, error) {
// Z: Zombie W: Wait L: Lock // Z: Zombie W: Wait L: Lock
// The charactor is same within all supported platforms. // The charactor is same within all supported platforms.
func (p *Process) Status() (string, error) { func (p *Process) Status() (string, error) {
return p.StatusWithContext(context.Background())
}
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
err := p.fillFromStatus() err := p.fillFromStatus()
if err != nil { if err != nil {
return "", err return "", err
@ -158,6 +201,10 @@ func (p *Process) Status() (string, error) {
// Uids returns user ids of the process as a slice of the int // Uids returns user ids of the process as a slice of the int
func (p *Process) Uids() ([]int32, error) { func (p *Process) Uids() ([]int32, error) {
return p.UidsWithContext(context.Background())
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
err := p.fillFromStatus() err := p.fillFromStatus()
if err != nil { if err != nil {
return []int32{}, err return []int32{}, err
@ -167,6 +214,10 @@ func (p *Process) Uids() ([]int32, error) {
// Gids returns group ids of the process as a slice of the int // Gids returns group ids of the process as a slice of the int
func (p *Process) Gids() ([]int32, error) { func (p *Process) Gids() ([]int32, error) {
return p.GidsWithContext(context.Background())
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
err := p.fillFromStatus() err := p.fillFromStatus()
if err != nil { if err != nil {
return []int32{}, err return []int32{}, err
@ -176,17 +227,30 @@ func (p *Process) Gids() ([]int32, error) {
// Terminal returns a terminal which is associated with the process. // Terminal returns a terminal which is associated with the process.
func (p *Process) Terminal() (string, error) { func (p *Process) Terminal() (string, error) {
terminal, _, _, _, _, err := p.fillFromStat() return p.TerminalWithContext(context.Background())
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
t, _, _, _, _, _, err := p.fillFromStat()
if err != nil { if err != nil {
return "", err return "", err
} }
termmap, err := getTerminalMap()
if err != nil {
return "", err
}
terminal := termmap[t]
return terminal, nil return terminal, nil
} }
// Nice returns a nice value (priority). // Nice returns a nice value (priority).
// Notice: gopsutil can not set nice value. // Notice: gopsutil can not set nice value.
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
_, _, _, _, nice, err := p.fillFromStat() return p.NiceWithContext(context.Background())
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
_, _, _, _, _, nice, err := p.fillFromStat()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -195,21 +259,99 @@ func (p *Process) Nice() (int32, error) {
// IOnice returns process I/O nice value (priority). // IOnice returns process I/O nice value (priority).
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
// Rlimit returns Resource Limits. // Rlimit returns Resource Limits.
func (p *Process) Rlimit() ([]RlimitStat, error) { func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.fillFromLimits() return p.RlimitWithContext(context.Background())
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
return p.RlimitUsage(false)
}
// RlimitUsage returns Resource Limits.
// If gatherUsed is true, the currently used value will be gathered and added
// to the resulting RlimitStat.
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
rlimits, err := p.fillFromLimits()
if !gatherUsed || err != nil {
return rlimits, err
}
_, _, _, _, rtprio, nice, err := p.fillFromStat()
if err != nil {
return nil, err
}
if err := p.fillFromStatus(); err != nil {
return nil, err
}
for i := range rlimits {
rs := &rlimits[i]
switch rs.Resource {
case RLIMIT_CPU:
times, err := p.Times()
if err != nil {
return nil, err
}
rs.Used = uint64(times.User + times.System)
case RLIMIT_DATA:
rs.Used = uint64(p.memInfo.Data)
case RLIMIT_STACK:
rs.Used = uint64(p.memInfo.Stack)
case RLIMIT_RSS:
rs.Used = uint64(p.memInfo.RSS)
case RLIMIT_NOFILE:
n, err := p.NumFDs()
if err != nil {
return nil, err
}
rs.Used = uint64(n)
case RLIMIT_MEMLOCK:
rs.Used = uint64(p.memInfo.Locked)
case RLIMIT_AS:
rs.Used = uint64(p.memInfo.VMS)
case RLIMIT_LOCKS:
//TODO we can get the used value from /proc/$pid/locks. But linux doesn't enforce it, so not a high priority.
case RLIMIT_SIGPENDING:
rs.Used = p.sigInfo.PendingProcess
case RLIMIT_NICE:
// The rlimit for nice is a little unusual, in that 0 means the niceness cannot be decreased beyond the current value, but it can be increased.
// So effectively: if rs.Soft == 0 { rs.Soft = rs.Used }
rs.Used = uint64(nice)
case RLIMIT_RTPRIO:
rs.Used = uint64(rtprio)
}
}
return rlimits, err
} }
// IOCounters returns IO Counters. // IOCounters returns IO Counters.
func (p *Process) IOCounters() (*IOCountersStat, error) { func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.IOCountersWithContext(context.Background())
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
return p.fillFromIO() return p.fillFromIO()
} }
// NumCtxSwitches returns the number of the context switches of the process. // NumCtxSwitches returns the number of the context switches of the process.
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.NumCtxSwitchesWithContext(context.Background())
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
err := p.fillFromStatus() err := p.fillFromStatus()
if err != nil { if err != nil {
return nil, err return nil, err
@ -219,12 +361,20 @@ func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
// NumFDs returns the number of File Descriptors used by the process. // NumFDs returns the number of File Descriptors used by the process.
func (p *Process) NumFDs() (int32, error) { func (p *Process) NumFDs() (int32, error) {
numFds, _, err := p.fillFromfd() return p.NumFDsWithContext(context.Background())
return numFds, err }
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
_, fnames, err := p.fillFromfdList()
return int32(len(fnames)), err
} }
// NumThreads returns the number of threads used by the process. // NumThreads returns the number of threads used by the process.
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
return p.NumThreadsWithContext(context.Background())
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
err := p.fillFromStatus() err := p.fillFromStatus()
if err != nil { if err != nil {
return 0, err return 0, err
@ -232,17 +382,37 @@ func (p *Process) NumThreads() (int32, error) {
return p.numThreads, nil return p.numThreads, nil
} }
// Threads returns a map of threads func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
// return p.ThreadsWithContext(context.Background())
// Notice: Not implemented yet. always returns empty map. }
func (p *Process) Threads() (map[string]string, error) {
ret := make(map[string]string, 0) func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
ret := make(map[int32]*cpu.TimesStat)
taskPath := common.HostProc(strconv.Itoa(int(p.Pid)), "task")
tids, err := readPidsFromDir(taskPath)
if err != nil {
return nil, err
}
for _, tid := range tids {
_, _, cpuTimes, _, _, _, err := p.fillFromTIDStat(tid)
if err != nil {
return nil, err
}
ret[tid] = cpuTimes
}
return ret, nil return ret, nil
} }
// Times returns CPU times of the process. // Times returns CPU times of the process.
func (p *Process) Times() (*cpu.TimesStat, error) { func (p *Process) Times() (*cpu.TimesStat, error) {
_, _, cpuTimes, _, _, err := p.fillFromStat() return p.TimesWithContext(context.Background())
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
_, _, cpuTimes, _, _, _, err := p.fillFromStat()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -253,11 +423,19 @@ func (p *Process) Times() (*cpu.TimesStat, error) {
// //
// Notice: Not implemented yet. // Notice: Not implemented yet.
func (p *Process) CPUAffinity() ([]int32, error) { func (p *Process) CPUAffinity() ([]int32, error) {
return p.CPUAffinityWithContext(context.Background())
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
// MemoryInfo returns platform in-dependend memory information, such as RSS, VMS and Swap // MemoryInfo returns platform in-dependend memory information, such as RSS, VMS and Swap
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return p.MemoryInfoWithContext(context.Background())
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
meminfo, _, err := p.fillFromStatm() meminfo, _, err := p.fillFromStatm()
if err != nil { if err != nil {
return nil, err return nil, err
@ -267,6 +445,10 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
// MemoryInfoEx returns platform dependend memory information. // MemoryInfoEx returns platform dependend memory information.
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return p.MemoryInfoExWithContext(context.Background())
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
_, memInfoEx, err := p.fillFromStatm() _, memInfoEx, err := p.fillFromStatm()
if err != nil { if err != nil {
return nil, err return nil, err
@ -276,7 +458,11 @@ func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
// Children returns a slice of Process of the process. // Children returns a slice of Process of the process.
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
pids, err := common.CallPgrep(invoke, p.Pid) return p.ChildrenWithContext(context.Background())
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil { if err != nil {
if pids == nil || len(pids) == 0 { if pids == nil || len(pids) == 0 {
return nil, ErrorNoChildren return nil, ErrorNoChildren
@ -297,6 +483,10 @@ func (p *Process) Children() ([]*Process, error) {
// OpenFiles returns a slice of OpenFilesStat opend by the process. // OpenFiles returns a slice of OpenFilesStat opend by the process.
// OpenFilesStat includes a file path and file descriptor. // OpenFilesStat includes a file path and file descriptor.
func (p *Process) OpenFiles() ([]OpenFilesStat, error) { func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return p.OpenFilesWithContext(context.Background())
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
_, ofs, err := p.fillFromfd() _, ofs, err := p.fillFromfd()
if err != nil { if err != nil {
return nil, err return nil, err
@ -312,11 +502,19 @@ func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
// Connections returns a slice of net.ConnectionStat used by the process. // Connections returns a slice of net.ConnectionStat used by the process.
// This returns all kind of the connection. This measn TCP, UDP or UNIX. // This returns all kind of the connection. This measn TCP, UDP or UNIX.
func (p *Process) Connections() ([]net.ConnectionStat, error) { func (p *Process) Connections() ([]net.ConnectionStat, error) {
return p.ConnectionsWithContext(context.Background())
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return net.ConnectionsPid("all", p.Pid) return net.ConnectionsPid("all", p.Pid)
} }
// NetIOCounters returns NetIOCounters of the process. // NetIOCounters returns NetIOCounters of the process.
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
return p.NetIOCountersWithContext(context.Background(), pernic)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev") filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev")
return net.IOCountersByFile(pernic, filename) return net.IOCountersByFile(pernic, filename)
} }
@ -324,11 +522,19 @@ func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
// IsRunning returns whether the process is running or not. // IsRunning returns whether the process is running or not.
// Not implemented yet. // Not implemented yet.
func (p *Process) IsRunning() (bool, error) { func (p *Process) IsRunning() (bool, error) {
return p.IsRunningWithContext(context.Background())
}
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
return true, common.ErrNotImplementedError return true, common.ErrNotImplementedError
} }
// MemoryMaps get memory maps from /proc/(pid)/smaps // MemoryMaps get memory maps from /proc/(pid)/smaps
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return p.MemoryMapsWithContext(context.Background(), grouped)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
pid := p.Pid pid := p.Pid
var ret []MemoryMapsStat var ret []MemoryMapsStat
smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps") smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps")
@ -423,6 +629,10 @@ func limitToInt(val string) (int32, error) {
// Get num_fds from /proc/(pid)/limits // Get num_fds from /proc/(pid)/limits
func (p *Process) fillFromLimits() ([]RlimitStat, error) { func (p *Process) fillFromLimits() ([]RlimitStat, error) {
return p.fillFromLimitsWithContext(context.Background())
}
func (p *Process) fillFromLimitsWithContext(ctx context.Context) ([]RlimitStat, error) {
pid := p.Pid pid := p.Pid
limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits") limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits")
d, err := os.Open(limitsFile) d, err := os.Open(limitsFile)
@ -514,16 +724,33 @@ func (p *Process) fillFromLimits() ([]RlimitStat, error) {
return limitStats, nil return limitStats, nil
} }
// Get num_fds from /proc/(pid)/fd // Get list of /proc/(pid)/fd files
func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) { func (p *Process) fillFromfdList() (string, []string, error) {
return p.fillFromfdListWithContext(context.Background())
}
func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) {
pid := p.Pid pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "fd") statPath := common.HostProc(strconv.Itoa(int(pid)), "fd")
d, err := os.Open(statPath) d, err := os.Open(statPath)
if err != nil { if err != nil {
return 0, nil, err return statPath, []string{}, err
} }
defer d.Close() defer d.Close()
fnames, err := d.Readdirnames(-1) fnames, err := d.Readdirnames(-1)
return statPath, fnames, err
}
// Get num_fds from /proc/(pid)/fd
func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) {
return p.fillFromfdWithContext(context.Background())
}
func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) {
statPath, fnames, err := p.fillFromfdList()
if err != nil {
return 0, nil, err
}
numFDs := int32(len(fnames)) numFDs := int32(len(fnames))
var openfiles []*OpenFilesStat var openfiles []*OpenFilesStat
@ -549,6 +776,10 @@ func (p *Process) fillFromfd() (int32, []*OpenFilesStat, error) {
// Get cwd from /proc/(pid)/cwd // Get cwd from /proc/(pid)/cwd
func (p *Process) fillFromCwd() (string, error) { func (p *Process) fillFromCwd() (string, error) {
return p.fillFromCwdWithContext(context.Background())
}
func (p *Process) fillFromCwdWithContext(ctx context.Context) (string, error) {
pid := p.Pid pid := p.Pid
cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd") cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd")
cwd, err := os.Readlink(cwdPath) cwd, err := os.Readlink(cwdPath)
@ -560,6 +791,10 @@ func (p *Process) fillFromCwd() (string, error) {
// Get exe from /proc/(pid)/exe // Get exe from /proc/(pid)/exe
func (p *Process) fillFromExe() (string, error) { func (p *Process) fillFromExe() (string, error) {
return p.fillFromExeWithContext(context.Background())
}
func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) {
pid := p.Pid pid := p.Pid
exePath := common.HostProc(strconv.Itoa(int(pid)), "exe") exePath := common.HostProc(strconv.Itoa(int(pid)), "exe")
exe, err := os.Readlink(exePath) exe, err := os.Readlink(exePath)
@ -571,6 +806,10 @@ func (p *Process) fillFromExe() (string, error) {
// Get cmdline from /proc/(pid)/cmdline // Get cmdline from /proc/(pid)/cmdline
func (p *Process) fillFromCmdline() (string, error) { func (p *Process) fillFromCmdline() (string, error) {
return p.fillFromCmdlineWithContext(context.Background())
}
func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) {
pid := p.Pid pid := p.Pid
cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
cmdline, err := ioutil.ReadFile(cmdPath) cmdline, err := ioutil.ReadFile(cmdPath)
@ -588,6 +827,10 @@ func (p *Process) fillFromCmdline() (string, error) {
} }
func (p *Process) fillSliceFromCmdline() ([]string, error) { func (p *Process) fillSliceFromCmdline() ([]string, error) {
return p.fillSliceFromCmdlineWithContext(context.Background())
}
func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) {
pid := p.Pid pid := p.Pid
cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
cmdline, err := ioutil.ReadFile(cmdPath) cmdline, err := ioutil.ReadFile(cmdPath)
@ -611,6 +854,10 @@ func (p *Process) fillSliceFromCmdline() ([]string, error) {
// Get IO status from /proc/(pid)/io // Get IO status from /proc/(pid)/io
func (p *Process) fillFromIO() (*IOCountersStat, error) { func (p *Process) fillFromIO() (*IOCountersStat, error) {
return p.fillFromIOWithContext(context.Background())
}
func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) {
pid := p.Pid pid := p.Pid
ioPath := common.HostProc(strconv.Itoa(int(pid)), "io") ioPath := common.HostProc(strconv.Itoa(int(pid)), "io")
ioline, err := ioutil.ReadFile(ioPath) ioline, err := ioutil.ReadFile(ioPath)
@ -650,6 +897,10 @@ func (p *Process) fillFromIO() (*IOCountersStat, error) {
// Get memory info from /proc/(pid)/statm // Get memory info from /proc/(pid)/statm
func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) { func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) {
return p.fillFromStatmWithContext(context.Background())
}
func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) {
pid := p.Pid pid := p.Pid
memPath := common.HostProc(strconv.Itoa(int(pid)), "statm") memPath := common.HostProc(strconv.Itoa(int(pid)), "statm")
contents, err := ioutil.ReadFile(memPath) contents, err := ioutil.ReadFile(memPath)
@ -702,6 +953,10 @@ func (p *Process) fillFromStatm() (*MemoryInfoStat, *MemoryInfoExStat, error) {
// Get various status from /proc/(pid)/status // Get various status from /proc/(pid)/status
func (p *Process) fillFromStatus() error { func (p *Process) fillFromStatus() error {
return p.fillFromStatusWithContext(context.Background())
}
func (p *Process) fillFromStatusWithContext(ctx context.Context) error {
pid := p.Pid pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "status") statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
contents, err := ioutil.ReadFile(statPath) contents, err := ioutil.ReadFile(statPath)
@ -711,6 +966,7 @@ func (p *Process) fillFromStatus() error {
lines := strings.Split(string(contents), "\n") lines := strings.Split(string(contents), "\n")
p.numCtxSwitches = &NumCtxSwitchesStat{} p.numCtxSwitches = &NumCtxSwitchesStat{}
p.memInfo = &MemoryInfoStat{} p.memInfo = &MemoryInfoStat{}
p.sigInfo = &SignalInfoStat{}
for _, line := range lines { for _, line := range lines {
tabParts := strings.SplitN(line, "\t", 2) tabParts := strings.SplitN(line, "\t", 2)
if len(tabParts) < 2 { if len(tabParts) < 2 {
@ -729,6 +985,8 @@ func (p *Process) fillFromStatus() error {
extendedName := filepath.Base(cmdlineSlice[0]) extendedName := filepath.Base(cmdlineSlice[0])
if strings.HasPrefix(extendedName, p.name) { if strings.HasPrefix(extendedName, p.name) {
p.name = extendedName p.name = extendedName
} else {
p.name = cmdlineSlice[0]
} }
} }
} }
@ -740,6 +998,12 @@ func (p *Process) fillFromStatus() error {
return err return err
} }
p.parent = int32(pval) p.parent = int32(pval)
case "Tgid":
pval, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
p.tgid = int32(pval)
case "Uid": case "Uid":
p.uids = make([]int32, 0, 4) p.uids = make([]int32, 0, 4)
for _, i := range strings.Split(value, "\t") { for _, i := range strings.Split(value, "\t") {
@ -797,18 +1061,80 @@ func (p *Process) fillFromStatus() error {
return err return err
} }
p.memInfo.Swap = v * 1024 p.memInfo.Swap = v * 1024
case "VmData":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Data = v * 1024
case "VmStk":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Stack = v * 1024
case "VmLck":
value := strings.Trim(value, " kB") // remove last "kB"
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return err
}
p.memInfo.Locked = v * 1024
case "SigPnd":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.PendingThread = v
case "ShdPnd":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.PendingProcess = v
case "SigBlk":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Blocked = v
case "SigIgn":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Ignored = v
case "SigCgt":
v, err := strconv.ParseUint(value, 16, 64)
if err != nil {
return err
}
p.sigInfo.Caught = v
} }
} }
return nil return nil
} }
func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, error) { func (p *Process) fillFromTIDStat(tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) {
return p.fillFromTIDStatWithContext(context.Background(), tid)
}
func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) {
pid := p.Pid pid := p.Pid
statPath := common.HostProc(strconv.Itoa(int(pid)), "stat") var statPath string
if tid == -1 {
statPath = common.HostProc(strconv.Itoa(int(pid)), "stat")
} else {
statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat")
}
contents, err := ioutil.ReadFile(statPath) contents, err := ioutil.ReadFile(statPath)
if err != nil { if err != nil {
return "", 0, nil, 0, 0, err return 0, 0, nil, 0, 0, 0, err
} }
fields := strings.Fields(string(contents)) fields := strings.Fields(string(contents))
@ -817,28 +1143,23 @@ func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, e
i++ i++
} }
termmap, err := getTerminalMap() terminal, err := strconv.ParseUint(fields[i+5], 10, 64)
terminal := "" if err != nil {
if err == nil { return 0, 0, nil, 0, 0, 0, err
t, err := strconv.ParseUint(fields[i+5], 10, 64)
if err != nil {
return "", 0, nil, 0, 0, err
}
terminal = termmap[t]
} }
ppid, err := strconv.ParseInt(fields[i+2], 10, 32) ppid, err := strconv.ParseInt(fields[i+2], 10, 32)
if err != nil { if err != nil {
return "", 0, nil, 0, 0, err return 0, 0, nil, 0, 0, 0, err
} }
utime, err := strconv.ParseFloat(fields[i+12], 64) utime, err := strconv.ParseFloat(fields[i+12], 64)
if err != nil { if err != nil {
return "", 0, nil, 0, 0, err return 0, 0, nil, 0, 0, 0, err
} }
stime, err := strconv.ParseFloat(fields[i+13], 64) stime, err := strconv.ParseFloat(fields[i+13], 64)
if err != nil { if err != nil {
return "", 0, nil, 0, 0, err return 0, 0, nil, 0, 0, 0, err
} }
cpuTimes := &cpu.TimesStat{ cpuTimes := &cpu.TimesStat{
@ -850,24 +1171,75 @@ func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, int32, e
bootTime, _ := host.BootTime() bootTime, _ := host.BootTime()
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, nil, 0, 0, err return 0, 0, nil, 0, 0, 0, err
} }
ctime := (t / uint64(ClockTicks)) + uint64(bootTime) ctime := (t / uint64(ClockTicks)) + uint64(bootTime)
createTime := int64(ctime * 1000) createTime := int64(ctime * 1000)
rtpriority, err := strconv.ParseInt(fields[i+16], 10, 32)
if err != nil {
return 0, 0, nil, 0, 0, 0, err
}
if rtpriority < 0 {
rtpriority = rtpriority*-1 - 1
} else {
rtpriority = 0
}
// p.Nice = mustParseInt32(fields[18]) // p.Nice = mustParseInt32(fields[18])
// use syscall instead of parse Stat file // use syscall instead of parse Stat file
snice, _ := syscall.Getpriority(PrioProcess, int(pid)) snice, _ := unix.Getpriority(PrioProcess, int(pid))
nice := int32(snice) // FIXME: is this true? nice := int32(snice) // FIXME: is this true?
return terminal, int32(ppid), cpuTimes, createTime, nice, nil return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, nil
}
func (p *Process) fillFromStat() (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) {
return p.fillFromStatWithContext(context.Background())
}
func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) {
return p.fillFromTIDStat(-1)
} }
// Pids returns a slice of process ID list which are running now. // Pids returns a slice of process ID list which are running now.
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
return readPidsFromDir(common.HostProc())
}
// Process returns a slice of pointers to Process structs for all
// currently running processes.
func Processes() ([]*Process, error) {
return ProcessesWithContext(context.Background())
}
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
out := []*Process{}
pids, err := PidsWithContext(ctx)
if err != nil {
return out, err
}
for _, pid := range pids {
p, err := NewProcess(pid)
if err != nil {
continue
}
out = append(out, p)
}
return out, nil
}
func readPidsFromDir(path string) ([]int32, error) {
var ret []int32 var ret []int32
d, err := os.Open(common.HostProc()) d, err := os.Open(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,15 +5,16 @@ package process
import ( import (
"C" "C"
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"strings" "strings"
"syscall"
"unsafe" "unsafe"
cpu "github.com/shirou/gopsutil/cpu" cpu "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
mem "github.com/shirou/gopsutil/mem" mem "github.com/shirou/gopsutil/mem"
net "github.com/shirou/gopsutil/net" net "github.com/shirou/gopsutil/net"
"golang.org/x/sys/unix"
) )
// MemoryInfoExStat is different between OSes // MemoryInfoExStat is different between OSes
@ -24,8 +25,12 @@ type MemoryMapsStat struct {
} }
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
var ret []int32 var ret []int32
procs, err := processes() procs, err := Processes()
if err != nil { if err != nil {
return ret, nil return ret, nil
} }
@ -38,6 +43,10 @@ func Pids() ([]int32, error) {
} }
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
return p.PpidWithContext(context.Background())
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return 0, err return 0, err
@ -46,6 +55,10 @@ func (p *Process) Ppid() (int32, error) {
return k.Ppid, nil return k.Ppid, nil
} }
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
return p.NameWithContext(context.Background())
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -53,11 +66,22 @@ func (p *Process) Name() (string, error) {
return common.IntToString(k.Comm[:]), nil return common.IntToString(k.Comm[:]), nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.ExeWithContext(context.Background())
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) CmdlineSlice() ([]string, error) { func (p *Process) CmdlineSlice() ([]string, error) {
return p.CmdlineSliceWithContext(context.Background())
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv} mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv}
buf, _, err := common.CallSyscall(mib) buf, _, err := common.CallSyscall(mib)
@ -81,6 +105,10 @@ func (p *Process) CmdlineSlice() ([]string, error) {
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
return p.CmdlineWithContext(context.Background())
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
argv, err := p.CmdlineSlice() argv, err := p.CmdlineSlice()
if err != nil { if err != nil {
return "", err return "", err
@ -89,15 +117,31 @@ func (p *Process) Cmdline() (string, error) {
} }
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
return p.CreateTimeWithContext(context.Background())
}
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return p.CwdWithContext(context.Background())
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Parent() (*Process, error) { func (p *Process) Parent() (*Process, error) {
return p.ParentWithContext(context.Background())
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
return p, common.ErrNotImplementedError return p, common.ErrNotImplementedError
} }
func (p *Process) Status() (string, error) { func (p *Process) Status() (string, error) {
return p.StatusWithContext(context.Background())
}
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -119,6 +163,10 @@ func (p *Process) Status() (string, error) {
return s, nil return s, nil
} }
func (p *Process) Uids() ([]int32, error) { func (p *Process) Uids() ([]int32, error) {
return p.UidsWithContext(context.Background())
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -131,6 +179,10 @@ func (p *Process) Uids() ([]int32, error) {
return uids, nil return uids, nil
} }
func (p *Process) Gids() ([]int32, error) { func (p *Process) Gids() ([]int32, error) {
return p.GidsWithContext(context.Background())
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -142,6 +194,10 @@ func (p *Process) Gids() ([]int32, error) {
return gids, nil return gids, nil
} }
func (p *Process) Terminal() (string, error) { func (p *Process) Terminal() (string, error) {
return p.TerminalWithContext(context.Background())
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return "", err return "", err
@ -157,6 +213,10 @@ func (p *Process) Terminal() (string, error) {
return termmap[ttyNr], nil return termmap[ttyNr], nil
} }
func (p *Process) Nice() (int32, error) { func (p *Process) Nice() (int32, error) {
return p.NiceWithContext(context.Background())
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return 0, err return 0, err
@ -164,13 +224,33 @@ func (p *Process) Nice() (int32, error) {
return int32(k.Nice), nil return int32(k.Nice), nil
} }
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Rlimit() ([]RlimitStat, error) { func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.RlimitWithContext(context.Background())
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
var rlimit []RlimitStat var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError return rlimit, common.ErrNotImplementedError
} }
func (p *Process) IOCounters() (*IOCountersStat, error) { func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.IOCountersWithContext(context.Background())
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -181,20 +261,40 @@ func (p *Process) IOCounters() (*IOCountersStat, error) {
}, nil }, nil
} }
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.NumCtxSwitchesWithContext(context.Background())
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumFDs() (int32, error) { func (p *Process) NumFDs() (int32, error) {
return p.NumFDsWithContext(context.Background())
}
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
return p.NumThreadsWithContext(context.Background())
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
/* not supported, just return 1 */ /* not supported, just return 1 */
return 1, nil return 1, nil
} }
func (p *Process) Threads() (map[string]string, error) { func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
ret := make(map[string]string, 0) return p.ThreadsWithContext(context.Background())
}
func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
ret := make(map[int32]*cpu.TimesStat)
return ret, common.ErrNotImplementedError return ret, common.ErrNotImplementedError
} }
func (p *Process) Times() (*cpu.TimesStat, error) { func (p *Process) Times() (*cpu.TimesStat, error) {
return p.TimesWithContext(context.Background())
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -206,9 +306,17 @@ func (p *Process) Times() (*cpu.TimesStat, error) {
}, nil }, nil
} }
func (p *Process) CPUAffinity() ([]int32, error) { func (p *Process) CPUAffinity() ([]int32, error) {
return p.CPUAffinityWithContext(context.Background())
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return p.MemoryInfoWithContext(context.Background())
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
k, err := p.getKProc() k, err := p.getKProc()
if err != nil { if err != nil {
return nil, err return nil, err
@ -225,11 +333,19 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
}, nil }, nil
} }
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return p.MemoryInfoExWithContext(context.Background())
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
pids, err := common.CallPgrep(invoke, p.Pid) return p.ChildrenWithContext(context.Background())
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -245,27 +361,51 @@ func (p *Process) Children() ([]*Process, error) {
} }
func (p *Process) OpenFiles() ([]OpenFilesStat, error) { func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return p.OpenFilesWithContext(context.Background())
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Connections() ([]net.ConnectionStat, error) { func (p *Process) Connections() ([]net.ConnectionStat, error) {
return p.ConnectionsWithContext(context.Background())
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
return p.NetIOCountersWithContext(context.Background(), pernic)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) IsRunning() (bool, error) { func (p *Process) IsRunning() (bool, error) {
return p.IsRunningWithContext(context.Background())
}
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
return true, common.ErrNotImplementedError return true, common.ErrNotImplementedError
} }
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return p.MemoryMapsWithContext(context.Background(), grouped)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
var ret []MemoryMapsStat var ret []MemoryMapsStat
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
func processes() ([]Process, error) { func Processes() ([]*Process, error) {
results := make([]Process, 0, 50) return ProcessesWithContext(context.Background())
}
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
results := []*Process{}
buf, length, err := CallKernProcSyscall(KernProcAll, 0) buf, length, err := CallKernProcSyscall(KernProcAll, 0)
@ -288,7 +428,7 @@ func processes() ([]Process, error) {
continue continue
} }
results = append(results, *p) results = append(results, p)
} }
return results, nil return results, nil
@ -302,6 +442,10 @@ func parseKinfoProc(buf []byte) (KinfoProc, error) {
} }
func (p *Process) getKProc() (*KinfoProc, error) { func (p *Process) getKProc() (*KinfoProc, error) {
return p.getKProcWithContext(context.Background())
}
func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) {
buf, length, err := CallKernProcSyscall(KernProcPID, p.Pid) buf, length, err := CallKernProcSyscall(KernProcPID, p.Pid)
if err != nil { if err != nil {
return nil, err return nil, err
@ -324,12 +468,16 @@ func NewProcess(pid int32) (*Process, error) {
} }
func CallKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) { func CallKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) {
return CallKernProcSyscallWithContext(context.Background(), op, arg)
}
func CallKernProcSyscallWithContext(ctx context.Context, op int32, arg int32) ([]byte, uint64, error) {
mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0} mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0}
mibptr := unsafe.Pointer(&mib[0]) mibptr := unsafe.Pointer(&mib[0])
miblen := uint64(len(mib)) miblen := uint64(len(mib))
length := uint64(0) length := uint64(0)
_, _, err := syscall.Syscall6( _, _, err := unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(mibptr), uintptr(mibptr),
uintptr(miblen), uintptr(miblen),
0, 0,
@ -346,8 +494,8 @@ func CallKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) {
miblen = uint64(len(mib)) miblen = uint64(len(mib))
// get proc info itself // get proc info itself
buf := make([]byte, length) buf := make([]byte, length)
_, _, err = syscall.Syscall6( _, _, err = unix.Syscall6(
syscall.SYS___SYSCTL, unix.SYS___SYSCTL,
uintptr(mibptr), uintptr(mibptr),
uintptr(miblen), uintptr(miblen),
uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&buf[0])),

View File

@ -3,15 +3,15 @@
package process package process
import ( import (
"context"
"os" "os"
"os/exec"
"os/user" "os/user"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"github.com/shirou/gopsutil/internal/common" "golang.org/x/sys/unix"
) )
// POSIX // POSIX
@ -26,6 +26,9 @@ func getTerminalMap() (map[uint64]string, error) {
defer d.Close() defer d.Close()
devnames, err := d.Readdirnames(-1) devnames, err := d.Readdirnames(-1)
if err != nil {
return nil, err
}
for _, devname := range devnames { for _, devname := range devnames {
if strings.HasPrefix(devname, "/dev/tty") { if strings.HasPrefix(devname, "/dev/tty") {
termfiles = append(termfiles, "/dev/tty/"+devname) termfiles = append(termfiles, "/dev/tty/"+devname)
@ -40,9 +43,14 @@ func getTerminalMap() (map[uint64]string, error) {
return nil, err return nil, err
} }
} }
defer ptsd.Close()
if ptsnames == nil { if ptsnames == nil {
defer ptsd.Close() defer ptsd.Close()
ptsnames, err = ptsd.Readdirnames(-1) ptsnames, err = ptsd.Readdirnames(-1)
if err != nil {
return nil, err
}
for _, ptsname := range ptsnames { for _, ptsname := range ptsnames {
termfiles = append(termfiles, "/dev/pts/"+ptsname) termfiles = append(termfiles, "/dev/pts/"+ptsname)
} }
@ -51,8 +59,8 @@ func getTerminalMap() (map[uint64]string, error) {
} }
for _, name := range termfiles { for _, name := range termfiles {
stat := syscall.Stat_t{} stat := unix.Stat_t{}
if err = syscall.Stat(name, &stat); err != nil { if err = unix.Stat(name, &stat); err != nil {
return nil, err return nil, err
} }
rdev := uint64(stat.Rdev) rdev := uint64(stat.Rdev)
@ -61,31 +69,19 @@ func getTerminalMap() (map[uint64]string, error) {
return ret, nil return ret, nil
} }
// SendSignal sends a syscall.Signal to the process. // SendSignal sends a unix.Signal to the process.
// Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported. // Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported.
func (p *Process) SendSignal(sig syscall.Signal) error { func (p *Process) SendSignal(sig syscall.Signal) error {
sigAsStr := "INT" return p.SendSignalWithContext(context.Background(), sig)
switch sig { }
case syscall.SIGSTOP:
sigAsStr = "STOP"
case syscall.SIGCONT:
sigAsStr = "CONT"
case syscall.SIGTERM:
sigAsStr = "TERM"
case syscall.SIGKILL:
sigAsStr = "KILL"
}
kill, err := exec.LookPath("kill") func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error {
process, err := os.FindProcess(int(p.Pid))
if err != nil { if err != nil {
return err return err
} }
cmd := exec.Command(kill, "-s", sigAsStr, strconv.Itoa(int(p.Pid)))
cmd.Stderr = os.Stderr err = process.Signal(sig)
if err := cmd.Start(); err != nil {
return err
}
err = common.WaitTimeout(cmd, common.Timeout)
if err != nil { if err != nil {
return err return err
} }
@ -95,26 +91,46 @@ func (p *Process) SendSignal(sig syscall.Signal) error {
// Suspend sends SIGSTOP to the process. // Suspend sends SIGSTOP to the process.
func (p *Process) Suspend() error { func (p *Process) Suspend() error {
return p.SendSignal(syscall.SIGSTOP) return p.SuspendWithContext(context.Background())
}
func (p *Process) SuspendWithContext(ctx context.Context) error {
return p.SendSignal(unix.SIGSTOP)
} }
// Resume sends SIGCONT to the process. // Resume sends SIGCONT to the process.
func (p *Process) Resume() error { func (p *Process) Resume() error {
return p.SendSignal(syscall.SIGCONT) return p.ResumeWithContext(context.Background())
}
func (p *Process) ResumeWithContext(ctx context.Context) error {
return p.SendSignal(unix.SIGCONT)
} }
// Terminate sends SIGTERM to the process. // Terminate sends SIGTERM to the process.
func (p *Process) Terminate() error { func (p *Process) Terminate() error {
return p.SendSignal(syscall.SIGTERM) return p.TerminateWithContext(context.Background())
}
func (p *Process) TerminateWithContext(ctx context.Context) error {
return p.SendSignal(unix.SIGTERM)
} }
// Kill sends SIGKILL to the process. // Kill sends SIGKILL to the process.
func (p *Process) Kill() error { func (p *Process) Kill() error {
return p.SendSignal(syscall.SIGKILL) return p.KillWithContext(context.Background())
}
func (p *Process) KillWithContext(ctx context.Context) error {
return p.SendSignal(unix.SIGKILL)
} }
// Username returns a username of the process. // Username returns a username of the process.
func (p *Process) Username() (string, error) { func (p *Process) Username() (string, error) {
return p.UsernameWithContext(context.Background())
}
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
uids, err := p.Uids() uids, err := p.Uids()
if err != nil { if err != nil {
return "", err return "", err

View File

@ -3,19 +3,20 @@
package process package process
import ( import (
"errors" "context"
"fmt" "fmt"
"os"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
"github.com/StackExchange/wmi" "github.com/StackExchange/wmi"
"github.com/shirou/w32"
cpu "github.com/shirou/gopsutil/cpu" cpu "github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/internal/common" "github.com/shirou/gopsutil/internal/common"
net "github.com/shirou/gopsutil/net" net "github.com/shirou/gopsutil/net"
"github.com/shirou/w32"
"golang.org/x/sys/windows"
) )
const ( const (
@ -24,7 +25,7 @@ const (
) )
var ( var (
modpsapi = syscall.NewLazyDLL("psapi.dll") modpsapi = windows.NewLazySystemDLL("psapi.dll")
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
) )
@ -51,99 +52,143 @@ type MemoryMapsStat struct {
} }
type Win32_Process struct { type Win32_Process struct {
Name string Name string
ExecutablePath *string ExecutablePath *string
CommandLine *string CommandLine *string
Priority uint32 Priority uint32
CreationDate *time.Time CreationDate *time.Time
ProcessID uint32 ProcessID uint32
ThreadCount uint32 ThreadCount uint32
Status *string Status *string
ReadOperationCount uint64 ReadOperationCount uint64
ReadTransferCount uint64 ReadTransferCount uint64
WriteOperationCount uint64 WriteOperationCount uint64
WriteTransferCount uint64 WriteTransferCount uint64
CSCreationClassName string
CSName string
Caption *string
CreationClassName string
Description *string
ExecutionState *uint16
HandleCount uint32
KernelModeTime uint64
MaximumWorkingSetSize *uint32
MinimumWorkingSetSize *uint32
OSCreationClassName string
OSName string
OtherOperationCount uint64
OtherTransferCount uint64
PageFaults uint32
PageFileUsage uint32
ParentProcessID uint32
PeakPageFileUsage uint32
PeakVirtualSize uint64
PeakWorkingSetSize uint32
PrivatePageCount uint64
TerminationDate *time.Time
UserModeTime uint64
WorkingSetSize uint64
}
/* func init() {
CSCreationClassName string wmi.DefaultClient.AllowMissingFields = true
CSName string
Caption *string
CreationClassName string
Description *string
ExecutionState *uint16
HandleCount uint32
KernelModeTime uint64
MaximumWorkingSetSize *uint32
MinimumWorkingSetSize *uint32
OSCreationClassName string
OSName string
OtherOperationCount uint64
OtherTransferCount uint64
PageFaults uint32
PageFileUsage uint32
ParentProcessID uint32
PeakPageFileUsage uint32
PeakVirtualSize uint64
PeakWorkingSetSize uint32
PrivatePageCount uint64
TerminationDate *time.Time
UserModeTime uint64
WorkingSetSize uint64
*/
} }
func Pids() ([]int32, error) { func Pids() ([]int32, error) {
return PidsWithContext(context.Background())
}
func PidsWithContext(ctx context.Context) ([]int32, error) {
// inspired by https://gist.github.com/henkman/3083408
// and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329
var ret []int32 var ret []int32
var read uint32 = 0
var psSize uint32 = 1024
const dwordSize uint32 = 4
procs, err := processes() for {
if err != nil { ps := make([]uint32, psSize)
if !w32.EnumProcesses(ps, uint32(len(ps)), &read) {
return nil, fmt.Errorf("could not get w32.EnumProcesses")
}
if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one
psSize += 1024
continue
}
for _, pid := range ps[:read/dwordSize] {
ret = append(ret, int32(pid))
}
return ret, nil return ret, nil
} }
for _, proc := range procs {
ret = append(ret, proc.Pid)
}
return ret, nil
} }
func (p *Process) Ppid() (int32, error) { func (p *Process) Ppid() (int32, error) {
ret, _, _, err := p.getFromSnapProcess(p.Pid) return p.PpidWithContext(context.Background())
}
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
ppid, _, _, err := getFromSnapProcess(p.Pid)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return ret, nil return ppid, nil
} }
func GetWin32Proc(pid int32) ([]Win32_Process, error) { func GetWin32Proc(pid int32) ([]Win32_Process, error) {
return GetWin32ProcWithContext(context.Background(), pid)
}
func GetWin32ProcWithContext(ctx context.Context, pid int32) ([]Win32_Process, error) {
var dst []Win32_Process var dst []Win32_Process
query := fmt.Sprintf("WHERE ProcessId = %d", pid) query := fmt.Sprintf("WHERE ProcessId = %d", pid)
q := wmi.CreateQuery(&dst, query) q := wmi.CreateQuery(&dst, query)
err := wmi.Query(q, &dst) err := common.WMIQueryWithContext(ctx, q, &dst)
if err != nil { if err != nil {
return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err) return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err)
} }
if len(dst) != 1 {
if len(dst) == 0 {
return []Win32_Process{}, fmt.Errorf("could not get win32Proc: empty") return []Win32_Process{}, fmt.Errorf("could not get win32Proc: empty")
} }
return dst, nil return dst, nil
} }
func (p *Process) Name() (string, error) { func (p *Process) Name() (string, error) {
dst, err := GetWin32Proc(p.Pid) return p.NameWithContext(context.Background())
}
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
_, _, name, err := getFromSnapProcess(p.Pid)
if err != nil { if err != nil {
return "", fmt.Errorf("could not get Name: %s", err) return "", fmt.Errorf("could not get Name: %s", err)
} }
return dst[0].Name, nil return name, nil
} }
func (p *Process) Tgid() (int32, error) {
return 0, common.ErrNotImplementedError
}
func (p *Process) Exe() (string, error) { func (p *Process) Exe() (string, error) {
return p.ExeWithContext(context.Background())
}
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
dst, err := GetWin32Proc(p.Pid) dst, err := GetWin32Proc(p.Pid)
if err != nil { if err != nil {
return "", fmt.Errorf("could not get ExecutablePath: %s", err) return "", fmt.Errorf("could not get ExecutablePath: %s", err)
} }
return *dst[0].ExecutablePath, nil return *dst[0].ExecutablePath, nil
} }
func (p *Process) Cmdline() (string, error) { func (p *Process) Cmdline() (string, error) {
return p.CmdlineWithContext(context.Background())
}
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
dst, err := GetWin32Proc(p.Pid) dst, err := GetWin32Proc(p.Pid)
if err != nil { if err != nil {
return "", fmt.Errorf("could not get CommandLine: %s", err) return "", fmt.Errorf("could not get CommandLine: %s", err)
@ -155,6 +200,10 @@ func (p *Process) Cmdline() (string, error) {
// element being an argument. This merely returns the CommandLine informations passed // element being an argument. This merely returns the CommandLine informations passed
// to the process split on the 0x20 ASCII character. // to the process split on the 0x20 ASCII character.
func (p *Process) CmdlineSlice() ([]string, error) { func (p *Process) CmdlineSlice() ([]string, error) {
return p.CmdlineSliceWithContext(context.Background())
}
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
cmdline, err := p.Cmdline() cmdline, err := p.Cmdline()
if err != nil { if err != nil {
return nil, err return nil, err
@ -163,6 +212,10 @@ func (p *Process) CmdlineSlice() ([]string, error) {
} }
func (p *Process) CreateTime() (int64, error) { func (p *Process) CreateTime() (int64, error) {
return p.CreateTimeWithContext(context.Background())
}
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
ru, err := getRusage(p.Pid) ru, err := getRusage(p.Pid)
if err != nil { if err != nil {
return 0, fmt.Errorf("could not get CreationDate: %s", err) return 0, fmt.Errorf("could not get CreationDate: %s", err)
@ -172,32 +225,90 @@ func (p *Process) CreateTime() (int64, error) {
} }
func (p *Process) Cwd() (string, error) { func (p *Process) Cwd() (string, error) {
return p.CwdWithContext(context.Background())
}
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Parent() (*Process, error) { func (p *Process) Parent() (*Process, error) {
return p, common.ErrNotImplementedError return p.ParentWithContext(context.Background())
}
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
ppid, err := p.PpidWithContext(ctx)
if err != nil {
return nil, fmt.Errorf("could not get ParentProcessID: %s", err)
}
return NewProcess(ppid)
} }
func (p *Process) Status() (string, error) { func (p *Process) Status() (string, error) {
return p.StatusWithContext(context.Background())
}
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
func (p *Process) Username() (string, error) { func (p *Process) Username() (string, error) {
return "", common.ErrNotImplementedError return p.UsernameWithContext(context.Background())
} }
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
pid := p.Pid
// 0x1000 is PROCESS_QUERY_LIMITED_INFORMATION
c, err := syscall.OpenProcess(0x1000, false, uint32(pid))
if err != nil {
return "", err
}
defer syscall.CloseHandle(c)
var token syscall.Token
err = syscall.OpenProcessToken(c, syscall.TOKEN_QUERY, &token)
if err != nil {
return "", err
}
defer token.Close()
tokenUser, err := token.GetTokenUser()
if err != nil {
return "", err
}
user, domain, _, err := tokenUser.User.Sid.LookupAccount("")
return domain + "\\" + user, err
}
func (p *Process) Uids() ([]int32, error) { func (p *Process) Uids() ([]int32, error) {
return p.UidsWithContext(context.Background())
}
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
var uids []int32 var uids []int32
return uids, common.ErrNotImplementedError return uids, common.ErrNotImplementedError
} }
func (p *Process) Gids() ([]int32, error) { func (p *Process) Gids() ([]int32, error) {
return p.GidsWithContext(context.Background())
}
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
var gids []int32 var gids []int32
return gids, common.ErrNotImplementedError return gids, common.ErrNotImplementedError
} }
func (p *Process) Terminal() (string, error) { func (p *Process) Terminal() (string, error) {
return p.TerminalWithContext(context.Background())
}
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
return "", common.ErrNotImplementedError return "", common.ErrNotImplementedError
} }
// Nice returnes 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())
}
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
dst, err := GetWin32Proc(p.Pid) dst, err := GetWin32Proc(p.Pid)
if err != nil { if err != nil {
return 0, fmt.Errorf("could not get Priority: %s", err) return 0, fmt.Errorf("could not get Priority: %s", err)
@ -205,15 +316,36 @@ func (p *Process) Nice() (int32, error) {
return int32(dst[0].Priority), nil return int32(dst[0].Priority), nil
} }
func (p *Process) IOnice() (int32, error) { func (p *Process) IOnice() (int32, error) {
return p.IOniceWithContext(context.Background())
}
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) Rlimit() ([]RlimitStat, error) { func (p *Process) Rlimit() ([]RlimitStat, error) {
return p.RlimitWithContext(context.Background())
}
func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError
}
func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) {
return p.RlimitUsageWithContext(context.Background(), gatherUsed)
}
func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
var rlimit []RlimitStat var rlimit []RlimitStat
return rlimit, common.ErrNotImplementedError return rlimit, common.ErrNotImplementedError
} }
func (p *Process) IOCounters() (*IOCountersStat, error) { func (p *Process) IOCounters() (*IOCountersStat, error) {
return p.IOCountersWithContext(context.Background())
}
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
dst, err := GetWin32Proc(p.Pid) dst, err := GetWin32Proc(p.Pid)
if err != nil || len(dst) == 0 { if err != nil || len(dst) == 0 {
return nil, fmt.Errorf("could not get Win32Proc: %s", err) return nil, fmt.Errorf("could not get Win32Proc: %s", err)
@ -228,29 +360,77 @@ func (p *Process) IOCounters() (*IOCountersStat, error) {
return ret, nil return ret, nil
} }
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
return p.NumCtxSwitchesWithContext(context.Background())
}
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NumFDs() (int32, error) { func (p *Process) NumFDs() (int32, error) {
return p.NumFDsWithContext(context.Background())
}
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
return 0, common.ErrNotImplementedError return 0, common.ErrNotImplementedError
} }
func (p *Process) NumThreads() (int32, error) { func (p *Process) NumThreads() (int32, error) {
return p.NumThreadsWithContext(context.Background())
}
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
dst, err := GetWin32Proc(p.Pid) dst, err := GetWin32Proc(p.Pid)
if err != nil { if err != nil {
return 0, fmt.Errorf("could not get ThreadCount: %s", err) return 0, fmt.Errorf("could not get ThreadCount: %s", err)
} }
return int32(dst[0].ThreadCount), nil return int32(dst[0].ThreadCount), nil
} }
func (p *Process) Threads() (map[string]string, error) { func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) {
ret := make(map[string]string, 0) return p.ThreadsWithContext(context.Background())
}
func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
ret := make(map[int32]*cpu.TimesStat)
return ret, common.ErrNotImplementedError return ret, common.ErrNotImplementedError
} }
func (p *Process) Times() (*cpu.TimesStat, error) { func (p *Process) Times() (*cpu.TimesStat, error) {
return nil, common.ErrNotImplementedError return p.TimesWithContext(context.Background())
}
func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
sysTimes, err := getProcessCPUTimes(p.Pid)
if err != nil {
return nil, err
}
// User and kernel times are represented as a FILETIME structure
// which contains a 64-bit value representing the number of
// 100-nanosecond intervals since January 1, 1601 (UTC):
// http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
// To convert it into a float representing the seconds that the
// process has executed in user/kernel mode I borrowed the code
// below from psutil's _psutil_windows.c, and in turn from Python's
// Modules/posixmodule.c
user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7
kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7
return &cpu.TimesStat{
User: user,
System: kernel,
}, nil
} }
func (p *Process) CPUAffinity() ([]int32, error) { func (p *Process) CPUAffinity() ([]int32, error) {
return p.CPUAffinityWithContext(context.Background())
}
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return p.MemoryInfoWithContext(context.Background())
}
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
mem, err := getMemoryInfo(p.Pid) mem, err := getMemoryInfo(p.Pid)
if err != nil { if err != nil {
return nil, err return nil, err
@ -264,30 +444,74 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
return ret, nil return ret, nil
} }
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
return p.MemoryInfoExWithContext(context.Background())
}
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Children() ([]*Process, error) { func (p *Process) Children() ([]*Process, error) {
return nil, common.ErrNotImplementedError return p.ChildrenWithContext(context.Background())
}
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
var dst []Win32_Process
query := wmi.CreateQuery(&dst, fmt.Sprintf("Where ParentProcessId = %d", p.Pid))
err := common.WMIQueryWithContext(ctx, query, &dst)
if err != nil {
return nil, err
}
out := []*Process{}
for _, proc := range dst {
p, err := NewProcess(int32(proc.ProcessID))
if err != nil {
continue
}
out = append(out, p)
}
return out, nil
} }
func (p *Process) OpenFiles() ([]OpenFilesStat, error) { func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
return p.OpenFilesWithContext(context.Background())
}
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) Connections() ([]net.ConnectionStat, error) { func (p *Process) Connections() ([]net.ConnectionStat, error) {
return p.ConnectionsWithContext(context.Background())
}
func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) {
return p.NetIOCountersWithContext(context.Background(), pernic)
}
func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
return nil, common.ErrNotImplementedError return nil, common.ErrNotImplementedError
} }
func (p *Process) IsRunning() (bool, error) { func (p *Process) IsRunning() (bool, error) {
return p.IsRunningWithContext(context.Background())
}
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
return true, common.ErrNotImplementedError return true, common.ErrNotImplementedError
} }
func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) {
return p.MemoryMapsWithContext(context.Background(), grouped)
}
func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
var ret []MemoryMapsStat var ret []MemoryMapsStat
return &ret, common.ErrNotImplementedError return &ret, common.ErrNotImplementedError
} }
@ -298,82 +522,103 @@ func NewProcess(pid int32) (*Process, error) {
return p, nil return p, nil
} }
func (p *Process) SendSignal(sig syscall.Signal) error { func (p *Process) SendSignal(sig windows.Signal) error {
return p.SendSignalWithContext(context.Background(), sig)
}
func (p *Process) SendSignalWithContext(ctx context.Context, sig windows.Signal) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Suspend() error { func (p *Process) Suspend() error {
return p.SuspendWithContext(context.Background())
}
func (p *Process) SuspendWithContext(ctx context.Context) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Resume() error { func (p *Process) Resume() error {
return p.ResumeWithContext(context.Background())
}
func (p *Process) ResumeWithContext(ctx context.Context) error {
return common.ErrNotImplementedError return common.ErrNotImplementedError
} }
func (p *Process) Terminate() error { func (p *Process) Terminate() error {
return p.TerminateWithContext(context.Background())
}
func (p *Process) TerminateWithContext(ctx context.Context) error {
// PROCESS_TERMINATE = 0x0001 // PROCESS_TERMINATE = 0x0001
proc := w32.OpenProcess(0x0001, false, uint32(p.Pid)) proc := w32.OpenProcess(0x0001, false, uint32(p.Pid))
ret := w32.TerminateProcess(proc, 0) ret := w32.TerminateProcess(proc, 0)
w32.CloseHandle(proc) w32.CloseHandle(proc)
if ret == false { if ret == false {
return syscall.GetLastError() return windows.GetLastError()
} else { } else {
return nil return nil
} }
} }
func (p *Process) Kill() error { func (p *Process) Kill() error {
return common.ErrNotImplementedError return p.KillWithContext(context.Background())
} }
func (p *Process) getFromSnapProcess(pid int32) (int32, int32, string, error) { func (p *Process) KillWithContext(ctx context.Context) error {
process := os.Process{Pid: int(p.Pid)}
return process.Kill()
}
func getFromSnapProcess(pid int32) (int32, int32, string, error) {
snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid)) snap := w32.CreateToolhelp32Snapshot(w32.TH32CS_SNAPPROCESS, uint32(pid))
if snap == 0 { if snap == 0 {
return 0, 0, "", syscall.GetLastError() return 0, 0, "", windows.GetLastError()
} }
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) == false {
return 0, 0, "", syscall.GetLastError() return 0, 0, "", windows.GetLastError()
} }
if pe32.Th32ProcessID == uint32(pid) { if pe32.Th32ProcessID == uint32(pid) {
szexe := syscall.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
} }
for w32.Process32Next(snap, &pe32) { for w32.Process32Next(snap, &pe32) {
if pe32.Th32ProcessID == uint32(pid) { if pe32.Th32ProcessID == uint32(pid) {
szexe := syscall.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
} }
} }
return 0, 0, "", errors.New("Couldn't find pid:" + string(pid)) return 0, 0, "", fmt.Errorf("Couldn't find pid: %d", pid)
} }
// Get processes // Get processes
func processes() ([]*Process, error) { func Processes() ([]*Process, error) {
return ProcessesWithContext(context.Background())
}
var dst []Win32_Process func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
q := wmi.CreateQuery(&dst, "") out := []*Process{}
err := wmi.Query(q, &dst)
pids, err := PidsWithContext(ctx)
if err != nil { if err != nil {
return []*Process{}, err return out, fmt.Errorf("could not get Processes %s", err)
} }
if len(dst) == 0 {
return []*Process{}, fmt.Errorf("could not get Process") for _, pid := range pids {
} p, err := NewProcess(pid)
results := make([]*Process, 0, len(dst))
for _, proc := range dst {
p, err := NewProcess(int32(proc.ProcessID))
if err != nil { if err != nil {
continue continue
} }
results = append(results, p) out = append(out, p)
} }
return results, nil return out, nil
} }
func getProcInfo(pid int32) (*SystemProcessInformation, error) { func getProcInfo(pid int32) (*SystemProcessInformation, error) {
@ -388,22 +633,22 @@ func getProcInfo(pid int32) (*SystemProcessInformation, error) {
uintptr(unsafe.Pointer(&bufferSize)), uintptr(unsafe.Pointer(&bufferSize)),
uintptr(unsafe.Pointer(&bufferSize))) uintptr(unsafe.Pointer(&bufferSize)))
if ret != 0 { if ret != 0 {
return nil, syscall.GetLastError() return nil, windows.GetLastError()
} }
return &sysProcInfo, nil return &sysProcInfo, nil
} }
func getRusage(pid int32) (*syscall.Rusage, error) { func getRusage(pid int32) (*windows.Rusage, error) {
var CPU syscall.Rusage var CPU windows.Rusage
c, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) c, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, uint32(pid))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer syscall.CloseHandle(c) defer windows.CloseHandle(c)
if err := syscall.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil { if err := windows.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil {
return nil, err return nil, err
} }
@ -412,11 +657,12 @@ func getRusage(pid int32) (*syscall.Rusage, error) {
func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) { func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) {
var mem PROCESS_MEMORY_COUNTERS var mem PROCESS_MEMORY_COUNTERS
c, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid)) // PROCESS_QUERY_LIMITED_INFORMATION is 0x1000
c, err := windows.OpenProcess(0x1000, false, uint32(pid))
if err != nil { if err != nil {
return mem, err return mem, err
} }
defer syscall.CloseHandle(c) defer windows.CloseHandle(c)
if err := getProcessMemoryInfo(c, &mem); err != nil { if err := getProcessMemoryInfo(c, &mem); err != nil {
return mem, err return mem, err
} }
@ -424,7 +670,7 @@ func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) {
return mem, err return mem, err
} }
func getProcessMemoryInfo(h syscall.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) { func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) {
r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem))) r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem)))
if r1 == 0 { if r1 == 0 {
if e1 != 0 { if e1 != 0 {
@ -435,3 +681,31 @@ func getProcessMemoryInfo(h syscall.Handle, mem *PROCESS_MEMORY_COUNTERS) (err e
} }
return return
} }
type SYSTEM_TIMES struct {
CreateTime syscall.Filetime
ExitTime syscall.Filetime
KernelTime syscall.Filetime
UserTime syscall.Filetime
}
func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
var times SYSTEM_TIMES
// PROCESS_QUERY_LIMITED_INFORMATION is 0x1000
h, err := windows.OpenProcess(0x1000, false, uint32(pid))
if err != nil {
return times, err
}
defer windows.CloseHandle(h)
err = syscall.GetProcessTimes(
syscall.Handle(h),
&times.CreateTime,
&times.ExitTime,
&times.KernelTime,
&times.UserTime,
)
return times, err
}

View File

@ -1,160 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Hand Writing
// - all pointer in ExternProc to uint64
// +build ignore
/*
Input to cgo -godefs.
*/
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
// +godefs map struct_ [16]byte /* in6_addr */
package process
/*
#define __DARWIN_UNIX03 0
#define KERNEL
#define _DARWIN_USE_64_BIT_INODE
#include <dirent.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <sys/event.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <net/bpf.h>
#include <net/if_dl.h>
#include <net/if_var.h>
#include <net/route.h>
#include <netinet/in.h>
#include <sys/sysctl.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/_types/_timeval.h>
#include <sys/appleapiopts.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include <bsm/audit.h>
#include <sys/queue.h>
enum {
sizeofPtr = sizeof(void*),
};
union sockaddr_all {
struct sockaddr s1; // this one gets used for fields
struct sockaddr_in s2; // these pad it out
struct sockaddr_in6 s3;
struct sockaddr_un s4;
struct sockaddr_dl s5;
};
struct sockaddr_any {
struct sockaddr addr;
char pad[sizeof(union sockaddr_all) - sizeof(struct sockaddr)];
};
struct ucred_queue {
struct ucred *tqe_next;
struct ucred **tqe_prev;
TRACEBUF
};
*/
import "C"
// Machine characteristics; for internal use.
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
// Time
type Timespec C.struct_timespec
type Timeval C.struct_timeval
// Processes
type Rusage C.struct_rusage
type Rlimit C.struct_rlimit
type UGid_t C.gid_t
type KinfoProc C.struct_kinfo_proc
type Eproc C.struct_eproc
type Proc C.struct_proc
type Session C.struct_session
type ucred C.struct_ucred
type Uucred C.struct__ucred
type Upcred C.struct__pcred
type Vmspace C.struct_vmspace
type Sigacts C.struct_sigacts
type ExternProc C.struct_extern_proc
type Itimerval C.struct_itimerval
type Vnode C.struct_vnode
type Pgrp C.struct_pgrp
type UserStruct C.struct_user
type Au_session C.struct_au_session
type Posix_cred C.struct_posix_cred
type Label C.struct_label
type AuditinfoAddr C.struct_auditinfo_addr
type AuMask C.struct_au_mask
type AuTidAddr C.struct_au_tid_addr
// TAILQ(ucred)
type UcredQueue C.struct_ucred_queue

View File

@ -1,95 +0,0 @@
// +build ignore
// We still need editing by hands.
// go tool cgo -godefs types_freebsd.go | sed 's/\*int64/int64/' | sed 's/\*byte/int64/' > process_freebsd_amd64.go
/*
Input to cgo -godefs.
*/
// +godefs map struct_pargs int64 /* pargs */
// +godefs map struct_proc int64 /* proc */
// +godefs map struct_user int64 /* user */
// +godefs map struct_vnode int64 /* vnode */
// +godefs map struct_vnode int64 /* vnode */
// +godefs map struct_filedesc int64 /* filedesc */
// +godefs map struct_vmspace int64 /* vmspace */
// +godefs map struct_pcb int64 /* pcb */
// +godefs map struct_thread int64 /* thread */
// +godefs map struct___sigset [16]byte /* sigset */
package process
/*
#include <sys/types.h>
#include <sys/user.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
CTLKern = 1 // "high kernel": proc, limits
KernProc = 14 // struct: process entries
KernProcPID = 1 // by process id
KernProcProc = 8 // only return procs
KernProcPathname = 12 // path to executable
KernProcArgs = 7 // get/set arguments/proctitle
)
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
)
const (
sizeOfKinfoVmentry = C.sizeof_struct_kinfo_vmentry
sizeOfKinfoProc = C.sizeof_struct_kinfo_proc
)
// from sys/proc.h
const (
SIDL = 1 /* Process being created by fork. */
SRUN = 2 /* Currently runnable. */
SSLEEP = 3 /* Sleeping on an address. */
SSTOP = 4 /* Process debugging or suspension. */
SZOMB = 5 /* Awaiting collection by parent. */
SWAIT = 6 /* Waiting for interrupt. */
SLOCK = 7 /* Blocked on a lock. */
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
// Time
type Timespec C.struct_timespec
type Timeval C.struct_timeval
// Processes
type Rusage C.struct_rusage
type Rlimit C.struct_rlimit
type KinfoProc C.struct_kinfo_proc
type Priority C.struct_priority
type KinfoVmentry C.struct_kinfo_vmentry

View File

@ -1,103 +0,0 @@
// +build ignore
// We still need editing by hands.
// go tool cgo -godefs types_openbsd.go | sed 's/\*int64/int64/' | sed 's/\*byte/int64/' > process_openbsd_amd64.go
/*
Input to cgo -godefs.
*/
// +godefs map struct_pargs int64 /* pargs */
// +godefs map struct_proc int64 /* proc */
// +godefs map struct_user int64 /* user */
// +godefs map struct_vnode int64 /* vnode */
// +godefs map struct_vnode int64 /* vnode */
// +godefs map struct_filedesc int64 /* filedesc */
// +godefs map struct_vmspace int64 /* vmspace */
// +godefs map struct_pcb int64 /* pcb */
// +godefs map struct_thread int64 /* thread */
// +godefs map struct___sigset [16]byte /* sigset */
package process
/*
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
enum {
sizeofPtr = sizeof(void*),
};
*/
import "C"
// Machine characteristics; for internal use.
const (
CTLKern = 1 // "high kernel": proc, limits
KernProc = 66 // struct: process entries
KernProcAll = 0
KernProcPID = 1 // by process id
KernProcProc = 8 // only return procs
KernProcPathname = 12 // path to executable
KernProcArgs = 55 // get/set arguments/proctitle
KernProcArgv = 1
KernProcEnv = 3
)
const (
ArgMax = 256 * 1024 // sys/syslimits.h:#define ARG_MAX
)
const (
sizeofPtr = C.sizeofPtr
sizeofShort = C.sizeof_short
sizeofInt = C.sizeof_int
sizeofLong = C.sizeof_long
sizeofLongLong = C.sizeof_longlong
)
const (
sizeOfKinfoVmentry = C.sizeof_struct_kinfo_vmentry
sizeOfKinfoProc = C.sizeof_struct_kinfo_proc
)
// from sys/proc.h
const (
SIDL = 1 /* Process being created by fork. */
SRUN = 2 /* Currently runnable. */
SSLEEP = 3 /* Sleeping on an address. */
SSTOP = 4 /* Process debugging or suspension. */
SZOMB = 5 /* Awaiting collection by parent. */
SDEAD = 6 /* Thread is almost gone */
SONPROC = 7 /* Thread is currently on a CPU. */
)
// Basic types
type (
_C_short C.short
_C_int C.int
_C_long C.long
_C_long_long C.longlong
)
// Time
type Timespec C.struct_timespec
type Timeval C.struct_timeval
// Processes
type Rusage C.struct_rusage
type Rlimit C.struct_rlimit
type KinfoProc C.struct_kinfo_proc
type Priority C.struct_priority
type KinfoVmentry C.struct_kinfo_vmentry

14
vendor/vendor.json vendored
View File

@ -258,13 +258,13 @@
{"path":"github.com/ryanuber/columnize","checksumSHA1":"ExnVEVNT8APpFTm26cUb5T09yR4=","comment":"v2.0.1-8-g983d3a5","revision":"9b3edd62028f107d7cabb19353292afd29311a4e","revisionTime":"2016-07-12T16:32:29Z"}, {"path":"github.com/ryanuber/columnize","checksumSHA1":"ExnVEVNT8APpFTm26cUb5T09yR4=","comment":"v2.0.1-8-g983d3a5","revision":"9b3edd62028f107d7cabb19353292afd29311a4e","revisionTime":"2016-07-12T16:32:29Z"},
{"path":"github.com/ryanuber/go-glob","checksumSHA1":"6JP37UqrI0H80Gpk0Y2P+KXgn5M=","revision":"256dc444b735e061061cf46c809487313d5b0065","revisionTime":"2017-01-28T01:21:29Z"}, {"path":"github.com/ryanuber/go-glob","checksumSHA1":"6JP37UqrI0H80Gpk0Y2P+KXgn5M=","revision":"256dc444b735e061061cf46c809487313d5b0065","revisionTime":"2017-01-28T01:21:29Z"},
{"path":"github.com/sean-/seed","checksumSHA1":"A/YUMbGg1LHIeK2+NLZBt+MIAao=","revision":"3c72d44db0c567f7c901f9c5da5fe68392227750","revisionTime":"2017-02-08T16:47:21Z"}, {"path":"github.com/sean-/seed","checksumSHA1":"A/YUMbGg1LHIeK2+NLZBt+MIAao=","revision":"3c72d44db0c567f7c901f9c5da5fe68392227750","revisionTime":"2017-02-08T16:47:21Z"},
{"path":"github.com/shirou/gopsutil/cpu","checksumSHA1":"zW2k8E1gkuySzTz2eXuSEDhpffY=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"}, {"path":"github.com/shirou/gopsutil/cpu","checksumSHA1":"xnCc/S88aNcLk5ypKy35jzFWSEA=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/gopsutil/disk","checksumSHA1":"wxkkOLGCVJ/qrh+eSSFyIW2kTd8=","revision":"b62e301a8b9958eebb7299683eb57fab229a9501","revisionTime":"2017-02-08T02:55:55Z"}, {"path":"github.com/shirou/gopsutil/disk","checksumSHA1":"JRyOki26wb3vORGErYK95N4tAeQ=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/gopsutil/host","checksumSHA1":"GsqEEmGv6sj8DreS2SYXRkoZ9NI=","revision":"b62e301a8b9958eebb7299683eb57fab229a9501","revisionTime":"2017-02-08T02:55:55Z"}, {"path":"github.com/shirou/gopsutil/host","checksumSHA1":"WpGujRcl7wfklBc5/8JREHAQmF4=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/gopsutil/internal/common","checksumSHA1":"hz9RxkaV3Tnju2eiHBWO/Yv7n5c=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"}, {"path":"github.com/shirou/gopsutil/internal/common","checksumSHA1":"GR7l7Ez4ppxIfonl7aJayYRVPWc=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/gopsutil/mem","checksumSHA1":"XQwjGKI51Y3aQ3/jNyRh9Gnprgg=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"}, {"path":"github.com/shirou/gopsutil/mem","checksumSHA1":"ehcscnqRvxDZYb1nBDVzZi8vFi8=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/gopsutil/net","checksumSHA1":"OSvOZs5uK5iolCOeS46nB2InVy8=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"}, {"path":"github.com/shirou/gopsutil/net","checksumSHA1":"wAc1Qo6T9MTYdSog78uJfCYZPtc=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/gopsutil/process","checksumSHA1":"JX0bRK/BdKVfbm4XOxMducVdY58=","revision":"32b6636de04b303274daac3ca2b10d3b0e4afc35","revisionTime":"2017-02-04T05:36:48Z"}, {"path":"github.com/shirou/gopsutil/process","checksumSHA1":"eI4qh+sc46gqKAfBt2jspO2l448=","revision":"a11c78ba2c13c5b1ee59c53296ba35f92f0ce658","revisionTime":"2018-09-27T12:43:08Z"},
{"path":"github.com/shirou/w32","checksumSHA1":"Nve7SpDmjsv6+rhkXAkfg/UQx94=","revision":"bb4de0191aa41b5507caa14b0650cdbddcd9280b","revisionTime":"2016-09-30T03:27:40Z"}, {"path":"github.com/shirou/w32","checksumSHA1":"Nve7SpDmjsv6+rhkXAkfg/UQx94=","revision":"bb4de0191aa41b5507caa14b0650cdbddcd9280b","revisionTime":"2016-09-30T03:27:40Z"},
{"path":"github.com/spf13/pflag","checksumSHA1":"WVKhLVXQFxXwmuRX+IRzebUmxjE=","revision":"298182f68c66c05229eb03ac171abe6e309ee79a","revisionTime":"2018-08-31T15:14:32Z"}, {"path":"github.com/spf13/pflag","checksumSHA1":"WVKhLVXQFxXwmuRX+IRzebUmxjE=","revision":"298182f68c66c05229eb03ac171abe6e309ee79a","revisionTime":"2018-08-31T15:14:32Z"},
{"path":"github.com/stretchr/objx","checksumSHA1":"n+vQ7Bmp+ODWGmCp8cI5MFsaZVA=","revision":"a5cfa15c000af5f09784e5355969ba7eb66ef0de","revisionTime":"2018-04-26T10:50:06Z"}, {"path":"github.com/stretchr/objx","checksumSHA1":"n+vQ7Bmp+ODWGmCp8cI5MFsaZVA=","revision":"a5cfa15c000af5f09784e5355969ba7eb66ef0de","revisionTime":"2018-04-26T10:50:06Z"},