Adding upstream version 2.52.6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
a960158181
commit
6d002e9543
441 changed files with 95392 additions and 0 deletions
61
internal/gopsutil/LICENSE
Normal file
61
internal/gopsutil/LICENSE
Normal file
|
@ -0,0 +1,61 @@
|
|||
gopsutil is distributed under BSD license reproduced below.
|
||||
|
||||
Copyright (c) 2014, WAKAYAMA Shirou
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the gopsutil authors nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
-------
|
||||
internal/common/binary.go in the gopsutil is copied and modifid from golang/encoding/binary.go.
|
||||
|
||||
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
636
internal/gopsutil/common/binary.go
Normal file
636
internal/gopsutil/common/binary.go
Normal file
|
@ -0,0 +1,636 @@
|
|||
package common
|
||||
|
||||
// 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.
|
||||
|
||||
// Package binary implements simple translation between numbers and byte
|
||||
// sequences and encoding and decoding of varints.
|
||||
//
|
||||
// Numbers are translated by reading and writing fixed-size values.
|
||||
// A fixed-size value is either a fixed-size arithmetic
|
||||
// type (int8, uint8, int16, float32, complex64, ...)
|
||||
// or an array or struct containing only fixed-size values.
|
||||
//
|
||||
// The varint functions encode and decode single integer values using
|
||||
// a variable-length encoding; smaller values require fewer bytes.
|
||||
// For a specification, see
|
||||
// http://code.google.com/apis/protocolbuffers/docs/encoding.html.
|
||||
//
|
||||
// This package favors simplicity over efficiency. Clients that require
|
||||
// high-performance serialization, especially for large data structures,
|
||||
// should look at more advanced solutions such as the encoding/gob
|
||||
// package or protocol buffers.
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A ByteOrder specifies how to convert byte sequences into
|
||||
// 16-, 32-, or 64-bit unsigned integers.
|
||||
type ByteOrder interface {
|
||||
Uint16([]byte) uint16
|
||||
Uint32([]byte) uint32
|
||||
Uint64([]byte) uint64
|
||||
PutUint16([]byte, uint16)
|
||||
PutUint32([]byte, uint32)
|
||||
PutUint64([]byte, uint64)
|
||||
String() string
|
||||
}
|
||||
|
||||
// LittleEndian is the little-endian implementation of ByteOrder.
|
||||
var LittleEndian littleEndian
|
||||
|
||||
// BigEndian is the big-endian implementation of ByteOrder.
|
||||
var BigEndian bigEndian
|
||||
|
||||
type littleEndian struct{}
|
||||
|
||||
func (littleEndian) Uint16(b []byte) uint16 { return uint16(b[0]) | uint16(b[1])<<8 }
|
||||
|
||||
func (littleEndian) PutUint16(b []byte, v uint16) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
}
|
||||
|
||||
func (littleEndian) Uint32(b []byte) uint32 {
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
}
|
||||
|
||||
func (littleEndian) PutUint32(b []byte, v uint32) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
}
|
||||
|
||||
func (littleEndian) Uint64(b []byte) uint64 {
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
}
|
||||
|
||||
func (littleEndian) PutUint64(b []byte, v uint64) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
}
|
||||
|
||||
func (littleEndian) String() string { return "LittleEndian" }
|
||||
|
||||
func (littleEndian) GoString() string { return "binary.LittleEndian" }
|
||||
|
||||
type bigEndian struct{}
|
||||
|
||||
func (bigEndian) Uint16(b []byte) uint16 { return uint16(b[1]) | uint16(b[0])<<8 }
|
||||
|
||||
func (bigEndian) PutUint16(b []byte, v uint16) {
|
||||
b[0] = byte(v >> 8)
|
||||
b[1] = byte(v)
|
||||
}
|
||||
|
||||
func (bigEndian) Uint32(b []byte) uint32 {
|
||||
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||
}
|
||||
|
||||
func (bigEndian) PutUint32(b []byte, v uint32) {
|
||||
b[0] = byte(v >> 24)
|
||||
b[1] = byte(v >> 16)
|
||||
b[2] = byte(v >> 8)
|
||||
b[3] = byte(v)
|
||||
}
|
||||
|
||||
func (bigEndian) Uint64(b []byte) uint64 {
|
||||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
}
|
||||
|
||||
func (bigEndian) PutUint64(b []byte, v uint64) {
|
||||
b[0] = byte(v >> 56)
|
||||
b[1] = byte(v >> 48)
|
||||
b[2] = byte(v >> 40)
|
||||
b[3] = byte(v >> 32)
|
||||
b[4] = byte(v >> 24)
|
||||
b[5] = byte(v >> 16)
|
||||
b[6] = byte(v >> 8)
|
||||
b[7] = byte(v)
|
||||
}
|
||||
|
||||
func (bigEndian) String() string { return "BigEndian" }
|
||||
|
||||
func (bigEndian) GoString() string { return "binary.BigEndian" }
|
||||
|
||||
// Read reads structured binary data from r into data.
|
||||
// Data must be a pointer to a fixed-size value or a slice
|
||||
// of fixed-size values.
|
||||
// Bytes read from r are decoded using the specified byte order
|
||||
// and written to successive fields of the data.
|
||||
// When reading into structs, the field data for fields with
|
||||
// blank (_) field names is skipped; i.e., blank field names
|
||||
// may be used for padding.
|
||||
// When reading into a struct, all non-blank fields must be exported.
|
||||
func Read(r io.Reader, order ByteOrder, data interface{}) error {
|
||||
// Fast path for basic types and slices.
|
||||
if n := intDataSize(data); n != 0 {
|
||||
var b [8]byte
|
||||
var bs []byte
|
||||
if n > len(b) {
|
||||
bs = make([]byte, n)
|
||||
} else {
|
||||
bs = b[:n]
|
||||
}
|
||||
if _, err := io.ReadFull(r, bs); err != nil {
|
||||
return err
|
||||
}
|
||||
switch data := data.(type) {
|
||||
case *int8:
|
||||
*data = int8(b[0])
|
||||
case *uint8:
|
||||
*data = b[0]
|
||||
case *int16:
|
||||
*data = int16(order.Uint16(bs))
|
||||
case *uint16:
|
||||
*data = order.Uint16(bs)
|
||||
case *int32:
|
||||
*data = int32(order.Uint32(bs))
|
||||
case *uint32:
|
||||
*data = order.Uint32(bs)
|
||||
case *int64:
|
||||
*data = int64(order.Uint64(bs))
|
||||
case *uint64:
|
||||
*data = order.Uint64(bs)
|
||||
case []int8:
|
||||
for i, x := range bs { // Easier to loop over the input for 8-bit values.
|
||||
data[i] = int8(x)
|
||||
}
|
||||
case []uint8:
|
||||
copy(data, bs)
|
||||
case []int16:
|
||||
for i := range data {
|
||||
data[i] = int16(order.Uint16(bs[2*i:]))
|
||||
}
|
||||
case []uint16:
|
||||
for i := range data {
|
||||
data[i] = order.Uint16(bs[2*i:])
|
||||
}
|
||||
case []int32:
|
||||
for i := range data {
|
||||
data[i] = int32(order.Uint32(bs[4*i:]))
|
||||
}
|
||||
case []uint32:
|
||||
for i := range data {
|
||||
data[i] = order.Uint32(bs[4*i:])
|
||||
}
|
||||
case []int64:
|
||||
for i := range data {
|
||||
data[i] = int64(order.Uint64(bs[8*i:]))
|
||||
}
|
||||
case []uint64:
|
||||
for i := range data {
|
||||
data[i] = order.Uint64(bs[8*i:])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback to reflect-based decoding.
|
||||
v := reflect.ValueOf(data)
|
||||
size := -1
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
v = v.Elem()
|
||||
size = dataSize(v)
|
||||
case reflect.Slice:
|
||||
size = dataSize(v)
|
||||
}
|
||||
if size < 0 {
|
||||
return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String())
|
||||
}
|
||||
d := &decoder{order: order, buf: make([]byte, size)}
|
||||
if _, err := io.ReadFull(r, d.buf); err != nil {
|
||||
return err
|
||||
}
|
||||
d.value(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the binary representation of data into w.
|
||||
// Data must be a fixed-size value or a slice of fixed-size
|
||||
// values, or a pointer to such data.
|
||||
// Bytes written to w are encoded using the specified byte order
|
||||
// and read from successive fields of the data.
|
||||
// When writing structs, zero values are written for fields
|
||||
// with blank (_) field names.
|
||||
func Write(w io.Writer, order ByteOrder, data interface{}) error {
|
||||
// Fast path for basic types and slices.
|
||||
if n := intDataSize(data); n != 0 {
|
||||
var b [8]byte
|
||||
var bs []byte
|
||||
if n > len(b) {
|
||||
bs = make([]byte, n)
|
||||
} else {
|
||||
bs = b[:n]
|
||||
}
|
||||
switch v := data.(type) {
|
||||
case *int8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(*v)
|
||||
case int8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(v)
|
||||
case []int8:
|
||||
for i, x := range v {
|
||||
bs[i] = byte(x)
|
||||
}
|
||||
case *uint8:
|
||||
bs = b[:1]
|
||||
b[0] = *v
|
||||
case uint8:
|
||||
bs = b[:1]
|
||||
b[0] = byte(v)
|
||||
case []uint8:
|
||||
bs = v
|
||||
case *int16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, uint16(*v))
|
||||
case int16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, uint16(v))
|
||||
case []int16:
|
||||
for i, x := range v {
|
||||
order.PutUint16(bs[2*i:], uint16(x))
|
||||
}
|
||||
case *uint16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, *v)
|
||||
case uint16:
|
||||
bs = b[:2]
|
||||
order.PutUint16(bs, v)
|
||||
case []uint16:
|
||||
for i, x := range v {
|
||||
order.PutUint16(bs[2*i:], x)
|
||||
}
|
||||
case *int32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, uint32(*v))
|
||||
case int32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, uint32(v))
|
||||
case []int32:
|
||||
for i, x := range v {
|
||||
order.PutUint32(bs[4*i:], uint32(x))
|
||||
}
|
||||
case *uint32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, *v)
|
||||
case uint32:
|
||||
bs = b[:4]
|
||||
order.PutUint32(bs, v)
|
||||
case []uint32:
|
||||
for i, x := range v {
|
||||
order.PutUint32(bs[4*i:], x)
|
||||
}
|
||||
case *int64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, uint64(*v))
|
||||
case int64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, uint64(v))
|
||||
case []int64:
|
||||
for i, x := range v {
|
||||
order.PutUint64(bs[8*i:], uint64(x))
|
||||
}
|
||||
case *uint64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, *v)
|
||||
case uint64:
|
||||
bs = b[:8]
|
||||
order.PutUint64(bs, v)
|
||||
case []uint64:
|
||||
for i, x := range v {
|
||||
order.PutUint64(bs[8*i:], x)
|
||||
}
|
||||
}
|
||||
_, err := w.Write(bs)
|
||||
return err
|
||||
}
|
||||
|
||||
// Fallback to reflect-based encoding.
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
size := dataSize(v)
|
||||
if size < 0 {
|
||||
return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String())
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
e := &encoder{order: order, buf: buf}
|
||||
e.value(v)
|
||||
_, err := w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// Size returns how many bytes Write would generate to encode the value v, which
|
||||
// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
|
||||
// If v is neither of these, Size returns -1.
|
||||
func Size(v interface{}) int {
|
||||
return dataSize(reflect.Indirect(reflect.ValueOf(v)))
|
||||
}
|
||||
|
||||
// dataSize returns the number of bytes the actual data represented by v occupies in memory.
|
||||
// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice
|
||||
// it returns the length of the slice times the element size and does not count the memory
|
||||
// occupied by the header. If the type of v is not acceptable, dataSize returns -1.
|
||||
func dataSize(v reflect.Value) int {
|
||||
if v.Kind() == reflect.Slice {
|
||||
if s := sizeof(v.Type().Elem()); s >= 0 {
|
||||
return s * v.Len()
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return sizeof(v.Type())
|
||||
}
|
||||
|
||||
// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable.
|
||||
func sizeof(t reflect.Type) int {
|
||||
switch t.Kind() {
|
||||
case reflect.Array:
|
||||
if s := sizeof(t.Elem()); s >= 0 {
|
||||
return s * t.Len()
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
sum := 0
|
||||
for i, n := 0, t.NumField(); i < n; i++ {
|
||||
s := sizeof(t.Field(i).Type)
|
||||
if s < 0 {
|
||||
return -1
|
||||
}
|
||||
sum += s
|
||||
}
|
||||
return sum
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.Ptr:
|
||||
return int(t.Size())
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
type coder struct {
|
||||
order ByteOrder
|
||||
buf []byte
|
||||
}
|
||||
|
||||
type (
|
||||
decoder coder
|
||||
encoder coder
|
||||
)
|
||||
|
||||
func (d *decoder) uint8() uint8 {
|
||||
x := d.buf[0]
|
||||
d.buf = d.buf[1:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint8(x uint8) {
|
||||
e.buf[0] = x
|
||||
e.buf = e.buf[1:]
|
||||
}
|
||||
|
||||
func (d *decoder) uint16() uint16 {
|
||||
x := d.order.Uint16(d.buf[0:2])
|
||||
d.buf = d.buf[2:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint16(x uint16) {
|
||||
e.order.PutUint16(e.buf[0:2], x)
|
||||
e.buf = e.buf[2:]
|
||||
}
|
||||
|
||||
func (d *decoder) uint32() uint32 {
|
||||
x := d.order.Uint32(d.buf[0:4])
|
||||
d.buf = d.buf[4:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint32(x uint32) {
|
||||
e.order.PutUint32(e.buf[0:4], x)
|
||||
e.buf = e.buf[4:]
|
||||
}
|
||||
|
||||
func (d *decoder) uint64() uint64 {
|
||||
x := d.order.Uint64(d.buf[0:8])
|
||||
d.buf = d.buf[8:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (e *encoder) uint64(x uint64) {
|
||||
e.order.PutUint64(e.buf[0:8], x)
|
||||
e.buf = e.buf[8:]
|
||||
}
|
||||
|
||||
func (d *decoder) int8() int8 { return int8(d.uint8()) }
|
||||
|
||||
func (e *encoder) int8(x int8) { e.uint8(uint8(x)) }
|
||||
|
||||
func (d *decoder) int16() int16 { return int16(d.uint16()) }
|
||||
|
||||
func (e *encoder) int16(x int16) { e.uint16(uint16(x)) }
|
||||
|
||||
func (d *decoder) int32() int32 { return int32(d.uint32()) }
|
||||
|
||||
func (e *encoder) int32(x int32) { e.uint32(uint32(x)) }
|
||||
|
||||
func (d *decoder) int64() int64 { return int64(d.uint64()) }
|
||||
|
||||
func (e *encoder) int64(x int64) { e.uint64(uint64(x)) }
|
||||
|
||||
func (d *decoder) value(v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Array:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
d.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
l := v.NumField()
|
||||
for i := 0; i < l; i++ {
|
||||
// Note: Calling v.CanSet() below is an optimization.
|
||||
// It would be sufficient to check the field name,
|
||||
// but creating the StructField info for each field is
|
||||
// costly (run "go test -bench=ReadStruct" and compare
|
||||
// results when making changes to this code).
|
||||
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
|
||||
d.value(v)
|
||||
} else {
|
||||
d.skip(v)
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
d.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Int8:
|
||||
v.SetInt(int64(d.int8()))
|
||||
case reflect.Int16:
|
||||
v.SetInt(int64(d.int16()))
|
||||
case reflect.Int32:
|
||||
v.SetInt(int64(d.int32()))
|
||||
case reflect.Int64:
|
||||
v.SetInt(d.int64())
|
||||
|
||||
case reflect.Uint8:
|
||||
v.SetUint(uint64(d.uint8()))
|
||||
case reflect.Uint16:
|
||||
v.SetUint(uint64(d.uint16()))
|
||||
case reflect.Uint32:
|
||||
v.SetUint(uint64(d.uint32()))
|
||||
case reflect.Uint64:
|
||||
v.SetUint(d.uint64())
|
||||
|
||||
case reflect.Float32:
|
||||
v.SetFloat(float64(math.Float32frombits(d.uint32())))
|
||||
case reflect.Float64:
|
||||
v.SetFloat(math.Float64frombits(d.uint64()))
|
||||
|
||||
case reflect.Complex64:
|
||||
v.SetComplex(complex(
|
||||
float64(math.Float32frombits(d.uint32())),
|
||||
float64(math.Float32frombits(d.uint32())),
|
||||
))
|
||||
case reflect.Complex128:
|
||||
v.SetComplex(complex(
|
||||
math.Float64frombits(d.uint64()),
|
||||
math.Float64frombits(d.uint64()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) value(v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Array:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
e.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
l := v.NumField()
|
||||
for i := 0; i < l; i++ {
|
||||
// see comment for corresponding code in decoder.value()
|
||||
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
|
||||
e.value(v)
|
||||
} else {
|
||||
e.skip(v)
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
e.value(v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int8:
|
||||
e.int8(int8(v.Int()))
|
||||
case reflect.Int16:
|
||||
e.int16(int16(v.Int()))
|
||||
case reflect.Int32:
|
||||
e.int32(int32(v.Int()))
|
||||
case reflect.Int64:
|
||||
e.int64(v.Int())
|
||||
}
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Uint8:
|
||||
e.uint8(uint8(v.Uint()))
|
||||
case reflect.Uint16:
|
||||
e.uint16(uint16(v.Uint()))
|
||||
case reflect.Uint32:
|
||||
e.uint32(uint32(v.Uint()))
|
||||
case reflect.Uint64:
|
||||
e.uint64(v.Uint())
|
||||
}
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Float32:
|
||||
e.uint32(math.Float32bits(float32(v.Float())))
|
||||
case reflect.Float64:
|
||||
e.uint64(math.Float64bits(v.Float()))
|
||||
}
|
||||
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Complex64:
|
||||
x := v.Complex()
|
||||
e.uint32(math.Float32bits(float32(real(x))))
|
||||
e.uint32(math.Float32bits(float32(imag(x))))
|
||||
case reflect.Complex128:
|
||||
x := v.Complex()
|
||||
e.uint64(math.Float64bits(real(x)))
|
||||
e.uint64(math.Float64bits(imag(x)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decoder) skip(v reflect.Value) {
|
||||
d.buf = d.buf[dataSize(v):]
|
||||
}
|
||||
|
||||
func (e *encoder) skip(v reflect.Value) {
|
||||
n := dataSize(v)
|
||||
for i := range e.buf[0:n] {
|
||||
e.buf[i] = 0
|
||||
}
|
||||
e.buf = e.buf[n:]
|
||||
}
|
||||
|
||||
// intDataSize returns the size of the data required to represent the data when encoded.
|
||||
// It returns zero if the type cannot be implemented by the fast path in Read or Write.
|
||||
func intDataSize(data interface{}) int {
|
||||
switch data := data.(type) {
|
||||
case int8, *int8, *uint8:
|
||||
return 1
|
||||
case []int8:
|
||||
return len(data)
|
||||
case []uint8:
|
||||
return len(data)
|
||||
case int16, *int16, *uint16:
|
||||
return 2
|
||||
case []int16:
|
||||
return 2 * len(data)
|
||||
case []uint16:
|
||||
return 2 * len(data)
|
||||
case int32, *int32, *uint32:
|
||||
return 4
|
||||
case []int32:
|
||||
return 4 * len(data)
|
||||
case []uint32:
|
||||
return 4 * len(data)
|
||||
case int64, *int64, *uint64:
|
||||
return 8
|
||||
case []int64:
|
||||
return 8 * len(data)
|
||||
case []uint64:
|
||||
return 8 * len(data)
|
||||
}
|
||||
return 0
|
||||
}
|
379
internal/gopsutil/common/common.go
Normal file
379
internal/gopsutil/common/common.go
Normal file
|
@ -0,0 +1,379 @@
|
|||
package common
|
||||
|
||||
//
|
||||
// gopsutil is a port of psutil(http://pythonhosted.org/psutil/).
|
||||
// This covers these architectures.
|
||||
// - linux (amd64, arm)
|
||||
// - freebsd (amd64)
|
||||
// - windows (amd64)
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Timeout = 3 * time.Second
|
||||
ErrTimeout = errors.New("command timed out")
|
||||
)
|
||||
|
||||
type Invoker interface {
|
||||
Command(string, ...string) ([]byte, error)
|
||||
CommandWithContext(context.Context, string, ...string) ([]byte, error)
|
||||
}
|
||||
|
||||
type Invoke struct{}
|
||||
|
||||
func (i Invoke) Command(name string, arg ...string) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 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 {
|
||||
Suffix string // Suffix species expected file name suffix such as "fail"
|
||||
Error error // If Error specfied, return the error.
|
||||
}
|
||||
|
||||
// Command in FakeInvoke returns from expected file if exists.
|
||||
func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) {
|
||||
if i.Error != nil {
|
||||
return []byte{}, i.Error
|
||||
}
|
||||
|
||||
arch := runtime.GOOS
|
||||
|
||||
commandName := filepath.Base(name)
|
||||
|
||||
fname := strings.Join(append([]string{commandName}, arg...), "")
|
||||
fname = url.QueryEscape(fname)
|
||||
fpath := path.Join("testdata", arch, fname)
|
||||
if i.Suffix != "" {
|
||||
fpath += "_" + i.Suffix
|
||||
}
|
||||
if PathExists(fpath) {
|
||||
return os.ReadFile(fpath)
|
||||
}
|
||||
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")
|
||||
|
||||
// ReadFile reads contents from a file
|
||||
func ReadFile(filename string) (string, error) {
|
||||
content, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// ReadLines reads contents from a file and splits them by new lines.
|
||||
// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
|
||||
func ReadLines(filename string) ([]string, error) {
|
||||
return ReadLinesOffsetN(filename, 0, -1)
|
||||
}
|
||||
|
||||
// ReadLines reads contents from file and splits them by new line.
|
||||
// The offset tells at which line number to start.
|
||||
// The count determines the number of lines to read (starting from offset):
|
||||
//
|
||||
// n >= 0: at most n lines
|
||||
// n < 0: whole file
|
||||
func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return []string{""}, err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}(f)
|
||||
|
||||
var ret []string
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
for i := 0; i < n+int(offset) || n < 0; i++ {
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if i < int(offset) {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, strings.Trim(line, "\n"))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IntToString(orig []int8) string {
|
||||
ret := make([]byte, len(orig))
|
||||
size := -1
|
||||
for i, o := range orig {
|
||||
if o == 0 {
|
||||
size = i
|
||||
break
|
||||
}
|
||||
ret[i] = byte(o)
|
||||
}
|
||||
if size == -1 {
|
||||
size = len(orig)
|
||||
}
|
||||
|
||||
return string(ret[0:size])
|
||||
}
|
||||
|
||||
func UintToString(orig []uint8) string {
|
||||
ret := make([]byte, len(orig))
|
||||
size := -1
|
||||
for i, o := range orig {
|
||||
if o == 0 {
|
||||
size = i
|
||||
break
|
||||
}
|
||||
ret[i] = byte(o)
|
||||
}
|
||||
if size == -1 {
|
||||
size = len(orig)
|
||||
}
|
||||
|
||||
return string(ret[0:size])
|
||||
}
|
||||
|
||||
func ByteToString(orig []byte) string {
|
||||
n := -1
|
||||
l := -1
|
||||
for i, b := range orig {
|
||||
// skip left side null
|
||||
if l == -1 && b == 0 {
|
||||
continue
|
||||
}
|
||||
if l == -1 {
|
||||
l = i
|
||||
}
|
||||
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
n = i + 1
|
||||
}
|
||||
if n == -1 {
|
||||
return string(orig)
|
||||
}
|
||||
return string(orig[l:n])
|
||||
}
|
||||
|
||||
// ReadInts reads contents from single line file and returns them as []int32.
|
||||
func ReadInts(filename string) ([]int64, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}(f)
|
||||
|
||||
var ret []int64
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
// The int files that this is concerned with should only be one liners.
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
|
||||
i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32)
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Parse Hex to uint32 without error
|
||||
func HexToUint32(hex string) uint32 {
|
||||
vv, _ := strconv.ParseUint(hex, 16, 32)
|
||||
return uint32(vv)
|
||||
}
|
||||
|
||||
// Parse to int32 without error
|
||||
func mustParseInt32(val string) int32 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 32)
|
||||
return int32(vv)
|
||||
}
|
||||
|
||||
// Parse to uint64 without error
|
||||
func mustParseUint64(val string) uint64 {
|
||||
vv, _ := strconv.ParseInt(val, 10, 64)
|
||||
return uint64(vv)
|
||||
}
|
||||
|
||||
// Parse to Float64 without error
|
||||
func mustParseFloat64(val string) float64 {
|
||||
vv, _ := strconv.ParseFloat(val, 64)
|
||||
return vv
|
||||
}
|
||||
|
||||
// StringsHas checks the target string slice contains src or not
|
||||
func StringsHas(target []string, src string) bool {
|
||||
for _, t := range target {
|
||||
if strings.TrimSpace(t) == src {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// StringsContains checks the src in any string of the target string slice
|
||||
func StringsContains(target []string, src string) bool {
|
||||
for _, t := range target {
|
||||
if strings.Contains(t, src) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IntContains checks the src in any int of the target int slice.
|
||||
func IntContains(target []int, src int) bool {
|
||||
for _, t := range target {
|
||||
if src == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// get struct attributes.
|
||||
// This method is used only for debugging platform dependent code.
|
||||
func attributes(m interface{}) map[string]reflect.Type {
|
||||
typ := reflect.TypeOf(m)
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
|
||||
attrs := make(map[string]reflect.Type)
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
p := typ.Field(i)
|
||||
if !p.Anonymous {
|
||||
attrs[p.Name] = p.Type
|
||||
}
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func PathExists(filename string) bool {
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetEnv retrieves the environment variable key. If it does not exist it returns the default.
|
||||
func GetEnv(key, dfault string, combineWith ...string) string {
|
||||
value := os.Getenv(key)
|
||||
if value == "" {
|
||||
value = dfault
|
||||
}
|
||||
|
||||
switch len(combineWith) {
|
||||
case 0:
|
||||
return value
|
||||
case 1:
|
||||
return filepath.Join(value, combineWith[0])
|
||||
default:
|
||||
all := make([]string, len(combineWith)+1)
|
||||
all[0] = value
|
||||
copy(all[1:], combineWith)
|
||||
return filepath.Join(all...)
|
||||
}
|
||||
}
|
||||
|
||||
func HostProc(combineWith ...string) string {
|
||||
return GetEnv("HOST_PROC", "/proc", combineWith...)
|
||||
}
|
||||
|
||||
func HostSys(combineWith ...string) string {
|
||||
return GetEnv("HOST_SYS", "/sys", combineWith...)
|
||||
}
|
||||
|
||||
func HostEtc(combineWith ...string) string {
|
||||
return GetEnv("HOST_ETC", "/etc", combineWith...)
|
||||
}
|
||||
|
||||
func HostVar(combineWith ...string) string {
|
||||
return GetEnv("HOST_VAR", "/var", combineWith...)
|
||||
}
|
||||
|
||||
func HostRun(combineWith ...string) string {
|
||||
return GetEnv("HOST_RUN", "/run", combineWith...)
|
||||
}
|
||||
|
||||
func HostDev(combineWith ...string) string {
|
||||
return GetEnv("HOST_DEV", "/dev", combineWith...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
69
internal/gopsutil/common/common_darwin.go
Normal file
69
internal/gopsutil/common/common_darwin.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
//go:build darwin
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
85
internal/gopsutil/common/common_freebsd.go
Normal file
85
internal/gopsutil/common/common_freebsd.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
//go:build freebsd || openbsd
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func SysctlUint(mib string) (uint64, error) {
|
||||
buf, err := unix.SysctlRaw(mib)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(buf) == 8 { // 64 bit
|
||||
return *(*uint64)(unsafe.Pointer(&buf[0])), nil
|
||||
}
|
||||
if len(buf) == 4 { // 32bit
|
||||
t := *(*uint32)(unsafe.Pointer(&buf[0]))
|
||||
return uint64(t), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unexpected size: %s, %d", mib, len(buf))
|
||||
}
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.Command(sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
mibptr := unsafe.Pointer(&mib[0])
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
269
internal/gopsutil/common/common_linux.go
Normal file
269
internal/gopsutil/common/common_linux.go
Normal file
|
@ -0,0 +1,269 @@
|
|||
//go:build linux
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.Command(sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func NumProcs() (uint64, error) {
|
||||
f, err := os.Open(HostProc())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}(f)
|
||||
|
||||
list, err := f.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var cnt uint64
|
||||
|
||||
for _, v := range list {
|
||||
if _, err = strconv.ParseUint(v, 10, 64); err == nil {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
|
||||
return cnt, nil
|
||||
}
|
||||
|
||||
func BootTimeWithContext(ctx context.Context) (uint64, error) {
|
||||
system, role, err := Virtualization()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
statFile := "stat"
|
||||
if system == "lxc" && role == "guest" {
|
||||
// if lxc, /proc/uptime is used.
|
||||
statFile = "uptime"
|
||||
} else if system == "docker" && role == "guest" {
|
||||
// also docker, guest
|
||||
statFile = "uptime"
|
||||
}
|
||||
|
||||
filename := HostProc(statFile)
|
||||
lines, err := ReadLines(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if statFile == "stat" {
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "btime") {
|
||||
f := strings.Fields(line)
|
||||
if len(f) != 2 {
|
||||
return 0, fmt.Errorf("wrong btime format")
|
||||
}
|
||||
b, err := strconv.ParseInt(f[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t := uint64(b)
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
} else if statFile == "uptime" {
|
||||
if len(lines) != 1 {
|
||||
return 0, fmt.Errorf("wrong uptime format")
|
||||
}
|
||||
f := strings.Fields(lines[0])
|
||||
b, err := strconv.ParseFloat(f[0], 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
t := uint64(time.Now().Unix()) - uint64(b)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("could not find btime")
|
||||
}
|
||||
|
||||
func Virtualization() (string, string, error) {
|
||||
return VirtualizationWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualizationWithContext(ctx context.Context) (string, string, error) {
|
||||
var system string
|
||||
var role string
|
||||
|
||||
filename := HostProc("xen")
|
||||
if PathExists(filename) {
|
||||
system = "xen"
|
||||
role = "guest" // assume guest
|
||||
|
||||
if PathExists(filepath.Join(filename, "capabilities")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "capabilities"))
|
||||
if err == nil {
|
||||
if StringsContains(contents, "control_d") {
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("modules")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "kvm") {
|
||||
system = "kvm"
|
||||
role = "host"
|
||||
} else if StringsContains(contents, "vboxdrv") {
|
||||
system = "vbox"
|
||||
role = "host"
|
||||
} else if StringsContains(contents, "vboxguest") {
|
||||
system = "vbox"
|
||||
role = "guest"
|
||||
} else if StringsContains(contents, "vmware") {
|
||||
system = "vmware"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("cpuinfo")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "QEMU Virtual CPU") ||
|
||||
StringsContains(contents, "Common KVM processor") ||
|
||||
StringsContains(contents, "Common 32-bit KVM processor") {
|
||||
system = "kvm"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc("bus/pci/devices")
|
||||
if PathExists(filename) {
|
||||
contents, err := ReadLines(filename)
|
||||
if err == nil {
|
||||
if StringsContains(contents, "virtio-pci") {
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filename = HostProc()
|
||||
if PathExists(filepath.Join(filename, "bc", "0")) {
|
||||
system = "openvz"
|
||||
role = "host"
|
||||
} else if PathExists(filepath.Join(filename, "vz")) {
|
||||
system = "openvz"
|
||||
role = "guest"
|
||||
}
|
||||
|
||||
// not use dmidecode because it requires root
|
||||
if PathExists(filepath.Join(filename, "self", "status")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "self", "status"))
|
||||
if err == nil {
|
||||
if StringsContains(contents, "s_context:") ||
|
||||
StringsContains(contents, "VxID:") {
|
||||
system = "linux-vserver"
|
||||
}
|
||||
// TODO: guest or host
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(filepath.Join(filename, "1", "environ")) {
|
||||
contents, err := ReadFile(filepath.Join(filename, "1", "environ"))
|
||||
|
||||
if err == nil {
|
||||
if strings.Contains(contents, "container=lxc") {
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(filepath.Join(filename, "self", "cgroup")) {
|
||||
contents, err := ReadLines(filepath.Join(filename, "self", "cgroup"))
|
||||
if err == nil {
|
||||
if StringsContains(contents, "lxc") {
|
||||
system = "lxc"
|
||||
role = "guest"
|
||||
} else if StringsContains(contents, "docker") {
|
||||
system = "docker"
|
||||
role = "guest"
|
||||
} else if StringsContains(contents, "machine-rkt") {
|
||||
system = "rkt"
|
||||
role = "guest"
|
||||
} else if PathExists("/usr/bin/lxc-version") {
|
||||
system = "lxc"
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PathExists(HostEtc("os-release")) {
|
||||
p, _, err := GetOSRelease()
|
||||
if err == nil && p == "coreos" {
|
||||
system = "rkt" // Is it true?
|
||||
role = "host"
|
||||
}
|
||||
}
|
||||
return system, role, nil
|
||||
}
|
||||
|
||||
func GetOSRelease() (platform, version string, err error) {
|
||||
contents, err := ReadLines(HostEtc("os-release"))
|
||||
if err != nil {
|
||||
return "", "", nil // return empty
|
||||
}
|
||||
for _, line := range contents {
|
||||
field := strings.Split(line, "=")
|
||||
if len(field) < 2 {
|
||||
continue
|
||||
}
|
||||
switch field[0] {
|
||||
case "ID": // use ID for lowercase
|
||||
platform = trimQuotes(field[1])
|
||||
case "VERSION":
|
||||
version = trimQuotes(field[1])
|
||||
}
|
||||
}
|
||||
return platform, version, nil
|
||||
}
|
||||
|
||||
// Remove quotes of the source string
|
||||
func trimQuotes(s string) string {
|
||||
if len(s) >= 2 {
|
||||
if s[0] == '"' && s[len(s)-1] == '"' {
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
69
internal/gopsutil/common/common_openbsd.go
Normal file
69
internal/gopsutil/common/common_openbsd.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
//go:build openbsd
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func DoSysctrl(mib string) ([]string, error) {
|
||||
sysctl, err := exec.LookPath("sysctl")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
cmd := exec.Command(sysctl, "-n", mib)
|
||||
cmd.Env = getSysctrlEnv(os.Environ())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
v := strings.Replace(string(out), "{ ", "", 1)
|
||||
v = strings.Replace(string(v), " }", "", 1)
|
||||
values := strings.Fields(string(v))
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func CallSyscall(mib []int32) ([]byte, uint64, error) {
|
||||
mibptr := unsafe.Pointer(&mib[0])
|
||||
miblen := uint64(len(mib))
|
||||
|
||||
// get required buffer size
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
if length == 0 {
|
||||
var b []byte
|
||||
return b, length, err
|
||||
}
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
66
internal/gopsutil/common/common_unix.go
Normal file
66
internal/gopsutil/common/common_unix.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
//go:build linux || freebsd || darwin || openbsd
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ...string) ([]string, error) {
|
||||
var cmd []string
|
||||
if pid == 0 { // will get from all processes.
|
||||
cmd = []string{"-a", "-n", "-P"}
|
||||
} else {
|
||||
cmd = []string{"-a", "-n", "-P", "-p", strconv.Itoa(int(pid))}
|
||||
}
|
||||
cmd = append(cmd, args...)
|
||||
lsof, err := exec.LookPath("lsof")
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, lsof, cmd...)
|
||||
if err != nil {
|
||||
// if no pid found, lsof returns code 1.
|
||||
if err.Error() == "exit status 1" && len(out) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
var ret []string
|
||||
for _, l := range lines[1:] {
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, l)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) {
|
||||
cmd := []string{"-P", strconv.Itoa(int(pid))}
|
||||
pgrep, err := exec.LookPath("pgrep")
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, pgrep, cmd...)
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
ret := make([]int32, 0, len(lines))
|
||||
for _, l := range lines {
|
||||
if len(l) == 0 {
|
||||
continue
|
||||
}
|
||||
i, err := strconv.Atoi(l)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, int32(i))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
234
internal/gopsutil/common/common_windows.go
Normal file
234
internal/gopsutil/common/common_windows.go
Normal file
|
@ -0,0 +1,234 @@
|
|||
//go:build windows
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/wmi"
|
||||
)
|
||||
|
||||
// for double values
|
||||
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// for 64 bit integer values
|
||||
type PDH_FMT_COUNTERVALUE_LARGE struct {
|
||||
CStatus uint32
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// for long values
|
||||
type PDH_FMT_COUNTERVALUE_LONG struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte
|
||||
}
|
||||
|
||||
// windows system const
|
||||
const (
|
||||
ERROR_SUCCESS = 0
|
||||
ERROR_FILE_NOT_FOUND = 2
|
||||
DRIVE_REMOVABLE = 2
|
||||
DRIVE_FIXED = 3
|
||||
HKEY_LOCAL_MACHINE = 0x80000002
|
||||
RRF_RT_REG_SZ = 0x00000002
|
||||
RRF_RT_REG_DWORD = 0x00000010
|
||||
PDH_FMT_LONG = 0x00000100
|
||||
PDH_FMT_DOUBLE = 0x00000200
|
||||
PDH_FMT_LARGE = 0x00000400
|
||||
PDH_INVALID_DATA = 0xc0000bc6
|
||||
PDH_INVALID_HANDLE = 0xC0000bbc
|
||||
PDH_NO_DATA = 0x800007d5
|
||||
)
|
||||
|
||||
const (
|
||||
ProcessBasicInformation = 0
|
||||
ProcessWow64Information = 26
|
||||
)
|
||||
|
||||
var (
|
||||
Modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
ModNt = windows.NewLazySystemDLL("ntdll.dll")
|
||||
ModPdh = windows.NewLazySystemDLL("pdh.dll")
|
||||
ModPsapi = windows.NewLazySystemDLL("psapi.dll")
|
||||
|
||||
ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
|
||||
ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
|
||||
ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation")
|
||||
ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError")
|
||||
ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess")
|
||||
ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory")
|
||||
ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64")
|
||||
ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")
|
||||
|
||||
PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
|
||||
PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW")
|
||||
PdhAddCounter = ModPdh.NewProc("PdhAddCounterW")
|
||||
PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
|
||||
PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
|
||||
PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
|
||||
|
||||
procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
|
||||
)
|
||||
|
||||
type FILETIME struct {
|
||||
DwLowDateTime uint32
|
||||
DwHighDateTime uint32
|
||||
}
|
||||
|
||||
// borrowed from net/interface_windows.go
|
||||
func BytePtrToString(p *uint8) string {
|
||||
a := (*[10000]uint8)(unsafe.Pointer(p))
|
||||
i := 0
|
||||
for a[i] != 0 {
|
||||
i++
|
||||
}
|
||||
return string(a[:i])
|
||||
}
|
||||
|
||||
// CounterInfo
|
||||
// copied from https://github.com/mackerelio/mackerel-agent/
|
||||
type CounterInfo struct {
|
||||
PostName string
|
||||
CounterName string
|
||||
Counter windows.Handle
|
||||
}
|
||||
|
||||
// CreateQuery XXX
|
||||
// copied from https://github.com/mackerelio/mackerel-agent/
|
||||
func CreateQuery() (windows.Handle, error) {
|
||||
var query windows.Handle
|
||||
r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
|
||||
if r != 0 {
|
||||
return 0, err
|
||||
}
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// CreateCounter XXX
|
||||
func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) {
|
||||
var counter windows.Handle
|
||||
r, _, err := PdhAddCounter.Call(
|
||||
uintptr(query),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&counter)))
|
||||
if r != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &CounterInfo{
|
||||
PostName: pname,
|
||||
CounterName: cname,
|
||||
Counter: counter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetCounterValue get counter value from handle
|
||||
// adapted from https://github.com/mackerelio/mackerel-agent/
|
||||
func GetCounterValue(counter windows.Handle) (float64, error) {
|
||||
var value PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
|
||||
if r != 0 && r != PDH_INVALID_DATA {
|
||||
return 0.0, err
|
||||
}
|
||||
return value.DoubleValue, nil
|
||||
}
|
||||
|
||||
type Win32PerformanceCounter struct {
|
||||
PostName string
|
||||
CounterName string
|
||||
Query windows.Handle
|
||||
Counter windows.Handle
|
||||
}
|
||||
|
||||
func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) {
|
||||
query, err := CreateQuery()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
counter := Win32PerformanceCounter{
|
||||
Query: query,
|
||||
PostName: postName,
|
||||
CounterName: counterName,
|
||||
}
|
||||
r, _, err := PdhAddEnglishCounterW.Call(
|
||||
uintptr(counter.Query),
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&counter.Counter)),
|
||||
)
|
||||
if r != 0 {
|
||||
return nil, err
|
||||
}
|
||||
return &counter, nil
|
||||
}
|
||||
|
||||
func (w *Win32PerformanceCounter) GetValue() (float64, error) {
|
||||
r, _, err := PdhCollectQueryData.Call(uintptr(w.Query))
|
||||
if r != 0 && err != nil {
|
||||
if r == PDH_NO_DATA {
|
||||
return 0.0, fmt.Errorf("%w: this counter has not data", err)
|
||||
}
|
||||
return 0.0, err
|
||||
}
|
||||
|
||||
return GetCounterValue(w.Counter)
|
||||
}
|
||||
|
||||
func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) {
|
||||
return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// Convert paths using native DOS format like:
|
||||
//
|
||||
// "\Device\HarddiskVolume1\Windows\systemew\file.txt"
|
||||
//
|
||||
// into:
|
||||
//
|
||||
// "C:\Windows\systemew\file.txt"
|
||||
func ConvertDOSPath(p string) string {
|
||||
rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)
|
||||
|
||||
for d := 'A'; d <= 'Z'; d++ {
|
||||
szDeviceName := string(d) + ":"
|
||||
szTarget := make([]uint16, 512)
|
||||
ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))),
|
||||
uintptr(unsafe.Pointer(&szTarget[0])),
|
||||
uintptr(len(szTarget)))
|
||||
if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive {
|
||||
return filepath.Join(szDeviceName, p[len(rawDrive):])
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
21
internal/gopsutil/common/sleep.go
Normal file
21
internal/gopsutil/common/sleep.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Sleep awaits for provided interval.
|
||||
// Can be interrupted by context cancelation.
|
||||
func Sleep(ctx context.Context, interval time.Duration) error {
|
||||
timer := time.NewTimer(interval)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
return nil
|
||||
}
|
||||
}
|
187
internal/gopsutil/cpu/cpu.go
Normal file
187
internal/gopsutil/cpu/cpu.go
Normal file
|
@ -0,0 +1,187 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
// TimesStat contains the amounts of time the CPU has spent performing different
|
||||
// kinds of work. Time units are in seconds. It is based on linux /proc/stat file.
|
||||
type TimesStat struct {
|
||||
CPU string `json:"cpu"`
|
||||
User float64 `json:"user"`
|
||||
System float64 `json:"system"`
|
||||
Idle float64 `json:"idle"`
|
||||
Nice float64 `json:"nice"`
|
||||
Iowait float64 `json:"iowait"`
|
||||
Irq float64 `json:"irq"`
|
||||
Softirq float64 `json:"softirq"`
|
||||
Steal float64 `json:"steal"`
|
||||
Guest float64 `json:"guest"`
|
||||
GuestNice float64 `json:"guestNice"`
|
||||
}
|
||||
|
||||
type InfoStat struct {
|
||||
CPU int32 `json:"cpu"`
|
||||
VendorID string `json:"vendorId"`
|
||||
Family string `json:"family"`
|
||||
Model string `json:"model"`
|
||||
Stepping int32 `json:"stepping"`
|
||||
PhysicalID string `json:"physicalId"`
|
||||
CoreID string `json:"coreId"`
|
||||
Cores int32 `json:"cores"`
|
||||
ModelName string `json:"modelName"`
|
||||
Mhz float64 `json:"mhz"`
|
||||
CacheSize int32 `json:"cacheSize"`
|
||||
Flags []string `json:"flags"`
|
||||
Microcode string `json:"microcode"`
|
||||
}
|
||||
|
||||
type lastPercent struct {
|
||||
sync.Mutex
|
||||
lastCPUTimes []TimesStat
|
||||
lastPerCPUTimes []TimesStat
|
||||
}
|
||||
|
||||
var (
|
||||
lastCPUPercent lastPercent
|
||||
invoke common.Invoker = common.Invoke{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
lastCPUPercent.Lock()
|
||||
lastCPUPercent.lastCPUTimes, _ = Times(false)
|
||||
lastCPUPercent.lastPerCPUTimes, _ = Times(true)
|
||||
lastCPUPercent.Unlock()
|
||||
}
|
||||
|
||||
// Counts returns the number of physical or logical cores in the system
|
||||
func Counts(logical bool) (int, error) {
|
||||
return CountsWithContext(context.Background(), logical)
|
||||
}
|
||||
|
||||
func (c TimesStat) String() string {
|
||||
v := []string{
|
||||
`"cpu":"` + c.CPU + `"`,
|
||||
`"user":` + strconv.FormatFloat(c.User, 'f', 1, 64),
|
||||
`"system":` + strconv.FormatFloat(c.System, 'f', 1, 64),
|
||||
`"idle":` + strconv.FormatFloat(c.Idle, 'f', 1, 64),
|
||||
`"nice":` + strconv.FormatFloat(c.Nice, 'f', 1, 64),
|
||||
`"iowait":` + strconv.FormatFloat(c.Iowait, 'f', 1, 64),
|
||||
`"irq":` + strconv.FormatFloat(c.Irq, 'f', 1, 64),
|
||||
`"softirq":` + strconv.FormatFloat(c.Softirq, 'f', 1, 64),
|
||||
`"steal":` + strconv.FormatFloat(c.Steal, 'f', 1, 64),
|
||||
`"guest":` + strconv.FormatFloat(c.Guest, 'f', 1, 64),
|
||||
`"guestNice":` + strconv.FormatFloat(c.GuestNice, 'f', 1, 64),
|
||||
}
|
||||
|
||||
return `{` + strings.Join(v, ",") + `}`
|
||||
}
|
||||
|
||||
// Total returns the total number of seconds in a CPUTimesStat
|
||||
func (c TimesStat) Total() float64 {
|
||||
total := c.User + c.System + c.Nice + c.Iowait + c.Irq + c.Softirq +
|
||||
c.Steal + c.Idle
|
||||
return total
|
||||
}
|
||||
|
||||
func (c InfoStat) String() string {
|
||||
s, _ := json.Marshal(c)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func getAllBusy(t TimesStat) (float64, float64) {
|
||||
busy := t.User + t.System + t.Nice + t.Iowait + t.Irq +
|
||||
t.Softirq + t.Steal
|
||||
return busy + t.Idle, busy
|
||||
}
|
||||
|
||||
func calculateBusy(t1, t2 TimesStat) float64 {
|
||||
t1All, t1Busy := getAllBusy(t1)
|
||||
t2All, t2Busy := getAllBusy(t2)
|
||||
|
||||
if t2Busy <= t1Busy {
|
||||
return 0
|
||||
}
|
||||
if t2All <= t1All {
|
||||
return 100
|
||||
}
|
||||
return math.Min(100, math.Max(0, (t2Busy-t1Busy)/(t2All-t1All)*100))
|
||||
}
|
||||
|
||||
func calculateAllBusy(t1, t2 []TimesStat) ([]float64, error) {
|
||||
// Make sure the CPU measurements have the same length.
|
||||
if len(t1) != len(t2) {
|
||||
return nil, fmt.Errorf(
|
||||
"received two CPU counts: %d != %d",
|
||||
len(t1), len(t2),
|
||||
)
|
||||
}
|
||||
|
||||
ret := make([]float64, len(t1))
|
||||
for i, t := range t2 {
|
||||
ret[i] = calculateBusy(t1[i], t)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Returns one value per cpu, or a single value if percpu is set to false.
|
||||
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 {
|
||||
return percentUsedFromLastCall(percpu)
|
||||
}
|
||||
|
||||
// Get CPU usage at the start of the interval.
|
||||
cpuTimes1, err := Times(percpu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := common.Sleep(ctx, interval); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// And at the end of the interval.
|
||||
cpuTimes2, err := Times(percpu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return calculateAllBusy(cpuTimes1, cpuTimes2)
|
||||
}
|
||||
|
||||
func percentUsedFromLastCall(percpu bool) ([]float64, error) {
|
||||
cpuTimes, err := Times(percpu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastCPUPercent.Lock()
|
||||
defer lastCPUPercent.Unlock()
|
||||
var lastTimes []TimesStat
|
||||
if percpu {
|
||||
lastTimes = lastCPUPercent.lastPerCPUTimes
|
||||
lastCPUPercent.lastPerCPUTimes = cpuTimes
|
||||
} else {
|
||||
lastTimes = lastCPUPercent.lastCPUTimes
|
||||
lastCPUPercent.lastCPUTimes = cpuTimes
|
||||
}
|
||||
|
||||
if lastTimes == nil {
|
||||
return nil, fmt.Errorf("error getting times for cpu percent. lastTimes was nil")
|
||||
}
|
||||
return calculateAllBusy(lastTimes, cpuTimes)
|
||||
}
|
119
internal/gopsutil/cpu/cpu_darwin.go
Normal file
119
internal/gopsutil/cpu/cpu_darwin.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
//go:build darwin
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// sys/resource.h
|
||||
const (
|
||||
CPUser = 0
|
||||
CPNice = 1
|
||||
CPSys = 2
|
||||
CPIntr = 3
|
||||
CPIdle = 4
|
||||
CPUStates = 5
|
||||
)
|
||||
|
||||
// default value. from time.h
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("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) {
|
||||
if percpu {
|
||||
return perCPUTimes()
|
||||
}
|
||||
|
||||
return allCPUTimes()
|
||||
}
|
||||
|
||||
// Returns only one CPUInfoStat on FreeBSD
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
|
||||
c := InfoStat{}
|
||||
c.ModelName, _ = unix.Sysctl("machdep.cpu.brand_string")
|
||||
family, _ := unix.SysctlUint32("machdep.cpu.family")
|
||||
c.Family = strconv.FormatUint(uint64(family), 10)
|
||||
model, _ := unix.SysctlUint32("machdep.cpu.model")
|
||||
c.Model = strconv.FormatUint(uint64(model), 10)
|
||||
stepping, _ := unix.SysctlUint32("machdep.cpu.stepping")
|
||||
c.Stepping = int32(stepping)
|
||||
features, err := unix.Sysctl("machdep.cpu.features")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(features) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
leaf7Features, err := unix.Sysctl("machdep.cpu.leaf7_features")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(leaf7Features) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
extfeatures, err := unix.Sysctl("machdep.cpu.extfeatures")
|
||||
if err == nil {
|
||||
for _, v := range strings.Fields(extfeatures) {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
cores, _ := unix.SysctlUint32("machdep.cpu.core_count")
|
||||
c.Cores = int32(cores)
|
||||
cacheSize, _ := unix.SysctlUint32("machdep.cpu.cache.size")
|
||||
c.CacheSize = int32(cacheSize)
|
||||
c.VendorID, _ = unix.Sysctl("machdep.cpu.vendor")
|
||||
|
||||
// Use the rated frequency of the CPU. This is a static value and does not
|
||||
// account for low power or Turbo Boost modes.
|
||||
cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.Mhz = float64(cpuFrequency) / 1000000.0
|
||||
|
||||
return append(ret, c), nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
var cpuArgument string
|
||||
if logical {
|
||||
cpuArgument = "hw.logicalcpu"
|
||||
} else {
|
||||
cpuArgument = "hw.physicalcpu"
|
||||
}
|
||||
|
||||
count, err := unix.SysctlUint32(cpuArgument)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(count), nil
|
||||
}
|
110
internal/gopsutil/cpu/cpu_darwin_cgo.go
Normal file
110
internal/gopsutil/cpu/cpu_darwin_cgo.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
//go:build darwin && cgo
|
||||
|
||||
package cpu
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_info.h>
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_MAC
|
||||
#include <libproc.h>
|
||||
#endif
|
||||
#include <mach/processor_info.h>
|
||||
#include <mach/vm_map.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// these CPU times for darwin is borrowed from influxdb/telegraf.
|
||||
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
var (
|
||||
count C.mach_msg_type_number_t
|
||||
cpuload *C.processor_cpu_load_info_data_t
|
||||
ncpu C.natural_t
|
||||
)
|
||||
|
||||
status := C.host_processor_info(C.host_t(C.mach_host_self()),
|
||||
C.PROCESSOR_CPU_LOAD_INFO,
|
||||
&ncpu,
|
||||
(*C.processor_info_array_t)(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_processor_info error=%d", status)
|
||||
}
|
||||
|
||||
// jump through some cgo casting hoops and ensure we properly free
|
||||
// the memory that cpuload points to
|
||||
target := C.vm_map_t(C.mach_task_self_)
|
||||
address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload)))
|
||||
defer C.vm_deallocate(target, address, C.vm_size_t(ncpu))
|
||||
|
||||
// the body of struct processor_cpu_load_info
|
||||
// aka processor_cpu_load_info_data_t
|
||||
var cpu_ticks [C.CPU_STATE_MAX]uint32
|
||||
|
||||
// copy the cpuload array to a []byte buffer
|
||||
// where we can binary.Read the data
|
||||
size := int(ncpu) * binary.Size(cpu_ticks)
|
||||
buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size]
|
||||
|
||||
bbuf := bytes.NewBuffer(buf)
|
||||
|
||||
var ret []TimesStat
|
||||
|
||||
for i := 0; i < int(ncpu); i++ {
|
||||
err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", i),
|
||||
User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
|
||||
System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
|
||||
Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
|
||||
Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func allCPUTimes() ([]TimesStat, error) {
|
||||
var count C.mach_msg_type_number_t
|
||||
var cpuload C.host_cpu_load_info_data_t
|
||||
|
||||
count = C.HOST_CPU_LOAD_INFO_COUNT
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_CPU_LOAD_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&cpuload)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
c := TimesStat{
|
||||
CPU: "cpu-total",
|
||||
User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec,
|
||||
System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec,
|
||||
Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec,
|
||||
Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec,
|
||||
}
|
||||
|
||||
return []TimesStat{c}, nil
|
||||
}
|
13
internal/gopsutil/cpu/cpu_darwin_nocgo.go
Normal file
13
internal/gopsutil/cpu/cpu_darwin_nocgo.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
//go:build darwin && !cgo
|
||||
|
||||
package cpu
|
||||
|
||||
import "github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
|
||||
func perCPUTimes() ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func allCPUTimes() ([]TimesStat, error) {
|
||||
return []TimesStat{}, common.ErrNotImplementedError
|
||||
}
|
164
internal/gopsutil/cpu/cpu_dragonfly.go
Normal file
164
internal/gopsutil/cpu/cpu_dragonfly.go
Normal file
|
@ -0,0 +1,164 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var (
|
||||
ClocksPerSec = float64(128)
|
||||
cpuMatch = regexp.MustCompile(`^CPU:`)
|
||||
originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`)
|
||||
featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
|
||||
featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
|
||||
cpuEnd = regexp.MustCompile(`^Trying to mount root`)
|
||||
cpuTimesSize int
|
||||
emptyTimes cpuTimes
|
||||
)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("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 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) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
if percpu {
|
||||
buf, err := unix.SysctlRaw("kern.cp_times")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
buf, err := unix.SysctlRaw("kern.cp_time")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
|
||||
return []TimesStat{*timeStat("cpu-total", times)}, nil
|
||||
}
|
||||
|
||||
// Returns only one InfoStat on DragonflyBSD. The information regarding core
|
||||
// count, however is accurate and it is assumed that all InfoStat attributes
|
||||
// are the same across CPUs.
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
const dmesgBoot = "/var/run/dmesg.boot"
|
||||
|
||||
c, err := parseDmesgBoot(dmesgBoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var u32 uint32
|
||||
if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Mhz = float64(u32)
|
||||
|
||||
var num int
|
||||
var buf string
|
||||
if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
num = strings.Count(buf, "CHIP")
|
||||
c.Cores = int32(strings.Count(string(buf), "CORE") / num)
|
||||
|
||||
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]InfoStat, num)
|
||||
for i := 0; i < num; i++ {
|
||||
ret[i] = c
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseDmesgBoot(fileName string) (InfoStat, error) {
|
||||
c := InfoStat{}
|
||||
lines, _ := common.ReadLines(fileName)
|
||||
for _, line := range lines {
|
||||
if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
|
||||
break
|
||||
} else if matches := originMatch.FindStringSubmatch(line); matches != nil {
|
||||
c.VendorID = matches[1]
|
||||
t, err := strconv.ParseInt(matches[2], 10, 32)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err)
|
||||
}
|
||||
c.Stepping = int32(t)
|
||||
} else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
9
internal/gopsutil/cpu/cpu_dragonfly_amd64.go
Normal file
9
internal/gopsutil/cpu/cpu_dragonfly_amd64.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Intr uint64
|
||||
Idle uint64
|
||||
}
|
30
internal/gopsutil/cpu/cpu_fallback.go
Normal file
30
internal/gopsutil/cpu/cpu_fallback.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
//go:build !darwin && !linux && !freebsd && !openbsd && !solaris && !windows && !dragonfly
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
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) {
|
||||
return []InfoStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
176
internal/gopsutil/cpu/cpu_freebsd.go
Normal file
176
internal/gopsutil/cpu/cpu_freebsd.go
Normal file
|
@ -0,0 +1,176 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var (
|
||||
ClocksPerSec = float64(128)
|
||||
cpuMatch = regexp.MustCompile(`^CPU:`)
|
||||
originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`)
|
||||
featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
|
||||
featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
|
||||
cpuEnd = regexp.MustCompile(`^Trying to mount root`)
|
||||
cpuCores = regexp.MustCompile(`FreeBSD/SMP: (\d*) package\(s\) x (\d*) core\(s\)`)
|
||||
cpuTimesSize int
|
||||
emptyTimes cpuTimes
|
||||
)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("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 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) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
if percpu {
|
||||
buf, err := unix.SysctlRaw("kern.cp_times")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
buf, err := unix.SysctlRaw("kern.cp_time")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
|
||||
return []TimesStat{*timeStat("cpu-total", times)}, nil
|
||||
}
|
||||
|
||||
// Returns only one InfoStat on FreeBSD. The information regarding core
|
||||
// count, however is accurate and it is assumed that all InfoStat attributes
|
||||
// are the same across CPUs.
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
const dmesgBoot = "/var/run/dmesg.boot"
|
||||
|
||||
c, num, err := parseDmesgBoot(dmesgBoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var u32 uint32
|
||||
if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Mhz = float64(u32)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
ret := make([]InfoStat, num)
|
||||
for i := 0; i < num; i++ {
|
||||
ret[i] = c
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseDmesgBoot(fileName string) (InfoStat, int, error) {
|
||||
c := InfoStat{}
|
||||
lines, _ := common.ReadLines(fileName)
|
||||
cpuNum := 1 // default cpu num is 1
|
||||
for _, line := range lines {
|
||||
if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
|
||||
break
|
||||
} else if matches := originMatch.FindStringSubmatch(line); matches != nil {
|
||||
c.VendorID = matches[1]
|
||||
c.Family = matches[3]
|
||||
c.Model = matches[4]
|
||||
t, err := strconv.ParseInt(matches[5], 10, 32)
|
||||
if err != nil {
|
||||
return c, 0, fmt.Errorf("unable to parse FreeBSD CPU stepping information from %q: %v", line, err)
|
||||
}
|
||||
c.Stepping = int32(t)
|
||||
} else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil {
|
||||
for _, v := range strings.Split(matches[1], ",") {
|
||||
c.Flags = append(c.Flags, strings.ToLower(v))
|
||||
}
|
||||
} else if matches := cpuCores.FindStringSubmatch(line); matches != nil {
|
||||
t, err := strconv.ParseInt(matches[1], 10, 32)
|
||||
if err != nil {
|
||||
return c, 0, fmt.Errorf("unable to parse FreeBSD CPU Nums from %q: %v", line, err)
|
||||
}
|
||||
cpuNum = int(t)
|
||||
t2, err := strconv.ParseInt(matches[2], 10, 32)
|
||||
if err != nil {
|
||||
return c, 0, fmt.Errorf("unable to parse FreeBSD CPU cores from %q: %v", line, err)
|
||||
}
|
||||
c.Cores = int32(t2)
|
||||
}
|
||||
}
|
||||
|
||||
return c, cpuNum, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
9
internal/gopsutil/cpu/cpu_freebsd_386.go
Normal file
9
internal/gopsutil/cpu/cpu_freebsd_386.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint32
|
||||
Nice uint32
|
||||
Sys uint32
|
||||
Intr uint32
|
||||
Idle uint32
|
||||
}
|
9
internal/gopsutil/cpu/cpu_freebsd_amd64.go
Normal file
9
internal/gopsutil/cpu/cpu_freebsd_amd64.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Intr uint64
|
||||
Idle uint64
|
||||
}
|
9
internal/gopsutil/cpu/cpu_freebsd_arm.go
Normal file
9
internal/gopsutil/cpu/cpu_freebsd_arm.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint32
|
||||
Nice uint32
|
||||
Sys uint32
|
||||
Intr uint32
|
||||
Idle uint32
|
||||
}
|
9
internal/gopsutil/cpu/cpu_freebsd_arm64.go
Normal file
9
internal/gopsutil/cpu/cpu_freebsd_arm64.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package cpu
|
||||
|
||||
type cpuTimes struct {
|
||||
User uint64
|
||||
Nice uint64
|
||||
Sys uint64
|
||||
Intr uint64
|
||||
Idle uint64
|
||||
}
|
369
internal/gopsutil/cpu/cpu_linux.go
Normal file
369
internal/gopsutil/cpu/cpu_linux.go
Normal file
|
@ -0,0 +1,369 @@
|
|||
//go:build linux
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var ClocksPerSec = float64(100)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("getconf")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK")
|
||||
// ignore errors
|
||||
if err == nil {
|
||||
i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
|
||||
if err == nil {
|
||||
ClocksPerSec = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
filename := common.HostProc("stat")
|
||||
lines := []string{}
|
||||
if percpu {
|
||||
statlines, err := common.ReadLines(filename)
|
||||
if err != nil || len(statlines) < 2 {
|
||||
return []TimesStat{}, nil
|
||||
}
|
||||
for _, line := range statlines[1:] {
|
||||
if !strings.HasPrefix(line, "cpu") {
|
||||
break
|
||||
}
|
||||
lines = append(lines, line)
|
||||
}
|
||||
} else {
|
||||
lines, _ = common.ReadLinesOffsetN(filename, 0, 1)
|
||||
}
|
||||
|
||||
ret := make([]TimesStat, 0, len(lines))
|
||||
|
||||
for _, line := range lines {
|
||||
ct, err := parseStatLine(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, *ct)
|
||||
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func sysCPUPath(cpu int32, relPath string) string {
|
||||
return common.HostSys(fmt.Sprintf("devices/system/cpu/cpu%d", cpu), relPath)
|
||||
}
|
||||
|
||||
func finishCPUInfo(c *InfoStat) error {
|
||||
var lines []string
|
||||
var err error
|
||||
var value float64
|
||||
|
||||
if len(c.CoreID) == 0 {
|
||||
lines, err = common.ReadLines(sysCPUPath(c.CPU, "topology/core_id"))
|
||||
if err == nil {
|
||||
c.CoreID = lines[0]
|
||||
}
|
||||
}
|
||||
|
||||
// override the value of c.Mhz with cpufreq/cpuinfo_max_freq regardless
|
||||
// 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
|
||||
lines, err = common.ReadLines(sysCPUPath(c.CPU, "cpufreq/cpuinfo_max_freq"))
|
||||
// if we encounter errors below such as there are no cpuinfo_max_freq file,
|
||||
// we just ignore. so let Mhz is 0.
|
||||
if err != nil || len(lines) == 0 {
|
||||
return nil
|
||||
}
|
||||
value, err = strconv.ParseFloat(lines[0], 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
c.Mhz = value / 1000.0 // value is in kHz
|
||||
if c.Mhz > 9999 {
|
||||
c.Mhz = c.Mhz / 1000.0 // value in Hz
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CPUInfo on linux will return 1 item per physical thread.
|
||||
//
|
||||
// CPUs have three levels of counting: sockets, cores, threads.
|
||||
// Cores with HyperThreading count as having 2 threads per core.
|
||||
// Sockets often come with many physical CPU cores.
|
||||
// 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.
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
filename := common.HostProc("cpuinfo")
|
||||
lines, _ := common.ReadLines(filename)
|
||||
|
||||
var ret []InfoStat
|
||||
var processorName string
|
||||
|
||||
c := InfoStat{CPU: -1, Cores: 1}
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(fields[0])
|
||||
value := strings.TrimSpace(fields[1])
|
||||
|
||||
switch key {
|
||||
case "Processor":
|
||||
processorName = value
|
||||
case "processor":
|
||||
if c.CPU >= 0 {
|
||||
err := finishCPUInfo(&c)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
c = InfoStat{Cores: 1, ModelName: processorName}
|
||||
t, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.CPU = int32(t)
|
||||
case "vendorId", "vendor_id":
|
||||
c.VendorID = value
|
||||
case "cpu family":
|
||||
c.Family = value
|
||||
case "model":
|
||||
c.Model = value
|
||||
case "model name", "cpu":
|
||||
c.ModelName = value
|
||||
if strings.Contains(value, "POWER8") ||
|
||||
strings.Contains(value, "POWER7") {
|
||||
c.Model = strings.Split(value, " ")[0]
|
||||
c.Family = "POWER"
|
||||
c.VendorID = "IBM"
|
||||
}
|
||||
case "stepping", "revision":
|
||||
val := value
|
||||
|
||||
if key == "revision" {
|
||||
val = strings.Split(value, ".")[0]
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.Stepping = int32(t)
|
||||
case "cpu MHz", "clock":
|
||||
// treat this as the fallback value, thus we ignore error
|
||||
if t, err := strconv.ParseFloat(strings.Replace(value, "MHz", "", 1), 64); err == nil {
|
||||
c.Mhz = t
|
||||
}
|
||||
case "cache size":
|
||||
t, err := strconv.ParseInt(strings.Replace(value, " KB", "", 1), 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c.CacheSize = int32(t)
|
||||
case "physical id":
|
||||
c.PhysicalID = value
|
||||
case "core id":
|
||||
c.CoreID = value
|
||||
case "flags", "Features":
|
||||
c.Flags = strings.FieldsFunc(value, func(r rune) bool {
|
||||
return r == ',' || r == ' '
|
||||
})
|
||||
case "microcode":
|
||||
c.Microcode = value
|
||||
}
|
||||
}
|
||||
if c.CPU >= 0 {
|
||||
err := finishCPUInfo(&c)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseStatLine(line string) (*TimesStat, error) {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
if len(fields) == 0 {
|
||||
return nil, errors.New("stat does not contain cpu info")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(fields[0], "cpu") {
|
||||
return nil, errors.New("not contain cpu")
|
||||
}
|
||||
|
||||
cpu := fields[0]
|
||||
if cpu == "cpu" {
|
||||
cpu = "cpu-total"
|
||||
}
|
||||
user, err := strconv.ParseFloat(fields[1], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nice, err := strconv.ParseFloat(fields[2], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
system, err := strconv.ParseFloat(fields[3], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idle, err := strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iowait, err := strconv.ParseFloat(fields[5], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
irq, err := strconv.ParseFloat(fields[6], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
softirq, err := strconv.ParseFloat(fields[7], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ct := &TimesStat{
|
||||
CPU: cpu,
|
||||
User: user / ClocksPerSec,
|
||||
Nice: nice / ClocksPerSec,
|
||||
System: system / ClocksPerSec,
|
||||
Idle: idle / ClocksPerSec,
|
||||
Iowait: iowait / ClocksPerSec,
|
||||
Irq: irq / ClocksPerSec,
|
||||
Softirq: softirq / ClocksPerSec,
|
||||
}
|
||||
if len(fields) > 8 { // Linux >= 2.6.11
|
||||
steal, err := strconv.ParseFloat(fields[8], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct.Steal = steal / ClocksPerSec
|
||||
}
|
||||
if len(fields) > 9 { // Linux >= 2.6.24
|
||||
guest, err := strconv.ParseFloat(fields[9], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct.Guest = guest / ClocksPerSec
|
||||
}
|
||||
if len(fields) > 10 { // Linux >= 3.2.0
|
||||
guestNice, err := strconv.ParseFloat(fields[10], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct.GuestNice = guestNice / ClocksPerSec
|
||||
}
|
||||
|
||||
return ct, nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
if logical {
|
||||
ret := 0
|
||||
// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_pslinux.py#L599
|
||||
procCpuinfo := common.HostProc("cpuinfo")
|
||||
lines, err := common.ReadLines(procCpuinfo)
|
||||
if err == nil {
|
||||
for _, line := range lines {
|
||||
line = strings.ToLower(line)
|
||||
if strings.HasPrefix(line, "processor") {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
}
|
||||
if ret == 0 {
|
||||
procStat := common.HostProc("stat")
|
||||
lines, err = common.ReadLines(procStat)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, line := range lines {
|
||||
if len(line) >= 4 && strings.HasPrefix(line, "cpu") && '0' <= line[3] && line[3] <= '9' { // `^cpu\d` regexp matching
|
||||
ret++
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
// physical cores
|
||||
// https://github.com/giampaolo/psutil/blob/122174a10b75c9beebe15f6c07dcf3afbe3b120d/psutil/_pslinux.py#L621-L629
|
||||
threadSiblingsLists := make(map[string]bool)
|
||||
if files, err := filepath.Glob(common.HostSys("devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list")); err == nil {
|
||||
for _, file := range files {
|
||||
lines, err := common.ReadLines(file)
|
||||
if err != nil || len(lines) != 1 {
|
||||
continue
|
||||
}
|
||||
threadSiblingsLists[lines[0]] = true
|
||||
}
|
||||
ret := len(threadSiblingsLists)
|
||||
if ret != 0 {
|
||||
return ret, nil
|
||||
}
|
||||
}
|
||||
// https://github.com/giampaolo/psutil/blob/122174a10b75c9beebe15f6c07dcf3afbe3b120d/psutil/_pslinux.py#L631-L652
|
||||
filename := common.HostProc("cpuinfo")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
mapping := make(map[int]int)
|
||||
currentInfo := make(map[string]int)
|
||||
for _, line := range lines {
|
||||
line = strings.ToLower(strings.TrimSpace(line))
|
||||
if line == "" {
|
||||
// new section
|
||||
id, okID := currentInfo["physical id"]
|
||||
cores, okCores := currentInfo["cpu cores"]
|
||||
if okID && okCores {
|
||||
mapping[id] = cores
|
||||
}
|
||||
currentInfo = make(map[string]int)
|
||||
continue
|
||||
}
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
fields[0] = strings.TrimSpace(fields[0])
|
||||
if fields[0] == "physical id" || fields[0] == "cpu cores" {
|
||||
val, err := strconv.Atoi(strings.TrimSpace(fields[1]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
currentInfo[fields[0]] = val
|
||||
}
|
||||
}
|
||||
ret := 0
|
||||
for _, v := range mapping {
|
||||
ret += v
|
||||
}
|
||||
return ret, nil
|
||||
}
|
187
internal/gopsutil/cpu/cpu_openbsd.go
Normal file
187
internal/gopsutil/cpu/cpu_openbsd.go
Normal file
|
@ -0,0 +1,187 @@
|
|||
//go:build openbsd
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
// sys/sched.h
|
||||
var (
|
||||
CPUser = 0
|
||||
CPNice = 1
|
||||
CPSys = 2
|
||||
CPIntr = 3
|
||||
CPIdle = 4
|
||||
CPUStates = 5
|
||||
)
|
||||
|
||||
// sys/sysctl.h
|
||||
const (
|
||||
CTLKern = 1 // "high kernel": proc, limits
|
||||
CTLHw = 6 // CTL_HW
|
||||
SMT = 24 // HW_SMT
|
||||
KernCptime = 40 // KERN_CPTIME
|
||||
KernCptime2 = 71 // KERN_CPTIME2
|
||||
)
|
||||
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
func() {
|
||||
getconf, err := exec.LookPath("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() {
|
||||
v, err := unix.Sysctl("kern.osrelease") // can't reuse host.PlatformInformation because of circular import
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
v = strings.ToLower(v)
|
||||
version, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if version >= 6.4 {
|
||||
CPIntr = 4
|
||||
CPIdle = 5
|
||||
CPUStates = 6
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func smt() (bool, error) {
|
||||
mib := []int32{CTLHw, SMT}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var ret bool
|
||||
br := bytes.NewReader(buf)
|
||||
if err := binary.Read(br, binary.LittleEndian, &ret); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
var ret []TimesStat
|
||||
|
||||
var ncpu int
|
||||
if percpu {
|
||||
ncpu, _ = Counts(true)
|
||||
} else {
|
||||
ncpu = 1
|
||||
}
|
||||
|
||||
smt, err := smt()
|
||||
if err == syscall.EOPNOTSUPP {
|
||||
// if hw.smt is not applicable for this platform (e.g. i386),
|
||||
// pretend it's enabled
|
||||
smt = true
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < ncpu; i++ {
|
||||
j := i
|
||||
if !smt {
|
||||
j *= 2
|
||||
}
|
||||
|
||||
cpuTimes := make([]int32, CPUStates)
|
||||
var mib []int32
|
||||
if percpu {
|
||||
mib = []int32{CTLKern, KernCptime2, int32(j)}
|
||||
} else {
|
||||
mib = []int32{CTLKern, KernCptime}
|
||||
}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
br := bytes.NewReader(buf)
|
||||
err = binary.Read(br, binary.LittleEndian, &cpuTimes)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
c := TimesStat{
|
||||
User: float64(cpuTimes[CPUser]) / ClocksPerSec,
|
||||
Nice: float64(cpuTimes[CPNice]) / ClocksPerSec,
|
||||
System: float64(cpuTimes[CPSys]) / ClocksPerSec,
|
||||
Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec,
|
||||
Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec,
|
||||
}
|
||||
if percpu {
|
||||
c.CPU = fmt.Sprintf("cpu%d", j)
|
||||
} else {
|
||||
c.CPU = "cpu-total"
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Returns only one (minimal) CPUInfoStat on OpenBSD
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
var err error
|
||||
|
||||
c := InfoStat{}
|
||||
|
||||
mhz, err := unix.SysctlUint32("hw.cpuspeed")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Mhz = float64(mhz)
|
||||
|
||||
ncpu, err := unix.SysctlUint32("hw.ncpuonline")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Cores = int32(ncpu)
|
||||
|
||||
if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(ret, c), nil
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
286
internal/gopsutil/cpu/cpu_solaris.go
Normal file
286
internal/gopsutil/cpu/cpu_solaris.go
Normal file
|
@ -0,0 +1,286 @@
|
|||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ClocksPerSec = float64(128)
|
||||
|
||||
func init() {
|
||||
getconf, err := exec.LookPath("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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sum all values in a float64 map with float64 keys
|
||||
func msum(x map[float64]float64) float64 {
|
||||
total := 0.0
|
||||
for _, y := range x {
|
||||
total += y
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func Times(percpu bool) ([]TimesStat, error) {
|
||||
return TimesWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
|
||||
kstatSys, err := exec.LookPath("kstat")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find kstat: %s", err)
|
||||
}
|
||||
cpu := make(map[float64]float64)
|
||||
idle := make(map[float64]float64)
|
||||
user := make(map[float64]float64)
|
||||
kern := make(map[float64]float64)
|
||||
iowt := make(map[float64]float64)
|
||||
// swap := make(map[float64]float64)
|
||||
kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-p", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot execute kstat: %s", err)
|
||||
}
|
||||
re := regexp.MustCompile(`[:\s]+`)
|
||||
for _, line := range strings.Split(string(kstatSysOut), "\n") {
|
||||
fields := re.Split(line, -1)
|
||||
if fields[0] != "cpu_stat" {
|
||||
continue
|
||||
}
|
||||
cpuNumber, err := strconv.ParseFloat(fields[1], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse cpu number: %s", err)
|
||||
}
|
||||
cpu[cpuNumber] = cpuNumber
|
||||
switch fields[3] {
|
||||
case "idle":
|
||||
idle[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse idle: %s", err)
|
||||
}
|
||||
case "user":
|
||||
user[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse user: %s", err)
|
||||
}
|
||||
case "kernel":
|
||||
kern[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse kernel: %s", err)
|
||||
}
|
||||
case "iowait":
|
||||
iowt[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse iowait: %s", err)
|
||||
}
|
||||
//not sure how this translates, don't report, add to kernel, something else?
|
||||
/*case "swap":
|
||||
swap[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse swap: %s", err)
|
||||
} */
|
||||
}
|
||||
}
|
||||
ret := make([]TimesStat, 0, len(cpu))
|
||||
if percpu {
|
||||
for _, c := range cpu {
|
||||
ct := &TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", int(cpu[c])),
|
||||
Idle: idle[c] / ClocksPerSec,
|
||||
User: user[c] / ClocksPerSec,
|
||||
System: kern[c] / ClocksPerSec,
|
||||
Iowait: iowt[c] / ClocksPerSec,
|
||||
}
|
||||
ret = append(ret, *ct)
|
||||
}
|
||||
} else {
|
||||
ct := &TimesStat{
|
||||
CPU: "cpu-total",
|
||||
Idle: msum(idle) / ClocksPerSec,
|
||||
User: msum(user) / ClocksPerSec,
|
||||
System: msum(kern) / ClocksPerSec,
|
||||
Iowait: msum(iowt) / ClocksPerSec,
|
||||
}
|
||||
ret = append(ret, *ct)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
psrInfo, err := exec.LookPath("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("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
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
return runtime.NumCPU(), nil
|
||||
}
|
256
internal/gopsutil/cpu/cpu_windows.go
Normal file
256
internal/gopsutil/cpu/cpu_windows.go
Normal file
|
@ -0,0 +1,256 @@
|
|||
//go:build windows
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
"github.com/gofiber/fiber/v2/internal/wmi"
|
||||
)
|
||||
|
||||
var (
|
||||
procGetActiveProcessorCount = common.Modkernel32.NewProc("GetActiveProcessorCount")
|
||||
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
|
||||
)
|
||||
|
||||
type Win32_Processor struct {
|
||||
LoadPercentage *uint16
|
||||
Family uint16
|
||||
Manufacturer string
|
||||
Name string
|
||||
NumberOfLogicalProcessors uint32
|
||||
NumberOfCores uint32
|
||||
ProcessorID *string
|
||||
Stepping *string
|
||||
MaxClockSpeed uint32
|
||||
}
|
||||
|
||||
// SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
|
||||
// defined in windows api doc with the following
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-ntquerysysteminformation#system_processor_performance_information
|
||||
// additional fields documented here
|
||||
// https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/processor_performance.htm
|
||||
type win32_SystemProcessorPerformanceInformation struct {
|
||||
IdleTime int64 // idle time in 100ns (this is not a filetime).
|
||||
KernelTime int64 // kernel time in 100ns. kernel time includes idle time. (this is not a filetime).
|
||||
UserTime int64 // usertime in 100ns (this is not a filetime).
|
||||
DpcTime int64 // dpc time in 100ns (this is not a filetime).
|
||||
InterruptTime int64 // interrupt time in 100ns
|
||||
InterruptCount 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
|
||||
}
|
||||
|
||||
const (
|
||||
ClocksPerSec = 10000000.0
|
||||
|
||||
// systemProcessorPerformanceInformationClass information class to query with NTQuerySystemInformation
|
||||
// https://processhacker.sourceforge.io/doc/ntexapi_8h.html#ad5d815b48e8f4da1ef2eb7a2f18a54e0
|
||||
win32_SystemProcessorPerformanceInformationClass = 8
|
||||
|
||||
// size of systemProcessorPerformanceInfoSize in memory
|
||||
win32_SystemProcessorPerformanceInfoSize = uint32(unsafe.Sizeof(win32_SystemProcessorPerformanceInformation{}))
|
||||
)
|
||||
|
||||
// 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 lpKernelTime common.FILETIME
|
||||
var lpUserTime common.FILETIME
|
||||
r, _, _ := common.ProcGetSystemTimes.Call(
|
||||
uintptr(unsafe.Pointer(&lpIdleTime)),
|
||||
uintptr(unsafe.Pointer(&lpKernelTime)),
|
||||
uintptr(unsafe.Pointer(&lpUserTime)))
|
||||
if r == 0 {
|
||||
return ret, windows.GetLastError()
|
||||
}
|
||||
|
||||
LOT := float64(0.0000001)
|
||||
HIT := (LOT * 4294967296.0)
|
||||
idle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime)))
|
||||
user := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime)))
|
||||
kernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime)))
|
||||
system := (kernel - idle)
|
||||
|
||||
ret = append(ret, TimesStat{
|
||||
CPU: "cpu-total",
|
||||
Idle: float64(idle),
|
||||
User: float64(user),
|
||||
System: float64(system),
|
||||
})
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Info() ([]InfoStat, error) {
|
||||
return InfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
|
||||
var ret []InfoStat
|
||||
var dst []Win32_Processor
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
var procID string
|
||||
for i, l := range dst {
|
||||
procID = ""
|
||||
if l.ProcessorID != nil {
|
||||
procID = *l.ProcessorID
|
||||
}
|
||||
|
||||
cpu := InfoStat{
|
||||
CPU: int32(i),
|
||||
Family: fmt.Sprintf("%d", l.Family),
|
||||
VendorID: l.Manufacturer,
|
||||
ModelName: l.Name,
|
||||
Cores: int32(l.NumberOfLogicalProcessors),
|
||||
PhysicalID: procID,
|
||||
Mhz: float64(l.MaxClockSpeed),
|
||||
Flags: []string{},
|
||||
}
|
||||
ret = append(ret, cpu)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 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 core, v := range stats {
|
||||
c := TimesStat{
|
||||
CPU: fmt.Sprintf("cpu%d", core),
|
||||
User: float64(v.UserTime) / ClocksPerSec,
|
||||
System: float64(v.KernelTime-v.IdleTime) / ClocksPerSec,
|
||||
Idle: float64(v.IdleTime) / ClocksPerSec,
|
||||
Irq: float64(v.InterruptTime) / ClocksPerSec,
|
||||
}
|
||||
ret = append(ret, c)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// makes call to Windows API function to retrieve performance information for each core
|
||||
func perfInfo() ([]win32_SystemProcessorPerformanceInformation, error) {
|
||||
// Make maxResults large for safety.
|
||||
// We can't invoke the api call with a results array that's too small.
|
||||
// If we have more than 2056 cores on a single host, then it's probably the future.
|
||||
maxBuffer := 2056
|
||||
// buffer for results from the windows proc
|
||||
resultBuffer := make([]win32_SystemProcessorPerformanceInformation, maxBuffer)
|
||||
// size of the buffer in memory
|
||||
bufferSize := uintptr(win32_SystemProcessorPerformanceInfoSize) * uintptr(maxBuffer)
|
||||
// size of the returned response
|
||||
var retSize uint32
|
||||
|
||||
// Invoke windows api proc.
|
||||
// The returned err from the windows dll proc will always be non-nil even when successful.
|
||||
// See https://godoc.org/golang.org/x/sys/windows#LazyProc.Call for more information
|
||||
retCode, _, err := common.ProcNtQuerySystemInformation.Call(
|
||||
win32_SystemProcessorPerformanceInformationClass, // System Information Class -> SystemProcessorPerformanceInformation
|
||||
uintptr(unsafe.Pointer(&resultBuffer[0])), // pointer to first element in result buffer
|
||||
bufferSize, // size of the buffer in memory
|
||||
uintptr(unsafe.Pointer(&retSize)), // pointer to the size of the returned results the windows proc will set this
|
||||
)
|
||||
|
||||
// check return code for errors
|
||||
if retCode != 0 {
|
||||
return nil, fmt.Errorf("call to NtQuerySystemInformation returned %d. err: %s", retCode, err.Error())
|
||||
}
|
||||
|
||||
// calculate the number of returned elements based on the returned size
|
||||
numReturnedElements := retSize / win32_SystemProcessorPerformanceInfoSize
|
||||
|
||||
// trim results to the number of returned elements
|
||||
resultBuffer = resultBuffer[:numReturnedElements]
|
||||
|
||||
return resultBuffer, nil
|
||||
}
|
||||
|
||||
// SystemInfo is an equivalent representation of SYSTEM_INFO in the Windows API.
|
||||
// https://msdn.microsoft.com/en-us/library/ms724958%28VS.85%29.aspx?f=255&MSPPError=-2147217396
|
||||
// https://github.com/elastic/go-windows/blob/bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd/kernel32.go#L43
|
||||
type systemInfo struct {
|
||||
wProcessorArchitecture uint16
|
||||
wReserved uint16
|
||||
dwPageSize uint32
|
||||
lpMinimumApplicationAddress uintptr
|
||||
lpMaximumApplicationAddress uintptr
|
||||
dwActiveProcessorMask uintptr
|
||||
dwNumberOfProcessors uint32
|
||||
dwProcessorType uint32
|
||||
dwAllocationGranularity uint32
|
||||
wProcessorLevel uint16
|
||||
wProcessorRevision uint16
|
||||
}
|
||||
|
||||
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
|
||||
if logical {
|
||||
// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
|
||||
err := procGetActiveProcessorCount.Find()
|
||||
if err == nil { // Win7+
|
||||
ret, _, _ := procGetActiveProcessorCount.Call(uintptr(0xffff)) // ALL_PROCESSOR_GROUPS is 0xffff according to Rust's winapi lib https://docs.rs/winapi/*/x86_64-pc-windows-msvc/src/winapi/shared/ntdef.rs.html#120
|
||||
if ret != 0 {
|
||||
return int(ret), nil
|
||||
}
|
||||
}
|
||||
var systemInfo systemInfo
|
||||
_, _, err = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
|
||||
if systemInfo.dwNumberOfProcessors == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return int(systemInfo.dwNumberOfProcessors), nil
|
||||
}
|
||||
// physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499
|
||||
// for the time being, try with unreliable and slow WMI call…
|
||||
var dst []Win32_Processor
|
||||
q := wmi.CreateQuery(&dst, "")
|
||||
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var count uint32
|
||||
for _, d := range dst {
|
||||
count += d.NumberOfCores
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
30
internal/gopsutil/load/load.go
Normal file
30
internal/gopsutil/load/load.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package load
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// var invoke common.Invoker = common.Invoke{}
|
||||
type AvgStat struct {
|
||||
Load1 float64 `json:"load1"`
|
||||
Load5 float64 `json:"load5"`
|
||||
Load15 float64 `json:"load15"`
|
||||
}
|
||||
|
||||
func (l AvgStat) String() string {
|
||||
s, _ := json.Marshal(l)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type MiscStat struct {
|
||||
ProcsTotal int64 `json:"procsTotal"`
|
||||
ProcsCreated int64 `json:"procsCreated"`
|
||||
ProcsRunning int64 `json:"procsRunning"`
|
||||
ProcsBlocked int64 `json:"procsBlocked"`
|
||||
Ctxt int64 `json:"ctxt"`
|
||||
}
|
||||
|
||||
func (m MiscStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
84
internal/gopsutil/load/load_bsd.go
Normal file
84
internal/gopsutil/load/load_bsd.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
//go:build freebsd || openbsd
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var invoke common.Invoker = common.Invoke{}
|
||||
|
||||
func Avg() (*AvgStat, error) {
|
||||
return AvgWithContext(context.Background())
|
||||
}
|
||||
|
||||
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
// This SysctlRaw method borrowed from
|
||||
// https://github.com/prometheus/node_exporter/blob/master/collector/loadavg_freebsd.go
|
||||
type loadavg struct {
|
||||
load [3]uint32
|
||||
scale int
|
||||
}
|
||||
b, err := unix.SysctlRaw("vm.loadavg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
load := *(*loadavg)(unsafe.Pointer((&b[0])))
|
||||
scale := float64(load.scale)
|
||||
ret := &AvgStat{
|
||||
Load1: float64(load.load[0]) / scale,
|
||||
Load5: float64(load.load[1]) / scale,
|
||||
Load15: float64(load.load[2]) / scale,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type forkstat struct {
|
||||
forks int
|
||||
vforks int
|
||||
__tforks int
|
||||
}
|
||||
|
||||
// Misc returns miscellaneous host-wide statistics.
|
||||
// darwin use ps command to get process running/blocked count.
|
||||
// Almost same as Darwin implementation, but state is different.
|
||||
func Misc() (*MiscStat, error) {
|
||||
return MiscWithContext(context.Background())
|
||||
}
|
||||
|
||||
func MiscWithContext(ctx context.Context) (*MiscStat, error) {
|
||||
bin, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, bin, "axo", "state")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
ret := MiscStat{}
|
||||
for _, l := range lines {
|
||||
if strings.Contains(l, "R") {
|
||||
ret.ProcsRunning++
|
||||
} else if strings.Contains(l, "D") {
|
||||
ret.ProcsBlocked++
|
||||
}
|
||||
}
|
||||
|
||||
f, err := getForkStat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.ProcsCreated = int64(f.forks)
|
||||
|
||||
return &ret, nil
|
||||
}
|
75
internal/gopsutil/load/load_darwin.go
Normal file
75
internal/gopsutil/load/load_darwin.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
//go:build darwin
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var invoke common.Invoker = common.Invoke{}
|
||||
|
||||
func Avg() (*AvgStat, error) {
|
||||
return AvgWithContext(context.Background())
|
||||
}
|
||||
|
||||
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
// This SysctlRaw method borrowed from
|
||||
// https://github.com/prometheus/node_exporter/blob/master/collector/loadavg_freebsd.go
|
||||
// this implementation is common with BSDs
|
||||
type loadavg struct {
|
||||
load [3]uint32
|
||||
scale int
|
||||
}
|
||||
b, err := unix.SysctlRaw("vm.loadavg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
load := *(*loadavg)(unsafe.Pointer((&b[0])))
|
||||
scale := float64(load.scale)
|
||||
ret := &AvgStat{
|
||||
Load1: float64(load.load[0]) / scale,
|
||||
Load5: float64(load.load[1]) / scale,
|
||||
Load15: float64(load.load[2]) / scale,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Misc returnes miscellaneous host-wide statistics.
|
||||
// darwin use ps command to get process running/blocked count.
|
||||
// Almost same as FreeBSD implementation, but state is different.
|
||||
// U means 'Uninterruptible Sleep'.
|
||||
func Misc() (*MiscStat, error) {
|
||||
return MiscWithContext(context.Background())
|
||||
}
|
||||
|
||||
func MiscWithContext(ctx context.Context) (*MiscStat, error) {
|
||||
bin, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, bin, "axo", "state")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
ret := MiscStat{}
|
||||
for _, l := range lines {
|
||||
if strings.Contains(l, "R") {
|
||||
ret.ProcsRunning++
|
||||
} else if strings.Contains(l, "U") {
|
||||
// uninterruptible sleep == blocked
|
||||
ret.ProcsBlocked++
|
||||
}
|
||||
}
|
||||
|
||||
return &ret, nil
|
||||
}
|
25
internal/gopsutil/load/load_fallback.go
Normal file
25
internal/gopsutil/load/load_fallback.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
//go:build !darwin && !linux && !freebsd && !openbsd && !windows && !solaris
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func Avg() (*AvgStat, error) {
|
||||
return AvgWithContext(context.Background())
|
||||
}
|
||||
|
||||
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Misc() (*MiscStat, error) {
|
||||
return MiscWithContext(context.Background())
|
||||
}
|
||||
|
||||
func MiscWithContext(ctx context.Context) (*MiscStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
7
internal/gopsutil/load/load_freebsd.go
Normal file
7
internal/gopsutil/load/load_freebsd.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build freebsd
|
||||
|
||||
package load
|
||||
|
||||
func getForkStat() (forkstat, error) {
|
||||
return forkstat{}, nil
|
||||
}
|
135
internal/gopsutil/load/load_linux.go
Normal file
135
internal/gopsutil/load/load_linux.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
//go:build linux
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func Avg() (*AvgStat, error) {
|
||||
return AvgWithContext(context.Background())
|
||||
}
|
||||
|
||||
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
stat, err := fileAvgWithContext(ctx)
|
||||
if err != nil {
|
||||
stat, err = sysinfoAvgWithContext(ctx)
|
||||
}
|
||||
return stat, err
|
||||
}
|
||||
|
||||
func sysinfoAvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
var info syscall.Sysinfo_t
|
||||
err := syscall.Sysinfo(&info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
const si_load_shift = 16
|
||||
return &AvgStat{
|
||||
Load1: float64(info.Loads[0]) / float64(1<<si_load_shift),
|
||||
Load5: float64(info.Loads[1]) / float64(1<<si_load_shift),
|
||||
Load15: float64(info.Loads[2]) / float64(1<<si_load_shift),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func fileAvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
values, err := readLoadAvgFromFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
load1, err := strconv.ParseFloat(values[0], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
load5, err := strconv.ParseFloat(values[1], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
load15, err := strconv.ParseFloat(values[2], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &AvgStat{
|
||||
Load1: load1,
|
||||
Load5: load5,
|
||||
Load15: load15,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Misc returnes miscellaneous host-wide statistics.
|
||||
// Note: the name should be changed near future.
|
||||
func Misc() (*MiscStat, error) {
|
||||
return MiscWithContext(context.Background())
|
||||
}
|
||||
|
||||
func MiscWithContext(ctx context.Context) (*MiscStat, error) {
|
||||
filename := common.HostProc("stat")
|
||||
out, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &MiscStat{}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 2 {
|
||||
continue
|
||||
}
|
||||
v, err := strconv.ParseInt(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
switch fields[0] {
|
||||
case "processes":
|
||||
ret.ProcsCreated = v
|
||||
case "procs_running":
|
||||
ret.ProcsRunning = v
|
||||
case "procs_blocked":
|
||||
ret.ProcsBlocked = v
|
||||
case "ctxt":
|
||||
ret.Ctxt = v
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
procsTotal, err := getProcsTotal()
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret.ProcsTotal = procsTotal
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getProcsTotal() (int64, error) {
|
||||
values, err := readLoadAvgFromFile()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseInt(strings.Split(values[3], "/")[1], 10, 64)
|
||||
}
|
||||
|
||||
func readLoadAvgFromFile() ([]string, error) {
|
||||
loadavgFilename := common.HostProc("loadavg")
|
||||
line, err := os.ReadFile(loadavgFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
values := strings.Fields(string(line))
|
||||
return values, nil
|
||||
}
|
17
internal/gopsutil/load/load_openbsd.go
Normal file
17
internal/gopsutil/load/load_openbsd.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
//go:build openbsd
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getForkStat() (forkstat, error) {
|
||||
b, err := unix.SysctlRaw("kern.forkstat")
|
||||
if err != nil {
|
||||
return forkstat{}, err
|
||||
}
|
||||
return *(*forkstat)(unsafe.Pointer((&b[0]))), nil
|
||||
}
|
44
internal/gopsutil/load/load_solaris.go
Normal file
44
internal/gopsutil/load/load_solaris.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
//go:build solaris
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func Avg() (*AvgStat, error) {
|
||||
return AvgWithContext(context.Background())
|
||||
}
|
||||
|
||||
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Misc() (*MiscStat, error) {
|
||||
return MiscWithContext(context.Background())
|
||||
}
|
||||
|
||||
func MiscWithContext(ctx context.Context) (*MiscStat, error) {
|
||||
bin, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, bin, "-efo", "s")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
ret := MiscStat{}
|
||||
for _, l := range lines {
|
||||
if l == "O" {
|
||||
ret.ProcsRunning++
|
||||
}
|
||||
}
|
||||
|
||||
return &ret, nil
|
||||
}
|
84
internal/gopsutil/load/load_windows.go
Normal file
84
internal/gopsutil/load/load_windows.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
//go:build windows
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var (
|
||||
loadErr error
|
||||
loadAvg1M float64 = 0.0
|
||||
loadAvg5M float64 = 0.0
|
||||
loadAvg15M float64 = 0.0
|
||||
loadAvgMutex sync.RWMutex
|
||||
loadAvgGoroutineOnce sync.Once
|
||||
)
|
||||
|
||||
// loadAvgGoroutine updates avg data by fetching current load by interval
|
||||
// TODO instead of this goroutine, we can register a Win32 counter just as psutil does
|
||||
// see https://psutil.readthedocs.io/en/latest/#psutil.getloadavg
|
||||
// code https://github.com/giampaolo/psutil/blob/8415355c8badc9c94418b19bdf26e622f06f0cce/psutil/arch/windows/wmi.c
|
||||
func loadAvgGoroutine() {
|
||||
var (
|
||||
samplingFrequency time.Duration = 5 * time.Second
|
||||
loadAvgFactor1M float64 = 1 / math.Exp(samplingFrequency.Seconds()/time.Minute.Seconds())
|
||||
loadAvgFactor5M float64 = 1 / math.Exp(samplingFrequency.Seconds()/(5*time.Minute).Seconds())
|
||||
loadAvgFactor15M float64 = 1 / math.Exp(samplingFrequency.Seconds()/(15*time.Minute).Seconds())
|
||||
currentLoad float64
|
||||
)
|
||||
|
||||
counter, err := common.ProcessorQueueLengthCounter()
|
||||
if err != nil || counter == nil {
|
||||
log.Println("gopsutil: unexpected processor queue length counter error, please file an issue on github: err")
|
||||
return
|
||||
}
|
||||
|
||||
tick := time.NewTicker(samplingFrequency).C
|
||||
for {
|
||||
currentLoad, err = counter.GetValue()
|
||||
loadAvgMutex.Lock()
|
||||
loadErr = err
|
||||
loadAvg1M = loadAvg1M*loadAvgFactor1M + currentLoad*(1-loadAvgFactor1M)
|
||||
loadAvg5M = loadAvg5M*loadAvgFactor5M + currentLoad*(1-loadAvgFactor5M)
|
||||
loadAvg15M = loadAvg15M*loadAvgFactor15M + currentLoad*(1-loadAvgFactor15M)
|
||||
loadAvgMutex.Unlock()
|
||||
<-tick
|
||||
}
|
||||
}
|
||||
|
||||
// Avg for Windows may return 0 values for the first few 5 second intervals
|
||||
func Avg() (*AvgStat, error) {
|
||||
return AvgWithContext(context.Background())
|
||||
}
|
||||
|
||||
func AvgWithContext(ctx context.Context) (*AvgStat, error) {
|
||||
loadAvgGoroutineOnce.Do(func() {
|
||||
go loadAvgGoroutine()
|
||||
})
|
||||
loadAvgMutex.RLock()
|
||||
defer loadAvgMutex.RUnlock()
|
||||
ret := AvgStat{
|
||||
Load1: loadAvg1M,
|
||||
Load5: loadAvg5M,
|
||||
Load15: loadAvg15M,
|
||||
}
|
||||
|
||||
return &ret, loadErr
|
||||
}
|
||||
|
||||
func Misc() (*MiscStat, error) {
|
||||
return MiscWithContext(context.Background())
|
||||
}
|
||||
|
||||
func MiscWithContext(ctx context.Context) (*MiscStat, error) {
|
||||
ret := MiscStat{}
|
||||
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
106
internal/gopsutil/mem/mem.go
Normal file
106
internal/gopsutil/mem/mem.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package mem
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
//lint:ignore U1000 we need this elsewhere
|
||||
var invoke common.Invoker = common.Invoke{} //nolint:all
|
||||
|
||||
// Memory usage statistics. Total, Available and Used contain numbers of bytes
|
||||
// for human consumption.
|
||||
//
|
||||
// The other fields in this struct contain kernel specific values.
|
||||
type VirtualMemoryStat struct {
|
||||
// Total amount of RAM on this system
|
||||
Total uint64 `json:"total"`
|
||||
|
||||
// RAM available for programs to allocate
|
||||
//
|
||||
// This value is computed from the kernel specific values.
|
||||
Available uint64 `json:"available"`
|
||||
|
||||
// RAM used by programs
|
||||
//
|
||||
// This value is computed from the kernel specific values.
|
||||
Used uint64 `json:"used"`
|
||||
|
||||
// Percentage of RAM used by programs
|
||||
//
|
||||
// This value is computed from the kernel specific values.
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
|
||||
// This is the kernel's notion of free memory; RAM chips whose bits nobody
|
||||
// cares about the value of right now. For a human consumable number,
|
||||
// Available is what you really want.
|
||||
Free uint64 `json:"free"`
|
||||
|
||||
// OS X / BSD specific numbers:
|
||||
// http://www.macyourself.com/2010/02/17/what-is-free-wired-active-and-inactive-system-memory-ram/
|
||||
Active uint64 `json:"active"`
|
||||
Inactive uint64 `json:"inactive"`
|
||||
Wired uint64 `json:"wired"`
|
||||
|
||||
// FreeBSD specific numbers:
|
||||
// https://reviews.freebsd.org/D8467
|
||||
Laundry uint64 `json:"laundry"`
|
||||
|
||||
// Linux specific numbers
|
||||
// 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/vm/overcommit-accounting
|
||||
Buffers uint64 `json:"buffers"`
|
||||
Cached uint64 `json:"cached"`
|
||||
Writeback uint64 `json:"writeback"`
|
||||
Dirty uint64 `json:"dirty"`
|
||||
WritebackTmp uint64 `json:"writebacktmp"`
|
||||
Shared uint64 `json:"shared"`
|
||||
Slab uint64 `json:"slab"`
|
||||
SReclaimable uint64 `json:"sreclaimable"`
|
||||
SUnreclaim uint64 `json:"sunreclaim"`
|
||||
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 {
|
||||
Total uint64 `json:"total"`
|
||||
Used uint64 `json:"used"`
|
||||
Free uint64 `json:"free"`
|
||||
UsedPercent float64 `json:"usedPercent"`
|
||||
Sin uint64 `json:"sin"`
|
||||
Sout uint64 `json:"sout"`
|
||||
PgIn uint64 `json:"pgin"`
|
||||
PgOut uint64 `json:"pgout"`
|
||||
PgFault uint64 `json:"pgfault"`
|
||||
|
||||
// Linux specific numbers
|
||||
// https://www.kernel.org/doc/Documentation/cgroup-v2.txt
|
||||
PgMajFault uint64 `json:"pgmajfault"`
|
||||
}
|
||||
|
||||
func (m VirtualMemoryStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (m SwapMemoryStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
69
internal/gopsutil/mem/mem_darwin.go
Normal file
69
internal/gopsutil/mem/mem_darwin.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
//go:build darwin
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getHwMemsize() (uint64, error) {
|
||||
totalString, err := unix.Sysctl("hw.memsize")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// unix.sysctl() helpfully assumes the result is a null-terminated string and
|
||||
// removes the last byte of the result if it's 0 :/
|
||||
totalString += "\x00"
|
||||
|
||||
total := uint64(binary.LittleEndian.Uint64([]byte(totalString)))
|
||||
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// xsw_usage in sys/sysctl.h
|
||||
type swapUsage struct {
|
||||
Total uint64
|
||||
Avail uint64
|
||||
Used uint64
|
||||
Pagesize int32
|
||||
Encrypted bool
|
||||
}
|
||||
|
||||
// SwapMemory returns swapinfo.
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
// https://github.com/yanllearnn/go-osstat/blob/ae8a279d26f52ec946a03698c7f50a26cfb427e3/memory/memory_darwin.go
|
||||
var ret *SwapMemoryStat
|
||||
|
||||
value, err := unix.SysctlRaw("vm.swapusage")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
if len(value) != 32 {
|
||||
return ret, fmt.Errorf("unexpected output of sysctl vm.swapusage: %v (len: %d)", value, len(value))
|
||||
}
|
||||
swap := (*swapUsage)(unsafe.Pointer(&value[0]))
|
||||
|
||||
u := float64(0)
|
||||
if swap.Total != 0 {
|
||||
u = ((float64(swap.Total) - float64(swap.Avail)) / float64(swap.Total)) * 100.0
|
||||
}
|
||||
|
||||
ret = &SwapMemoryStat{
|
||||
Total: swap.Total,
|
||||
Used: swap.Used,
|
||||
Free: swap.Avail,
|
||||
UsedPercent: u,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
58
internal/gopsutil/mem/mem_darwin_cgo.go
Normal file
58
internal/gopsutil/mem/mem_darwin_cgo.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
//go:build darwin && cgo
|
||||
|
||||
package mem
|
||||
|
||||
/*
|
||||
#include <mach/mach_host.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// VirtualMemory returns VirtualmemoryStat.
|
||||
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)
|
||||
var vmstat C.vm_statistics_data_t
|
||||
|
||||
status := C.host_statistics(C.host_t(C.mach_host_self()),
|
||||
C.HOST_VM_INFO,
|
||||
C.host_info_t(unsafe.Pointer(&vmstat)),
|
||||
&count)
|
||||
|
||||
if status != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("host_statistics error=%d", status)
|
||||
}
|
||||
|
||||
pageSize := uint64(unix.Getpagesize())
|
||||
total, err := getHwMemsize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalCount := C.natural_t(total / pageSize)
|
||||
|
||||
availableCount := vmstat.inactive_count + vmstat.free_count
|
||||
usedPercent := 100 * float64(totalCount-availableCount) / float64(totalCount)
|
||||
|
||||
usedCount := totalCount - availableCount
|
||||
|
||||
return &VirtualMemoryStat{
|
||||
Total: total,
|
||||
Available: pageSize * uint64(availableCount),
|
||||
Used: pageSize * uint64(usedCount),
|
||||
UsedPercent: usedPercent,
|
||||
Free: pageSize * uint64(vmstat.free_count),
|
||||
Active: pageSize * uint64(vmstat.active_count),
|
||||
Inactive: pageSize * uint64(vmstat.inactive_count),
|
||||
Wired: pageSize * uint64(vmstat.wire_count),
|
||||
}, nil
|
||||
}
|
93
internal/gopsutil/mem/mem_darwin_nocgo.go
Normal file
93
internal/gopsutil/mem/mem_darwin_nocgo.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
//go:build darwin && !cgo
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Runs vm_stat and returns Free and inactive pages
|
||||
func getVMStat(vms *VirtualMemoryStat) error {
|
||||
vm_stat, err := exec.LookPath("vm_stat")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := invoke.Command(vm_stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return parseVMStat(string(out), vms)
|
||||
}
|
||||
|
||||
func parseVMStat(out string, vms *VirtualMemoryStat) error {
|
||||
var err error
|
||||
|
||||
lines := strings.Split(out, "\n")
|
||||
pagesize := uint64(unix.Getpagesize())
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(fields[0])
|
||||
value := strings.Trim(fields[1], " .")
|
||||
switch key {
|
||||
case "Pages free":
|
||||
free, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Free = free * pagesize
|
||||
case "Pages inactive":
|
||||
inactive, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Inactive = inactive * pagesize
|
||||
case "Pages active":
|
||||
active, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Active = active * pagesize
|
||||
case "Pages wired down":
|
||||
wired, e := strconv.ParseUint(value, 10, 64)
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
vms.Wired = wired * pagesize
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// VirtualMemory returns VirtualmemoryStat.
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
ret := &VirtualMemoryStat{}
|
||||
|
||||
total, err := getHwMemsize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = getVMStat(ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret.Available = ret.Free + ret.Inactive
|
||||
ret.Total = total
|
||||
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = 100 * float64(ret.Used) / float64(ret.Total)
|
||||
|
||||
return ret, nil
|
||||
}
|
25
internal/gopsutil/mem/mem_fallback.go
Normal file
25
internal/gopsutil/mem/mem_fallback.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
//go:build !darwin && !linux && !freebsd && !openbsd && !solaris && !windows
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
167
internal/gopsutil/mem/mem_freebsd.go
Normal file
167
internal/gopsutil/mem/mem_freebsd.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
//go:build freebsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
pageSize, err := common.SysctlUint("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
physmem, err := common.SysctlUint("hw.physmem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
free, err := common.SysctlUint("vm.stats.vm.v_free_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
active, err := common.SysctlUint("vm.stats.vm.v_active_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inactive, err := common.SysctlUint("vm.stats.vm.v_inactive_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffers, err := common.SysctlUint("vfs.bufspace")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wired, err := common.SysctlUint("vm.stats.vm.v_wire_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cached, laundry uint64
|
||||
osreldate, _ := common.SysctlUint("kern.osreldate")
|
||||
if osreldate < 1102000 {
|
||||
cached, err = common.SysctlUint("vm.stats.vm.v_cache_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
laundry, err = common.SysctlUint("vm.stats.vm.v_laundry_count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p := pageSize
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: physmem,
|
||||
Free: free * p,
|
||||
Active: active * p,
|
||||
Inactive: inactive * p,
|
||||
Cached: cached * p,
|
||||
Buffers: buffers,
|
||||
Wired: wired * p,
|
||||
Laundry: laundry * p,
|
||||
}
|
||||
|
||||
ret.Available = ret.Inactive + ret.Cached + ret.Free + ret.Laundry
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Return swapinfo
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
// Constants from vm/vm_param.h
|
||||
// nolint: golint
|
||||
const (
|
||||
XSWDEV_VERSION11 = 1
|
||||
XSWDEV_VERSION = 2
|
||||
)
|
||||
|
||||
// Types from vm/vm_param.h
|
||||
type xswdev struct {
|
||||
Version uint32 // Version is the version
|
||||
Dev uint64 // Dev is the device identifier
|
||||
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
|
||||
}
|
||||
|
||||
// xswdev11 is a compatibility for under FreeBSD 11
|
||||
// sys/vm/swap_pager.c
|
||||
type xswdev11 struct {
|
||||
Version uint32 // Version is the version
|
||||
Dev uint32 // Dev is the device identifier
|
||||
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
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
// FreeBSD can have multiple swap devices so we total them up
|
||||
i, err := common.SysctlUint("vm.nswapdev")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
return nil, errors.New("no swap devices found")
|
||||
}
|
||||
|
||||
c := int(i)
|
||||
|
||||
i, err = common.SysctlUint("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageSize := i
|
||||
|
||||
var buf []byte
|
||||
s := &SwapMemoryStat{}
|
||||
for n := 0; n < c; n++ {
|
||||
buf, err = unix.SysctlRaw("vm.swap_info", n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// first, try to parse with version 2
|
||||
xsw := (*xswdev)(unsafe.Pointer(&buf[0]))
|
||||
if xsw.Version == XSWDEV_VERSION11 {
|
||||
// this is version 1, so try to parse again
|
||||
xsw := (*xswdev11)(unsafe.Pointer(&buf[0]))
|
||||
if xsw.Version != XSWDEV_VERSION11 {
|
||||
return nil, errors.New("xswdev version mismatch(11)")
|
||||
}
|
||||
s.Total += uint64(xsw.NBlks)
|
||||
s.Used += uint64(xsw.Used)
|
||||
} else if xsw.Version != XSWDEV_VERSION {
|
||||
return nil, errors.New("xswdev version mismatch")
|
||||
} else {
|
||||
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
|
||||
}
|
283
internal/gopsutil/mem/mem_linux.go
Normal file
283
internal/gopsutil/mem/mem_linux.go
Normal file
|
@ -0,0 +1,283 @@
|
|||
//go:build linux
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
type VirtualMemoryExStat struct {
|
||||
ActiveFile uint64 `json:"activefile"`
|
||||
InactiveFile uint64 `json:"inactivefile"`
|
||||
ActiveAnon uint64 `json:"activeanon"`
|
||||
InactiveAnon uint64 `json:"inactiveanon"`
|
||||
Unevictable uint64 `json:"unevictable"`
|
||||
}
|
||||
|
||||
func (v VirtualMemoryExStat) String() string {
|
||||
s, _ := json.Marshal(v)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
vm, _, err := fillFromMeminfoWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
func VirtualMemoryEx() (*VirtualMemoryExStat, error) {
|
||||
return VirtualMemoryExWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryExWithContext(ctx context.Context) (*VirtualMemoryExStat, error) {
|
||||
_, vmEx, err := fillFromMeminfoWithContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vmEx, nil
|
||||
}
|
||||
|
||||
func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *VirtualMemoryExStat, error) {
|
||||
filename := common.HostProc("meminfo")
|
||||
lines, _ := common.ReadLines(filename)
|
||||
|
||||
// flag if MemAvailable is in /proc/meminfo (kernel 3.14+)
|
||||
memavail := false
|
||||
activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008
|
||||
inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008
|
||||
sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006
|
||||
|
||||
ret := &VirtualMemoryStat{}
|
||||
retEx := &VirtualMemoryExStat{}
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ":")
|
||||
if len(fields) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(fields[0])
|
||||
value := strings.TrimSpace(fields[1])
|
||||
value = strings.Replace(value, " kB", "", -1)
|
||||
|
||||
t, err := strconv.ParseUint(value, 10, 64)
|
||||
if err != nil {
|
||||
return ret, retEx, err
|
||||
}
|
||||
switch key {
|
||||
case "MemTotal":
|
||||
ret.Total = t * 1024
|
||||
case "MemFree":
|
||||
ret.Free = t * 1024
|
||||
case "MemAvailable":
|
||||
memavail = true
|
||||
ret.Available = t * 1024
|
||||
case "Buffers":
|
||||
ret.Buffers = t * 1024
|
||||
case "Cached":
|
||||
ret.Cached = t * 1024
|
||||
case "Active":
|
||||
ret.Active = t * 1024
|
||||
case "Inactive":
|
||||
ret.Inactive = t * 1024
|
||||
case "Active(anon)":
|
||||
retEx.ActiveAnon = t * 1024
|
||||
case "Inactive(anon)":
|
||||
retEx.InactiveAnon = t * 1024
|
||||
case "Active(file)":
|
||||
activeFile = true
|
||||
retEx.ActiveFile = t * 1024
|
||||
case "Inactive(file)":
|
||||
inactiveFile = true
|
||||
retEx.InactiveFile = t * 1024
|
||||
case "Unevictable":
|
||||
retEx.Unevictable = t * 1024
|
||||
case "Writeback":
|
||||
ret.Writeback = t * 1024
|
||||
case "WritebackTmp":
|
||||
ret.WritebackTmp = t * 1024
|
||||
case "Dirty":
|
||||
ret.Dirty = t * 1024
|
||||
case "Shmem":
|
||||
ret.Shared = t * 1024
|
||||
case "Slab":
|
||||
ret.Slab = t * 1024
|
||||
case "SReclaimable":
|
||||
sReclaimable = true
|
||||
ret.SReclaimable = t * 1024
|
||||
case "SUnreclaim":
|
||||
ret.SUnreclaim = t * 1024
|
||||
case "PageTables":
|
||||
ret.PageTables = t * 1024
|
||||
case "SwapCached":
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
ret.Cached += ret.SReclaimable
|
||||
|
||||
if !memavail {
|
||||
if activeFile && inactiveFile && sReclaimable {
|
||||
ret.Available = calcuateAvailVmem(ret, retEx)
|
||||
} else {
|
||||
ret.Available = ret.Cached + ret.Free
|
||||
}
|
||||
}
|
||||
|
||||
ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
return ret, retEx, nil
|
||||
}
|
||||
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
sysinfo := &unix.Sysinfo_t{}
|
||||
|
||||
if err := unix.Sysinfo(sysinfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &SwapMemoryStat{
|
||||
Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit),
|
||||
Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit),
|
||||
}
|
||||
ret.Used = ret.Total - ret.Free
|
||||
// check Infinity
|
||||
if ret.Total != 0 {
|
||||
ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0
|
||||
} else {
|
||||
ret.UsedPercent = 0
|
||||
}
|
||||
filename := common.HostProc("vmstat")
|
||||
lines, _ := common.ReadLines(filename)
|
||||
for _, l := range lines {
|
||||
fields := strings.Fields(l)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
switch fields[0] {
|
||||
case "pswpin":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.Sin = value * 4 * 1024
|
||||
case "pswpout":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.Sout = value * 4 * 1024
|
||||
case "pgpgin":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgIn = value * 4 * 1024
|
||||
case "pgpgout":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgOut = value * 4 * 1024
|
||||
case "pgfault":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgFault = value * 4 * 1024
|
||||
case "pgmajfault":
|
||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ret.PgMajFault = value * 4 * 1024
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// calcuateAvailVmem is a fallback under kernel 3.14 where /proc/meminfo does not provide
|
||||
// "MemAvailable:" column. It reimplements an algorithm from the link below
|
||||
// https://github.com/giampaolo/psutil/pull/890
|
||||
func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint64 {
|
||||
var watermarkLow uint64
|
||||
|
||||
fn := common.HostProc("zoneinfo")
|
||||
lines, err := common.ReadLines(fn)
|
||||
if err != nil {
|
||||
return ret.Free + ret.Cached // fallback under kernel 2.6.13
|
||||
}
|
||||
|
||||
pagesize := uint64(os.Getpagesize())
|
||||
watermarkLow = 0
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
|
||||
if strings.HasPrefix(fields[0], "low") {
|
||||
lowValue, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
lowValue = 0
|
||||
}
|
||||
watermarkLow += lowValue
|
||||
}
|
||||
}
|
||||
|
||||
watermarkLow *= pagesize
|
||||
|
||||
availMemory := ret.Free - watermarkLow
|
||||
pageCache := retEx.ActiveFile + retEx.InactiveFile
|
||||
pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow)))
|
||||
availMemory += pageCache
|
||||
availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow)))
|
||||
|
||||
return availMemory
|
||||
}
|
106
internal/gopsutil/mem/mem_openbsd.go
Normal file
106
internal/gopsutil/mem/mem_openbsd.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
//go:build openbsd
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func GetPageSize() (uint64, error) {
|
||||
return GetPageSizeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func GetPageSizeWithContext(ctx context.Context) (uint64, error) {
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(uvmexp.Pagesize), nil
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
uvmexp, err := unix.SysctlUvmexp("vm.uvmexp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := uint64(uvmexp.Pagesize)
|
||||
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: uint64(uvmexp.Npages) * p,
|
||||
Free: uint64(uvmexp.Free) * p,
|
||||
Active: uint64(uvmexp.Active) * p,
|
||||
Inactive: uint64(uvmexp.Inactive) * p,
|
||||
Cached: 0, // not available
|
||||
Wired: uint64(uvmexp.Wired) * p,
|
||||
}
|
||||
|
||||
ret.Available = ret.Inactive + ret.Cached + ret.Free
|
||||
ret.Used = ret.Total - ret.Available
|
||||
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
||||
|
||||
mib := []int32{CTLVfs, VfsGeneric, VfsBcacheStat}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length < sizeOfBcachestats {
|
||||
return nil, fmt.Errorf("short syscall ret %d bytes", length)
|
||||
}
|
||||
var bcs Bcachestats
|
||||
br := bytes.NewReader(buf)
|
||||
err = common.Read(br, binary.LittleEndian, &bcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Buffers = uint64(bcs.Numbufpages) * p
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Return swapctl summary info
|
||||
func SwapMemory() (*SwapMemoryStat, error) {
|
||||
return SwapMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
||||
swapctl, err := exec.LookPath("swapctl")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := invoke.CommandWithContext(ctx, swapctl, "-sk")
|
||||
if err != nil {
|
||||
return &SwapMemoryStat{}, nil
|
||||
}
|
||||
|
||||
line := string(out)
|
||||
var total, used, free uint64
|
||||
|
||||
_, err = fmt.Sscanf(line,
|
||||
"total: %d 1K-blocks allocated, %d used, %d available",
|
||||
&total, &used, &free)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to parse swapctl output")
|
||||
}
|
||||
|
||||
percent := float64(used) / float64(total) * 100
|
||||
return &SwapMemoryStat{
|
||||
Total: total * 1024,
|
||||
Used: used * 1024,
|
||||
Free: free * 1024,
|
||||
UsedPercent: percent,
|
||||
}, nil
|
||||
}
|
37
internal/gopsutil/mem/mem_openbsd_386.go
Normal file
37
internal/gopsutil/mem/mem_openbsd_386.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
//go:build openbsd && 386
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs mem/types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x90
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
Highflips int64
|
||||
Highflops int64
|
||||
Dmaflips int64
|
||||
}
|
32
internal/gopsutil/mem/mem_openbsd_amd64.go
Normal file
32
internal/gopsutil/mem/mem_openbsd_amd64.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_openbsd.go
|
||||
|
||||
package mem
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = 0x78
|
||||
)
|
||||
|
||||
type Bcachestats struct {
|
||||
Numbufs int64
|
||||
Numbufpages int64
|
||||
Numdirtypages int64
|
||||
Numcleanpages int64
|
||||
Pendingwrites int64
|
||||
Pendingreads int64
|
||||
Numwrites int64
|
||||
Numreads int64
|
||||
Cachehits int64
|
||||
Busymapped int64
|
||||
Dmapages int64
|
||||
Highpages int64
|
||||
Delwribufs int64
|
||||
Kvaslots int64
|
||||
Avail int64
|
||||
}
|
121
internal/gopsutil/mem/mem_solaris.go
Normal file
121
internal/gopsutil/mem/mem_solaris.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/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("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("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("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
|
||||
}
|
99
internal/gopsutil/mem/mem_windows.go
Normal file
99
internal/gopsutil/mem/mem_windows.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
//go:build windows
|
||||
|
||||
package mem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var (
|
||||
procGlobalMemoryStatusEx = common.Modkernel32.NewProc("GlobalMemoryStatusEx")
|
||||
procGetPerformanceInfo = common.ModPsapi.NewProc("GetPerformanceInfo")
|
||||
)
|
||||
|
||||
type memoryStatusEx struct {
|
||||
cbSize uint32
|
||||
dwMemoryLoad uint32
|
||||
ullTotalPhys uint64 // in bytes
|
||||
ullAvailPhys uint64
|
||||
ullTotalPageFile uint64
|
||||
ullAvailPageFile uint64
|
||||
ullTotalVirtual uint64
|
||||
ullAvailVirtual uint64
|
||||
ullAvailExtendedVirtual uint64
|
||||
}
|
||||
|
||||
func VirtualMemory() (*VirtualMemoryStat, error) {
|
||||
return VirtualMemoryWithContext(context.Background())
|
||||
}
|
||||
|
||||
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
||||
var memInfo memoryStatusEx
|
||||
memInfo.cbSize = uint32(unsafe.Sizeof(memInfo))
|
||||
mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo)))
|
||||
if mem == 0 {
|
||||
return nil, windows.GetLastError()
|
||||
}
|
||||
|
||||
ret := &VirtualMemoryStat{
|
||||
Total: memInfo.ullTotalPhys,
|
||||
Available: memInfo.ullAvailPhys,
|
||||
Free: memInfo.ullAvailPhys,
|
||||
UsedPercent: float64(memInfo.dwMemoryLoad),
|
||||
}
|
||||
|
||||
ret.Used = ret.Total - ret.Available
|
||||
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) {
|
||||
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) * 100
|
||||
}
|
||||
ret := &SwapMemoryStat{
|
||||
Total: tot,
|
||||
Used: used,
|
||||
Free: free,
|
||||
UsedPercent: usedPercent,
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
28
internal/gopsutil/mem/types_openbsd.go
Normal file
28
internal/gopsutil/mem/types_openbsd.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
//go:build ignore
|
||||
|
||||
/*
|
||||
Input to cgo -godefs.
|
||||
*/
|
||||
|
||||
package mem
|
||||
|
||||
/*
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysctl.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Machine characteristics; for internal use.
|
||||
|
||||
const (
|
||||
CTLVfs = 10
|
||||
VfsGeneric = 0
|
||||
VfsBcacheStat = 3
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfBcachestats = C.sizeof_struct_bcachestats
|
||||
)
|
||||
|
||||
type Bcachestats C.struct_bcachestats
|
262
internal/gopsutil/net/net.go
Normal file
262
internal/gopsutil/net/net.go
Normal file
|
@ -0,0 +1,262 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var invoke common.Invoker = common.Invoke{}
|
||||
|
||||
type IOCountersStat struct {
|
||||
Name string `json:"name"` // interface name
|
||||
BytesSent uint64 `json:"bytesSent"` // number of bytes sent
|
||||
BytesRecv uint64 `json:"bytesRecv"` // number of bytes received
|
||||
PacketsSent uint64 `json:"packetsSent"` // number of packets sent
|
||||
PacketsRecv uint64 `json:"packetsRecv"` // number of packets received
|
||||
Errin uint64 `json:"errin"` // total number of errors while receiving
|
||||
Errout uint64 `json:"errout"` // total number of errors while sending
|
||||
Dropin uint64 `json:"dropin"` // total number of incoming packets which were dropped
|
||||
Dropout uint64 `json:"dropout"` // total number of outgoing packets which were dropped (always 0 on OSX and BSD)
|
||||
Fifoin uint64 `json:"fifoin"` // total number of FIFO buffers errors while receiving
|
||||
Fifoout uint64 `json:"fifoout"` // total number of FIFO buffers errors while sending
|
||||
}
|
||||
|
||||
// Addr is implemented compatibility to psutil
|
||||
type Addr struct {
|
||||
IP string `json:"ip"`
|
||||
Port uint32 `json:"port"`
|
||||
}
|
||||
|
||||
type ConnectionStat struct {
|
||||
Fd uint32 `json:"fd"`
|
||||
Family uint32 `json:"family"`
|
||||
Type uint32 `json:"type"`
|
||||
Laddr Addr `json:"localaddr"`
|
||||
Raddr Addr `json:"remoteaddr"`
|
||||
Status string `json:"status"`
|
||||
Uids []int32 `json:"uids"`
|
||||
Pid int32 `json:"pid"`
|
||||
}
|
||||
|
||||
// System wide stats about different network protocols
|
||||
type ProtoCountersStat struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Stats map[string]int64 `json:"stats"`
|
||||
}
|
||||
|
||||
// NetInterfaceAddr is designed for represent interface addresses
|
||||
type InterfaceAddr struct {
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
type InterfaceStat struct {
|
||||
Index int `json:"index"`
|
||||
MTU int `json:"mtu"` // maximum transmission unit
|
||||
Name string `json:"name"` // e.g., "en0", "lo0", "eth0.100"
|
||||
HardwareAddr string `json:"hardwareaddr"` // IEEE MAC-48, EUI-48 and EUI-64 form
|
||||
Flags []string `json:"flags"` // e.g., FlagUp, FlagLoopback, FlagMulticast
|
||||
Addrs []InterfaceAddr `json:"addrs"`
|
||||
}
|
||||
|
||||
type FilterStat struct {
|
||||
ConnTrackCount int64 `json:"conntrackCount"`
|
||||
ConnTrackMax int64 `json:"conntrackMax"`
|
||||
}
|
||||
|
||||
// ConntrackStat has conntrack summary info
|
||||
type ConntrackStat struct {
|
||||
Entries uint32 `json:"entries"` // Number of entries in the conntrack table
|
||||
Searched uint32 `json:"searched"` // Number of conntrack table lookups performed
|
||||
Found uint32 `json:"found"` // Number of searched entries which were successful
|
||||
New uint32 `json:"new"` // Number of entries added which were not expected before
|
||||
Invalid uint32 `json:"invalid"` // Number of packets seen which can not be tracked
|
||||
Ignore uint32 `json:"ignore"` // Packets seen which are already connected to an entry
|
||||
Delete uint32 `json:"delete"` // Number of entries which were removed
|
||||
DeleteList uint32 `json:"delete_list"` // Number of entries which were put to dying list
|
||||
Insert uint32 `json:"insert"` // Number of entries inserted into the list
|
||||
InsertFailed uint32 `json:"insert_failed"` // # insertion attempted but failed (same entry exists)
|
||||
Drop uint32 `json:"drop"` // Number of packets dropped due to conntrack failure.
|
||||
EarlyDrop uint32 `json:"early_drop"` // Dropped entries to make room for new ones, if maxsize reached
|
||||
IcmpError uint32 `json:"icmp_error"` // Subset of invalid. Packets that can't be tracked d/t error
|
||||
ExpectNew uint32 `json:"expect_new"` // Entries added after an expectation was already present
|
||||
ExpectCreate uint32 `json:"expect_create"` // Expectations added
|
||||
ExpectDelete uint32 `json:"expect_delete"` // Expectations deleted
|
||||
SearchRestart uint32 `json:"search_restart"` // Conntrack table lookups restarted due to hashtable resizes
|
||||
}
|
||||
|
||||
func NewConntrackStat(e, s, f, n, inv, ign, del, dlst, ins, insfail, drop, edrop, ie, en, ec, ed, sr uint32) *ConntrackStat {
|
||||
return &ConntrackStat{
|
||||
Entries: e,
|
||||
Searched: s,
|
||||
Found: f,
|
||||
New: n,
|
||||
Invalid: inv,
|
||||
Ignore: ign,
|
||||
Delete: del,
|
||||
DeleteList: dlst,
|
||||
Insert: ins,
|
||||
InsertFailed: insfail,
|
||||
Drop: drop,
|
||||
EarlyDrop: edrop,
|
||||
IcmpError: ie,
|
||||
ExpectNew: en,
|
||||
ExpectCreate: ec,
|
||||
ExpectDelete: ed,
|
||||
SearchRestart: sr,
|
||||
}
|
||||
}
|
||||
|
||||
type ConntrackStatList struct {
|
||||
items []*ConntrackStat
|
||||
}
|
||||
|
||||
func NewConntrackStatList() *ConntrackStatList {
|
||||
return &ConntrackStatList{
|
||||
items: []*ConntrackStat{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ConntrackStatList) Append(c *ConntrackStat) {
|
||||
l.items = append(l.items, c)
|
||||
}
|
||||
|
||||
func (l *ConntrackStatList) Items() []ConntrackStat {
|
||||
items := make([]ConntrackStat, len(l.items))
|
||||
for i, el := range l.items {
|
||||
items[i] = *el
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Summary returns a single-element list with totals from all list items.
|
||||
func (l *ConntrackStatList) Summary() []ConntrackStat {
|
||||
summary := NewConntrackStat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
for _, cs := range l.items {
|
||||
summary.Entries += cs.Entries
|
||||
summary.Searched += cs.Searched
|
||||
summary.Found += cs.Found
|
||||
summary.New += cs.New
|
||||
summary.Invalid += cs.Invalid
|
||||
summary.Ignore += cs.Ignore
|
||||
summary.Delete += cs.Delete
|
||||
summary.DeleteList += cs.DeleteList
|
||||
summary.Insert += cs.Insert
|
||||
summary.InsertFailed += cs.InsertFailed
|
||||
summary.Drop += cs.Drop
|
||||
summary.EarlyDrop += cs.EarlyDrop
|
||||
summary.IcmpError += cs.IcmpError
|
||||
summary.ExpectNew += cs.ExpectNew
|
||||
summary.ExpectCreate += cs.ExpectCreate
|
||||
summary.ExpectDelete += cs.ExpectDelete
|
||||
summary.SearchRestart += cs.SearchRestart
|
||||
}
|
||||
return []ConntrackStat{*summary}
|
||||
}
|
||||
|
||||
func (n IOCountersStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (n ConnectionStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (n ProtoCountersStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (a Addr) String() string {
|
||||
s, _ := json.Marshal(a)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (n InterfaceStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (n InterfaceAddr) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (n ConntrackStat) String() string {
|
||||
s, _ := json.Marshal(n)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func Interfaces() ([]InterfaceStat, error) {
|
||||
return InterfacesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func InterfacesWithContext(ctx context.Context) ([]InterfaceStat, error) {
|
||||
is, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]InterfaceStat, 0, len(is))
|
||||
for _, ifi := range is {
|
||||
|
||||
var flags []string
|
||||
if ifi.Flags&net.FlagUp != 0 {
|
||||
flags = append(flags, "up")
|
||||
}
|
||||
if ifi.Flags&net.FlagBroadcast != 0 {
|
||||
flags = append(flags, "broadcast")
|
||||
}
|
||||
if ifi.Flags&net.FlagLoopback != 0 {
|
||||
flags = append(flags, "loopback")
|
||||
}
|
||||
if ifi.Flags&net.FlagPointToPoint != 0 {
|
||||
flags = append(flags, "pointtopoint")
|
||||
}
|
||||
if ifi.Flags&net.FlagMulticast != 0 {
|
||||
flags = append(flags, "multicast")
|
||||
}
|
||||
|
||||
r := InterfaceStat{
|
||||
Index: ifi.Index,
|
||||
Name: ifi.Name,
|
||||
MTU: ifi.MTU,
|
||||
HardwareAddr: ifi.HardwareAddr.String(),
|
||||
Flags: flags,
|
||||
}
|
||||
addrs, err := ifi.Addrs()
|
||||
if err == nil {
|
||||
r.Addrs = make([]InterfaceAddr, 0, len(addrs))
|
||||
for _, addr := range addrs {
|
||||
r.Addrs = append(r.Addrs, InterfaceAddr{
|
||||
Addr: addr.String(),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
ret = append(ret, r)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getIOCountersAll(n []IOCountersStat) ([]IOCountersStat, error) {
|
||||
r := IOCountersStat{
|
||||
Name: "all",
|
||||
}
|
||||
for _, nic := range n {
|
||||
r.BytesRecv += nic.BytesRecv
|
||||
r.PacketsRecv += nic.PacketsRecv
|
||||
r.Errin += nic.Errin
|
||||
r.Dropin += nic.Dropin
|
||||
r.BytesSent += nic.BytesSent
|
||||
r.PacketsSent += nic.PacketsSent
|
||||
r.Errout += nic.Errout
|
||||
r.Dropout += nic.Dropout
|
||||
}
|
||||
|
||||
return []IOCountersStat{r}, nil
|
||||
}
|
420
internal/gopsutil/net/net_aix.go
Normal file
420
internal/gopsutil/net/net_aix.go
Normal file
|
@ -0,0 +1,420 @@
|
|||
//go:build aix
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func parseNetstatI(output string) ([]IOCountersStat, error) {
|
||||
lines := strings.Split(string(output), "\n")
|
||||
ret := make([]IOCountersStat, 0, len(lines)-1)
|
||||
exists := make([]string, 0, len(ret))
|
||||
|
||||
// Check first line is header
|
||||
if len(lines) > 0 && strings.Fields(lines[0])[0] != "Name" {
|
||||
return nil, fmt.Errorf("not a 'netstat -i' output")
|
||||
}
|
||||
|
||||
for _, line := range lines[1:] {
|
||||
values := strings.Fields(line)
|
||||
if len(values) < 1 || values[0] == "Name" {
|
||||
continue
|
||||
}
|
||||
if common.StringsHas(exists, values[0]) {
|
||||
// skip if already get
|
||||
continue
|
||||
}
|
||||
exists = append(exists, values[0])
|
||||
|
||||
if len(values) < 9 {
|
||||
continue
|
||||
}
|
||||
|
||||
base := 1
|
||||
// sometimes Address is omitted
|
||||
if len(values) < 10 {
|
||||
base = 0
|
||||
}
|
||||
|
||||
parsed := make([]uint64, 0, 5)
|
||||
vv := []string{
|
||||
values[base+3], // Ipkts == PacketsRecv
|
||||
values[base+4], // Ierrs == Errin
|
||||
values[base+5], // Opkts == PacketsSent
|
||||
values[base+6], // Oerrs == Errout
|
||||
values[base+8], // Drops == Dropout
|
||||
}
|
||||
|
||||
for _, target := range vv {
|
||||
if target == "-" {
|
||||
parsed = append(parsed, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := strconv.ParseUint(target, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed = append(parsed, t)
|
||||
}
|
||||
|
||||
n := IOCountersStat{
|
||||
Name: values[0],
|
||||
PacketsRecv: parsed[0],
|
||||
Errin: parsed[1],
|
||||
PacketsSent: parsed[2],
|
||||
Errout: parsed[3],
|
||||
Dropout: parsed[4],
|
||||
}
|
||||
ret = append(ret, n)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, "-idn")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
iocounters, err := parseNetstatI(string(out))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pernic == false {
|
||||
return getIOCountersAll(iocounters)
|
||||
}
|
||||
return iocounters, nil
|
||||
}
|
||||
|
||||
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
||||
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)
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return ProtoCountersWithContext(context.Background(), protocols)
|
||||
}
|
||||
|
||||
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func parseNetstatNetLine(line string) (ConnectionStat, error) {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 5 {
|
||||
return ConnectionStat{}, fmt.Errorf("wrong line,%s", line)
|
||||
}
|
||||
|
||||
var netType, netFamily uint32
|
||||
switch f[0] {
|
||||
case "tcp", "tcp4":
|
||||
netType = syscall.SOCK_STREAM
|
||||
netFamily = syscall.AF_INET
|
||||
case "udp", "udp4":
|
||||
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
|
||||
}
|
||||
|
||||
var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
|
||||
|
||||
// This function only works for netstat returning addresses with a "."
|
||||
// before the port (0.0.0.0.22 instead of 0.0.0.0:22).
|
||||
func parseNetstatAddr(local, remote string, family uint32) (laddr, 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
|
||||
}
|
||||
|
||||
func parseNetstatUnixLine(f []string) (ConnectionStat, error) {
|
||||
if len(f) < 8 {
|
||||
return ConnectionStat{}, fmt.Errorf("wrong number of fields: expected >=8 got %d", len(f))
|
||||
}
|
||||
|
||||
var netType uint32
|
||||
|
||||
switch f[1] {
|
||||
case "dgram":
|
||||
netType = syscall.SOCK_DGRAM
|
||||
case "stream":
|
||||
netType = syscall.SOCK_STREAM
|
||||
default:
|
||||
return ConnectionStat{}, fmt.Errorf("unknown type: %s", f[1])
|
||||
}
|
||||
|
||||
// Some Unix Socket don't have any address associated
|
||||
addr := ""
|
||||
if len(f) == 9 {
|
||||
addr = f[8]
|
||||
}
|
||||
|
||||
c := ConnectionStat{
|
||||
Fd: uint32(0), // not supported
|
||||
Family: uint32(syscall.AF_UNIX),
|
||||
Type: uint32(netType),
|
||||
Laddr: Addr{
|
||||
IP: addr,
|
||||
},
|
||||
Status: "NONE",
|
||||
Pid: int32(0), // not supported
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Return true if proto is the corresponding to the kind parameter
|
||||
// Only for Inet lines
|
||||
func hasCorrectInetProto(kind, proto string) bool {
|
||||
switch kind {
|
||||
case "all", "inet":
|
||||
return true
|
||||
case "unix":
|
||||
return false
|
||||
case "inet4":
|
||||
return !strings.HasSuffix(proto, "6")
|
||||
case "inet6":
|
||||
return strings.HasSuffix(proto, "6")
|
||||
case "tcp":
|
||||
return proto == "tcp" || proto == "tcp4" || proto == "tcp6"
|
||||
case "tcp4":
|
||||
return proto == "tcp" || proto == "tcp4"
|
||||
case "tcp6":
|
||||
return proto == "tcp6"
|
||||
case "udp":
|
||||
return proto == "udp" || proto == "udp4" || proto == "udp6"
|
||||
case "udp4":
|
||||
return proto == "udp" || proto == "udp4"
|
||||
case "udp6":
|
||||
return proto == "udp6"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseNetstatA(output, kind string) ([]ConnectionStat, error) {
|
||||
var ret []ConnectionStat
|
||||
lines := strings.Split(string(output), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(fields[0], "f1") {
|
||||
// Unix lines
|
||||
if len(fields) < 2 {
|
||||
// every unix connections have two lines
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := parseNetstatUnixLine(fields)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Unix Address (%s): %s", line, err)
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
|
||||
} else if strings.HasPrefix(fields[0], "tcp") || strings.HasPrefix(fields[0], "udp") {
|
||||
// Inet lines
|
||||
if !hasCorrectInetProto(kind, fields[0]) {
|
||||
continue
|
||||
}
|
||||
|
||||
// On AIX, netstat display some connections with "*.*" as local addresses
|
||||
// Skip them as they aren't real connections.
|
||||
if fields[3] == "*.*" {
|
||||
continue
|
||||
}
|
||||
|
||||
c, err := parseNetstatNetLine(line)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Inet Address (%s): %s", line, err)
|
||||
}
|
||||
|
||||
ret = append(ret, c)
|
||||
} else {
|
||||
// Header lines
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Connections(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
args := []string{"-na"}
|
||||
switch strings.ToLower(kind) {
|
||||
default:
|
||||
fallthrough
|
||||
case "":
|
||||
kind = "all"
|
||||
case "all":
|
||||
// nothing to add
|
||||
case "inet", "inet4", "inet6":
|
||||
args = append(args, "-finet")
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
args = append(args, "-finet")
|
||||
case "udp", "udp4", "udp6":
|
||||
args = append(args, "-finet")
|
||||
case "unix":
|
||||
args = append(args, "-funix")
|
||||
}
|
||||
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := parseNetstatA(string(out), kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
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 a list of network connections opened, omitting `Uids`.
|
||||
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
||||
// removed from the API in the future.
|
||||
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
|
||||
}
|
||||
|
||||
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
294
internal/gopsutil/net/net_darwin.go
Normal file
294
internal/gopsutil/net/net_darwin.go
Normal file
|
@ -0,0 +1,294 @@
|
|||
//go:build darwin
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var (
|
||||
errNetstatHeader = errors.New("Can't parse header of netstat output")
|
||||
netstatLinkRegexp = regexp.MustCompile(`^<Link#(\d+)>$`)
|
||||
)
|
||||
|
||||
const endOfLine = "\n"
|
||||
|
||||
func parseNetstatLine(line string) (stat *IOCountersStat, linkID *uint, err error) {
|
||||
var (
|
||||
numericValue uint64
|
||||
columns = strings.Fields(line)
|
||||
)
|
||||
|
||||
if columns[0] == "Name" {
|
||||
err = errNetstatHeader
|
||||
return
|
||||
}
|
||||
|
||||
// try to extract the numeric value from <Link#123>
|
||||
if subMatch := netstatLinkRegexp.FindStringSubmatch(columns[2]); len(subMatch) == 2 {
|
||||
numericValue, err = strconv.ParseUint(subMatch[1], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
linkIDUint := uint(numericValue)
|
||||
linkID = &linkIDUint
|
||||
}
|
||||
|
||||
base := 1
|
||||
numberColumns := len(columns)
|
||||
// sometimes Address is omitted
|
||||
if numberColumns < 12 {
|
||||
base = 0
|
||||
}
|
||||
if numberColumns < 11 || numberColumns > 13 {
|
||||
err = fmt.Errorf("Line %q do have an invalid number of columns %d", line, numberColumns)
|
||||
return
|
||||
}
|
||||
|
||||
parsed := make([]uint64, 0, 7)
|
||||
vv := []string{
|
||||
columns[base+3], // Ipkts == PacketsRecv
|
||||
columns[base+4], // Ierrs == Errin
|
||||
columns[base+5], // Ibytes == BytesRecv
|
||||
columns[base+6], // Opkts == PacketsSent
|
||||
columns[base+7], // Oerrs == Errout
|
||||
columns[base+8], // Obytes == BytesSent
|
||||
}
|
||||
if len(columns) == 12 {
|
||||
vv = append(vv, columns[base+10])
|
||||
}
|
||||
|
||||
for _, target := range vv {
|
||||
if target == "-" {
|
||||
parsed = append(parsed, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
if numericValue, err = strconv.ParseUint(target, 10, 64); err != nil {
|
||||
return
|
||||
}
|
||||
parsed = append(parsed, numericValue)
|
||||
}
|
||||
|
||||
stat = &IOCountersStat{
|
||||
Name: strings.Trim(columns[0], "*"), // remove the * that sometimes is on right on interface
|
||||
PacketsRecv: parsed[0],
|
||||
Errin: parsed[1],
|
||||
BytesRecv: parsed[2],
|
||||
PacketsSent: parsed[3],
|
||||
Errout: parsed[4],
|
||||
BytesSent: parsed[5],
|
||||
}
|
||||
if len(parsed) == 7 {
|
||||
stat.Dropout = parsed[6]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type netstatInterface struct {
|
||||
linkID *uint
|
||||
stat *IOCountersStat
|
||||
}
|
||||
|
||||
func parseNetstatOutput(output string) ([]netstatInterface, error) {
|
||||
var (
|
||||
err error
|
||||
lines = strings.Split(strings.Trim(output, endOfLine), endOfLine)
|
||||
)
|
||||
|
||||
// number of interfaces is number of lines less one for the header
|
||||
numberInterfaces := len(lines) - 1
|
||||
|
||||
interfaces := make([]netstatInterface, numberInterfaces)
|
||||
// no output beside header
|
||||
if numberInterfaces == 0 {
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
for index := 0; index < numberInterfaces; index++ {
|
||||
nsIface := netstatInterface{}
|
||||
if nsIface.stat, nsIface.linkID, err = parseNetstatLine(lines[index+1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
interfaces[index] = nsIface
|
||||
}
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
// map that hold the name of a network interface and the number of usage
|
||||
type mapInterfaceNameUsage map[string]uint
|
||||
|
||||
func newMapInterfaceNameUsage(ifaces []netstatInterface) mapInterfaceNameUsage {
|
||||
output := make(mapInterfaceNameUsage)
|
||||
for index := range ifaces {
|
||||
if ifaces[index].linkID != nil {
|
||||
ifaceName := ifaces[index].stat.Name
|
||||
usage, ok := output[ifaceName]
|
||||
if ok {
|
||||
output[ifaceName] = usage + 1
|
||||
} else {
|
||||
output[ifaceName] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (min mapInterfaceNameUsage) isTruncated() bool {
|
||||
for _, usage := range min {
|
||||
if usage > 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (min mapInterfaceNameUsage) notTruncated() []string {
|
||||
output := make([]string, 0)
|
||||
for ifaceName, usage := range min {
|
||||
if usage == 1 {
|
||||
output = append(output, ifaceName)
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// example of `netstat -ibdnW` output on yosemite
|
||||
// Name Mtu Network Address Ipkts Ierrs Ibytes Opkts Oerrs Obytes Coll Drop
|
||||
// lo0 16384 <Link#1> 869107 0 169411755 869107 0 169411755 0 0
|
||||
// lo0 16384 ::1/128 ::1 869107 - 169411755 869107 - 169411755 - -
|
||||
// lo0 16384 127 127.0.0.1 869107 - 169411755 869107 - 169411755 - -
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
var (
|
||||
ret []IOCountersStat
|
||||
retIndex int
|
||||
)
|
||||
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// try to get all interface metrics, and hope there won't be any truncated
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nsInterfaces, err := parseNetstatOutput(string(out))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ifaceUsage := newMapInterfaceNameUsage(nsInterfaces)
|
||||
notTruncated := ifaceUsage.notTruncated()
|
||||
ret = make([]IOCountersStat, len(notTruncated))
|
||||
|
||||
if !ifaceUsage.isTruncated() {
|
||||
// no truncated interface name, return stats of all interface with <Link#...>
|
||||
for index := range nsInterfaces {
|
||||
if nsInterfaces[index].linkID != nil {
|
||||
ret[retIndex] = *nsInterfaces[index].stat
|
||||
retIndex++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// duplicated interface, list all interfaces
|
||||
ifconfig, err := exec.LookPath("ifconfig")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if out, err = invoke.CommandWithContext(ctx, ifconfig, "-l"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
interfaceNames := strings.Fields(strings.TrimRight(string(out), endOfLine))
|
||||
|
||||
// for each of the interface name, run netstat if we don't have any stats yet
|
||||
for _, interfaceName := range interfaceNames {
|
||||
truncated := true
|
||||
for index := range nsInterfaces {
|
||||
if nsInterfaces[index].linkID != nil && nsInterfaces[index].stat.Name == interfaceName {
|
||||
// handle the non truncated name to avoid execute netstat for them again
|
||||
ret[retIndex] = *nsInterfaces[index].stat
|
||||
retIndex++
|
||||
truncated = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if truncated {
|
||||
// run netstat with -I$ifacename
|
||||
if out, err = invoke.CommandWithContext(ctx, netstat, "-ibdnWI"+interfaceName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsedIfaces, err := parseNetstatOutput(string(out))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(parsedIfaces) == 0 {
|
||||
// interface had been removed since `ifconfig -l` had been executed
|
||||
continue
|
||||
}
|
||||
for index := range parsedIfaces {
|
||||
if parsedIfaces[index].linkID != nil {
|
||||
ret = append(ret, *parsedIfaces[index].stat)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !pernic {
|
||||
return getIOCountersAll(ret)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
||||
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)
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, errors.New("NetFilterCounters not implemented for darwin")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
// Not Implemented for Darwin
|
||||
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")
|
||||
}
|
92
internal/gopsutil/net/net_fallback.go
Normal file
92
internal/gopsutil/net/net_fallback.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
//go:build !aix && !darwin && !linux && !freebsd && !openbsd && !windows
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
return []IOCountersStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return []FilterStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return ProtoCountersWithContext(context.Background(), protocols)
|
||||
}
|
||||
|
||||
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
||||
return []ProtoCountersStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Connections(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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 a list of network connections opened, omitting `Uids`.
|
||||
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
||||
// removed from the API in the future.
|
||||
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
|
||||
}
|
||||
|
||||
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
133
internal/gopsutil/net/net_freebsd.go
Normal file
133
internal/gopsutil/net/net_freebsd.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
//go:build freebsd
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(out), "\n")
|
||||
ret := make([]IOCountersStat, 0, len(lines)-1)
|
||||
exists := make([]string, 0, len(ret))
|
||||
|
||||
for _, line := range lines {
|
||||
values := strings.Fields(line)
|
||||
if len(values) < 1 || values[0] == "Name" {
|
||||
continue
|
||||
}
|
||||
if common.StringsHas(exists, values[0]) {
|
||||
// skip if already get
|
||||
continue
|
||||
}
|
||||
exists = append(exists, values[0])
|
||||
|
||||
if len(values) < 12 {
|
||||
continue
|
||||
}
|
||||
base := 1
|
||||
// sometimes Address is omitted
|
||||
if len(values) < 13 {
|
||||
base = 0
|
||||
}
|
||||
|
||||
parsed := make([]uint64, 0, 8)
|
||||
vv := []string{
|
||||
values[base+3], // PacketsRecv
|
||||
values[base+4], // Errin
|
||||
values[base+5], // Dropin
|
||||
values[base+6], // BytesRecvn
|
||||
values[base+7], // PacketSent
|
||||
values[base+8], // Errout
|
||||
values[base+9], // BytesSent
|
||||
values[base+11], // Dropout
|
||||
}
|
||||
for _, target := range vv {
|
||||
if target == "-" {
|
||||
parsed = append(parsed, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := strconv.ParseUint(target, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed = append(parsed, t)
|
||||
}
|
||||
|
||||
n := IOCountersStat{
|
||||
Name: values[0],
|
||||
PacketsRecv: parsed[0],
|
||||
Errin: parsed[1],
|
||||
Dropin: parsed[2],
|
||||
BytesRecv: parsed[3],
|
||||
PacketsSent: parsed[4],
|
||||
Errout: parsed[5],
|
||||
BytesSent: parsed[6],
|
||||
Dropout: parsed[7],
|
||||
}
|
||||
ret = append(ret, n)
|
||||
}
|
||||
|
||||
if pernic == false {
|
||||
return getIOCountersAll(ret)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
||||
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)
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, errors.New("NetFilterCounters not implemented for freebsd")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, errors.New("ConntrackStats not implemented for freebsd")
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
// Not Implemented for FreeBSD
|
||||
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")
|
||||
}
|
887
internal/gopsutil/net/net_linux.go
Normal file
887
internal/gopsutil/net/net_linux.go
Normal file
|
@ -0,0 +1,887 @@
|
|||
//go:build linux
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
const ( // Conntrack Column numbers
|
||||
CT_ENTRIES = iota
|
||||
CT_SEARCHED
|
||||
CT_FOUND
|
||||
CT_NEW
|
||||
CT_INVALID
|
||||
CT_IGNORE
|
||||
CT_DELETE
|
||||
CT_DELETE_LIST
|
||||
CT_INSERT
|
||||
CT_INSERT_FAILED
|
||||
CT_DROP
|
||||
CT_EARLY_DROP
|
||||
CT_ICMP_ERROR
|
||||
CT_EXPECT_NEW
|
||||
CT_EXPECT_CREATE
|
||||
CT_EXPECT_DELETE
|
||||
CT_SEARCH_RESTART
|
||||
)
|
||||
|
||||
// NetIOCounters returnes network I/O statistics for every network
|
||||
// interface installed on the system. If pernic argument is false,
|
||||
// return only sum of all information (which name is 'all'). If true,
|
||||
// every network interface installed on the system is returned
|
||||
// separately.
|
||||
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")
|
||||
return IOCountersByFileWithContext(ctx, pernic, filename)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parts := make([]string, 2)
|
||||
|
||||
statlen := len(lines) - 1
|
||||
|
||||
ret := make([]IOCountersStat, 0, statlen)
|
||||
|
||||
for _, line := range lines[2:] {
|
||||
separatorPos := strings.LastIndex(line, ":")
|
||||
if separatorPos == -1 {
|
||||
continue
|
||||
}
|
||||
parts[0] = line[0:separatorPos]
|
||||
parts[1] = line[separatorPos+1:]
|
||||
|
||||
interfaceName := strings.TrimSpace(parts[0])
|
||||
if interfaceName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Fields(strings.TrimSpace(parts[1]))
|
||||
bytesRecv, err := strconv.ParseUint(fields[0], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
packetsRecv, err := strconv.ParseUint(fields[1], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
errIn, err := strconv.ParseUint(fields[2], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
dropIn, err := strconv.ParseUint(fields[3], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
fifoIn, err := strconv.ParseUint(fields[4], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
bytesSent, err := strconv.ParseUint(fields[8], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
packetsSent, err := strconv.ParseUint(fields[9], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
errOut, err := strconv.ParseUint(fields[10], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
dropOut, err := strconv.ParseUint(fields[11], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
fifoOut, err := strconv.ParseUint(fields[12], 10, 64)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
nic := IOCountersStat{
|
||||
Name: interfaceName,
|
||||
BytesRecv: bytesRecv,
|
||||
PacketsRecv: packetsRecv,
|
||||
Errin: errIn,
|
||||
Dropin: dropIn,
|
||||
Fifoin: fifoIn,
|
||||
BytesSent: bytesSent,
|
||||
PacketsSent: packetsSent,
|
||||
Errout: errOut,
|
||||
Dropout: dropOut,
|
||||
Fifoout: fifoOut,
|
||||
}
|
||||
ret = append(ret, nic)
|
||||
}
|
||||
|
||||
if !pernic {
|
||||
return getIOCountersAll(ret)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var netProtocols = []string{
|
||||
"ip",
|
||||
"icmp",
|
||||
"icmpmsg",
|
||||
"tcp",
|
||||
"udp",
|
||||
"udplite",
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
// Available protocols:
|
||||
//
|
||||
// ip,icmp,icmpmsg,tcp,udp,udplite
|
||||
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return ProtoCountersWithContext(context.Background(), protocols)
|
||||
}
|
||||
|
||||
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
||||
if len(protocols) == 0 {
|
||||
protocols = netProtocols
|
||||
}
|
||||
|
||||
stats := make([]ProtoCountersStat, 0, len(protocols))
|
||||
protos := make(map[string]bool, len(protocols))
|
||||
for _, p := range protocols {
|
||||
protos[p] = true
|
||||
}
|
||||
|
||||
filename := common.HostProc("net/snmp")
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
linecount := len(lines)
|
||||
for i := 0; i < linecount; i++ {
|
||||
line := lines[i]
|
||||
r := strings.IndexRune(line, ':')
|
||||
if r == -1 {
|
||||
return nil, errors.New(filename + " is not fomatted correctly, expected ':'.")
|
||||
}
|
||||
proto := strings.ToLower(line[:r])
|
||||
if !protos[proto] {
|
||||
// skip protocol and data line
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// Read header line
|
||||
statNames := strings.Split(line[r+2:], " ")
|
||||
|
||||
// Read data line
|
||||
i++
|
||||
statValues := strings.Split(lines[i][r+2:], " ")
|
||||
if len(statNames) != len(statValues) {
|
||||
return nil, errors.New(filename + " is not fomatted correctly, expected same number of columns.")
|
||||
}
|
||||
stat := ProtoCountersStat{
|
||||
Protocol: proto,
|
||||
Stats: make(map[string]int64, len(statNames)),
|
||||
}
|
||||
for j := range statNames {
|
||||
value, err := strconv.ParseInt(statValues[j], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stat.Stats[statNames[j]] = value
|
||||
}
|
||||
stats = append(stats, stat)
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// NetFilterCounters returns iptables conntrack statistics
|
||||
// the currently in use conntrack count and the max.
|
||||
// If the file does not exist or is invalid it will return nil.
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
countfile := common.HostProc("sys/net/netfilter/nf_conntrack_count")
|
||||
maxfile := common.HostProc("sys/net/netfilter/nf_conntrack_max")
|
||||
|
||||
count, err := common.ReadInts(countfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats := make([]FilterStat, 0, 1)
|
||||
|
||||
max, err := common.ReadInts(maxfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload := FilterStat{
|
||||
ConnTrackCount: count[0],
|
||||
ConnTrackMax: max[0],
|
||||
}
|
||||
|
||||
stats = append(stats, payload)
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// ConntrackStats returns more detailed info about the conntrack table
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
// ConntrackStatsWithContext returns more detailed info about the conntrack table
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return conntrackStatsFromFile(common.HostProc("net/stat/nf_conntrack"), percpu)
|
||||
}
|
||||
|
||||
// conntrackStatsFromFile returns more detailed info about the conntrack table
|
||||
// from `filename`
|
||||
// If 'percpu' is false, the result will contain exactly one item with totals/summary
|
||||
func conntrackStatsFromFile(filename string, percpu bool) ([]ConntrackStat, error) {
|
||||
lines, err := common.ReadLines(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statlist := NewConntrackStatList()
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 17 && fields[0] != "entries" {
|
||||
statlist.Append(NewConntrackStat(
|
||||
common.HexToUint32(fields[CT_ENTRIES]),
|
||||
common.HexToUint32(fields[CT_SEARCHED]),
|
||||
common.HexToUint32(fields[CT_FOUND]),
|
||||
common.HexToUint32(fields[CT_NEW]),
|
||||
common.HexToUint32(fields[CT_INVALID]),
|
||||
common.HexToUint32(fields[CT_IGNORE]),
|
||||
common.HexToUint32(fields[CT_DELETE]),
|
||||
common.HexToUint32(fields[CT_DELETE_LIST]),
|
||||
common.HexToUint32(fields[CT_INSERT]),
|
||||
common.HexToUint32(fields[CT_INSERT_FAILED]),
|
||||
common.HexToUint32(fields[CT_DROP]),
|
||||
common.HexToUint32(fields[CT_EARLY_DROP]),
|
||||
common.HexToUint32(fields[CT_ICMP_ERROR]),
|
||||
common.HexToUint32(fields[CT_EXPECT_NEW]),
|
||||
common.HexToUint32(fields[CT_EXPECT_CREATE]),
|
||||
common.HexToUint32(fields[CT_EXPECT_DELETE]),
|
||||
common.HexToUint32(fields[CT_SEARCH_RESTART]),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if percpu {
|
||||
return statlist.Items(), nil
|
||||
}
|
||||
return statlist.Summary(), nil
|
||||
}
|
||||
|
||||
// http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
|
||||
var TCPStatuses = map[string]string{
|
||||
"01": "ESTABLISHED",
|
||||
"02": "SYN_SENT",
|
||||
"03": "SYN_RECV",
|
||||
"04": "FIN_WAIT1",
|
||||
"05": "FIN_WAIT2",
|
||||
"06": "TIME_WAIT",
|
||||
"07": "CLOSE",
|
||||
"08": "CLOSE_WAIT",
|
||||
"09": "LAST_ACK",
|
||||
"0A": "LISTEN",
|
||||
"0B": "CLOSING",
|
||||
}
|
||||
|
||||
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 kindUNIX = netConnectionKindType{
|
||||
family: syscall.AF_UNIX,
|
||||
filename: "unix",
|
||||
}
|
||||
|
||||
var netConnectionKindMap = map[string][]netConnectionKindType{
|
||||
"all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6, kindUNIX},
|
||||
"tcp": {kindTCP4, kindTCP6},
|
||||
"tcp4": {kindTCP4},
|
||||
"tcp6": {kindTCP6},
|
||||
"udp": {kindUDP4, kindUDP6},
|
||||
"udp4": {kindUDP4},
|
||||
"udp6": {kindUDP6},
|
||||
"unix": {kindUNIX},
|
||||
"inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
|
||||
"inet4": {kindTCP4, kindUDP4},
|
||||
"inet6": {kindTCP6, kindUDP6},
|
||||
}
|
||||
|
||||
type inodeMap struct {
|
||||
pid int32
|
||||
fd uint32
|
||||
}
|
||||
|
||||
type connTmp struct {
|
||||
fd uint32
|
||||
family uint32
|
||||
sockType uint32
|
||||
laddr Addr
|
||||
raddr Addr
|
||||
status string
|
||||
pid int32
|
||||
boundPid int32
|
||||
path string
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return ConnectionsPid(kind, 0)
|
||||
}
|
||||
|
||||
// Return a list of network connections opened returning at most `max`
|
||||
// connections for each running process.
|
||||
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 a list of network connections opened, omitting `Uids`.
|
||||
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
||||
// removed from the API in the future.
|
||||
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
||||
}
|
||||
|
||||
// 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 ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
// Return up to `max` network connections opened by a process.
|
||||
func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, false)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max, true)
|
||||
}
|
||||
|
||||
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int, skipUids bool) ([]ConnectionStat, error) {
|
||||
tmap, ok := netConnectionKindMap[kind]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid kind, %s", kind)
|
||||
}
|
||||
root := common.HostProc()
|
||||
var err error
|
||||
var inodes map[string][]inodeMap
|
||||
if pid == 0 {
|
||||
inodes, err = getProcInodesAll(root, max)
|
||||
} else {
|
||||
inodes, err = getProcInodes(root, pid, max)
|
||||
if len(inodes) == 0 {
|
||||
// no connection for the pid
|
||||
return []ConnectionStat{}, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cound not get pid(s), %d: %s", pid, err)
|
||||
}
|
||||
return statsFromInodes(root, pid, tmap, inodes, skipUids)
|
||||
}
|
||||
|
||||
func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap, skipUids bool) ([]ConnectionStat, error) {
|
||||
dupCheckMap := make(map[string]struct{})
|
||||
var ret []ConnectionStat
|
||||
|
||||
var err error
|
||||
for _, t := range tmap {
|
||||
var path string
|
||||
var connKey string
|
||||
var ls []connTmp
|
||||
if pid == 0 {
|
||||
path = fmt.Sprintf("%s/net/%s", root, t.filename)
|
||||
} else {
|
||||
path = fmt.Sprintf("%s/%d/net/%s", root, pid, t.filename)
|
||||
}
|
||||
switch t.family {
|
||||
case syscall.AF_INET, syscall.AF_INET6:
|
||||
ls, err = processInet(path, t, inodes, pid)
|
||||
case syscall.AF_UNIX:
|
||||
ls, err = processUnix(path, t, inodes, pid)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range ls {
|
||||
// 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
|
||||
}
|
||||
|
||||
conn := ConnectionStat{
|
||||
Fd: c.fd,
|
||||
Family: c.family,
|
||||
Type: c.sockType,
|
||||
Laddr: c.laddr,
|
||||
Raddr: c.raddr,
|
||||
Status: c.status,
|
||||
Pid: c.pid,
|
||||
}
|
||||
if c.pid == 0 {
|
||||
conn.Pid = c.boundPid
|
||||
} else {
|
||||
conn.Pid = c.pid
|
||||
}
|
||||
|
||||
if !skipUids {
|
||||
// fetch process owner Real, effective, saved set, and filesystem UIDs
|
||||
proc := process{Pid: conn.Pid}
|
||||
conn.Uids, _ = proc.getUids()
|
||||
}
|
||||
|
||||
ret = append(ret, conn)
|
||||
dupCheckMap[connKey] = struct{}{}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// getProcInodes returnes fd of the pid.
|
||||
func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
|
||||
ret := make(map[string][]inodeMap)
|
||||
|
||||
dir := fmt.Sprintf("%s/%d/fd", root, pid)
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
defer f.Close()
|
||||
files, err := f.Readdir(max)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
for _, fd := range files {
|
||||
inodePath := fmt.Sprintf("%s/%d/fd/%s", root, pid, fd.Name())
|
||||
|
||||
inode, err := os.Readlink(inodePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(inode, "socket:[") {
|
||||
continue
|
||||
}
|
||||
// the process is using a socket
|
||||
l := len(inode)
|
||||
inode = inode[8 : l-1]
|
||||
_, ok := ret[inode]
|
||||
if !ok {
|
||||
ret[inode] = make([]inodeMap, 0)
|
||||
}
|
||||
fd, err := strconv.Atoi(fd.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
i := inodeMap{
|
||||
pid: pid,
|
||||
fd: uint32(fd),
|
||||
}
|
||||
ret[inode] = append(ret[inode], i)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Pids retunres all pids.
|
||||
// Note: this is a copy of process_linux.Pids()
|
||||
// FIXME: Import process occures import cycle.
|
||||
// move to common made other platform breaking. Need consider.
|
||||
func Pids() ([]int32, error) {
|
||||
return PidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
var ret []int32
|
||||
|
||||
d, err := os.Open(common.HostProc())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
fnames, err := d.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fname := range fnames {
|
||||
pid, err := strconv.ParseInt(fname, 10, 32)
|
||||
if err != nil {
|
||||
// if not numeric name, just skip
|
||||
continue
|
||||
}
|
||||
ret = append(ret, int32(pid))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Note: the following is based off process_linux structs and methods
|
||||
// we need these to fetch the owner of a process ID
|
||||
// FIXME: Import process occures import cycle.
|
||||
// see remarks on pids()
|
||||
type process struct {
|
||||
Pid int32 `json:"pid"`
|
||||
uids []int32
|
||||
}
|
||||
|
||||
// Uids returns user ids of the process as a slice of the int
|
||||
func (p *process) getUids() ([]int32, error) {
|
||||
err := p.fillFromStatus()
|
||||
if err != nil {
|
||||
return []int32{}, err
|
||||
}
|
||||
return p.uids, nil
|
||||
}
|
||||
|
||||
// Get status from /proc/(pid)/status
|
||||
func (p *process) fillFromStatus() error {
|
||||
pid := p.Pid
|
||||
statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
|
||||
contents, err := os.ReadFile(statPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(contents), "\n")
|
||||
for _, line := range lines {
|
||||
tabParts := strings.SplitN(line, "\t", 2)
|
||||
if len(tabParts) < 2 {
|
||||
continue
|
||||
}
|
||||
value := tabParts[1]
|
||||
switch strings.TrimRight(tabParts[0], ":") {
|
||||
case "Uid":
|
||||
p.uids = make([]int32, 0, 4)
|
||||
for _, i := range strings.Split(value, "\t") {
|
||||
v, err := strconv.ParseInt(i, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.uids = append(p.uids, int32(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
|
||||
pids, err := Pids()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make(map[string][]inodeMap)
|
||||
|
||||
for _, pid := range pids {
|
||||
t, err := getProcInodes(root, pid, max)
|
||||
if err != nil {
|
||||
// skip if permission error or no longer exists
|
||||
if os.IsPermission(err) || errors.Is(err, fs.ErrNotExist) || err == io.EOF {
|
||||
continue
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
if len(t) == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO: update ret.
|
||||
ret = updateMap(ret, t)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// decodeAddress decode addresse represents addr in proc/net/*
|
||||
// ex:
|
||||
// "0500000A:0016" -> "10.0.0.5", 22
|
||||
// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53
|
||||
func decodeAddress(family uint32, src string) (Addr, error) {
|
||||
t := strings.Split(src, ":")
|
||||
if len(t) != 2 {
|
||||
return Addr{}, fmt.Errorf("does not contain port, %s", src)
|
||||
}
|
||||
addr := t[0]
|
||||
port, err := strconv.ParseUint(t[1], 16, 16)
|
||||
if err != nil {
|
||||
return Addr{}, fmt.Errorf("invalid port, %s", src)
|
||||
}
|
||||
decoded, err := hex.DecodeString(addr)
|
||||
if err != nil {
|
||||
return Addr{}, fmt.Errorf("decode error, %s", err)
|
||||
}
|
||||
var ip net.IP
|
||||
// Assumes this is little_endian
|
||||
if family == syscall.AF_INET {
|
||||
ip = net.IP(Reverse(decoded))
|
||||
} else { // IPv6
|
||||
ip, err = parseIPv6HexString(decoded)
|
||||
if err != nil {
|
||||
return Addr{}, err
|
||||
}
|
||||
}
|
||||
return Addr{
|
||||
IP: ip.String(),
|
||||
Port: uint32(port),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Reverse reverses array of bytes.
|
||||
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 {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// parseIPv6HexString parse array of bytes to IPv6 string
|
||||
func parseIPv6HexString(src []byte) (net.IP, error) {
|
||||
if len(src) != 16 {
|
||||
return nil, fmt.Errorf("invalid IPv6 string")
|
||||
}
|
||||
|
||||
buf := make([]byte, 0, 16)
|
||||
for i := 0; i < len(src); i += 4 {
|
||||
r := Reverse(src[i : i+4])
|
||||
buf = append(buf, r...)
|
||||
}
|
||||
return net.IP(buf), nil
|
||||
}
|
||||
|
||||
func processInet(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
|
||||
if strings.HasSuffix(file, "6") && !common.PathExists(file) {
|
||||
// IPv6 not supported, return empty.
|
||||
return []connTmp{}, nil
|
||||
}
|
||||
|
||||
// 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 := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := bytes.Split(contents, []byte("\n"))
|
||||
|
||||
var ret []connTmp
|
||||
// skip first line
|
||||
for _, line := range lines[1:] {
|
||||
l := strings.Fields(string(line))
|
||||
if len(l) < 10 {
|
||||
continue
|
||||
}
|
||||
laddr := l[1]
|
||||
raddr := l[2]
|
||||
status := l[3]
|
||||
inode := l[9]
|
||||
pid := int32(0)
|
||||
fd := uint32(0)
|
||||
i, exists := inodes[inode]
|
||||
if exists {
|
||||
pid = i[0].pid
|
||||
fd = i[0].fd
|
||||
}
|
||||
if filterPid > 0 && filterPid != pid {
|
||||
continue
|
||||
}
|
||||
if kind.sockType == syscall.SOCK_STREAM {
|
||||
status = TCPStatuses[status]
|
||||
} else {
|
||||
status = "NONE"
|
||||
}
|
||||
la, err := decodeAddress(kind.family, laddr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ra, err := decodeAddress(kind.family, raddr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, connTmp{
|
||||
fd: fd,
|
||||
family: kind.family,
|
||||
sockType: kind.sockType,
|
||||
laddr: la,
|
||||
raddr: ra,
|
||||
status: status,
|
||||
pid: pid,
|
||||
})
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func processUnix(file string, kind netConnectionKindType, inodes map[string][]inodeMap, filterPid int32) ([]connTmp, error) {
|
||||
// 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 := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := bytes.Split(contents, []byte("\n"))
|
||||
|
||||
var ret []connTmp
|
||||
// skip first line
|
||||
for _, line := range lines[1:] {
|
||||
tokens := strings.Fields(string(line))
|
||||
if len(tokens) < 6 {
|
||||
continue
|
||||
}
|
||||
st, err := strconv.Atoi(tokens[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inode := tokens[6]
|
||||
|
||||
var pairs []inodeMap
|
||||
pairs, exists := inodes[inode]
|
||||
if !exists {
|
||||
pairs = []inodeMap{
|
||||
{},
|
||||
}
|
||||
}
|
||||
for _, pair := range pairs {
|
||||
if filterPid > 0 && filterPid != pair.pid {
|
||||
continue
|
||||
}
|
||||
var path string
|
||||
if len(tokens) == 8 {
|
||||
path = tokens[len(tokens)-1]
|
||||
}
|
||||
ret = append(ret, connTmp{
|
||||
fd: pair.fd,
|
||||
family: kind.family,
|
||||
sockType: uint32(st),
|
||||
laddr: Addr{
|
||||
IP: path,
|
||||
},
|
||||
pid: pair.pid,
|
||||
status: "NONE",
|
||||
path: path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func updateMap(src, add map[string][]inodeMap) map[string][]inodeMap {
|
||||
for key, value := range add {
|
||||
a, exists := src[key]
|
||||
if !exists {
|
||||
src[key] = value
|
||||
continue
|
||||
}
|
||||
src[key] = append(a, value...)
|
||||
}
|
||||
return src
|
||||
}
|
320
internal/gopsutil/net/net_openbsd.go
Normal file
320
internal/gopsutil/net/net_openbsd.go
Normal file
|
@ -0,0 +1,320 @@
|
|||
//go:build openbsd
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var portMatch = regexp.MustCompile(`(.*)\.(\d+)$`)
|
||||
|
||||
func ParseNetstat(output, mode string,
|
||||
iocs map[string]IOCountersStat,
|
||||
) error {
|
||||
lines := strings.Split(output, "\n")
|
||||
|
||||
exists := make([]string, 0, len(lines)-1)
|
||||
|
||||
columns := 6
|
||||
if mode == "ind" {
|
||||
columns = 10
|
||||
}
|
||||
for _, line := range lines {
|
||||
values := strings.Fields(line)
|
||||
if len(values) < 1 || values[0] == "Name" {
|
||||
continue
|
||||
}
|
||||
if common.StringsHas(exists, values[0]) {
|
||||
// skip if already get
|
||||
continue
|
||||
}
|
||||
|
||||
if len(values) < columns {
|
||||
continue
|
||||
}
|
||||
base := 1
|
||||
// sometimes Address is omitted
|
||||
if len(values) < columns {
|
||||
base = 0
|
||||
}
|
||||
|
||||
parsed := make([]uint64, 0, 8)
|
||||
var vv []string
|
||||
if mode == "inb" {
|
||||
vv = []string{
|
||||
values[base+3], // BytesRecv
|
||||
values[base+4], // BytesSent
|
||||
}
|
||||
} else {
|
||||
vv = []string{
|
||||
values[base+3], // Ipkts
|
||||
values[base+4], // Ierrs
|
||||
values[base+5], // Opkts
|
||||
values[base+6], // Oerrs
|
||||
values[base+8], // Drops
|
||||
}
|
||||
}
|
||||
for _, target := range vv {
|
||||
if target == "-" {
|
||||
parsed = append(parsed, 0)
|
||||
continue
|
||||
}
|
||||
|
||||
t, err := strconv.ParseUint(target, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsed = append(parsed, t)
|
||||
}
|
||||
exists = append(exists, values[0])
|
||||
|
||||
n, present := iocs[values[0]]
|
||||
if !present {
|
||||
n = IOCountersStat{Name: values[0]}
|
||||
}
|
||||
if mode == "inb" {
|
||||
n.BytesRecv = parsed[0]
|
||||
n.BytesSent = parsed[1]
|
||||
} else {
|
||||
n.PacketsRecv = parsed[0]
|
||||
n.Errin = parsed[1]
|
||||
n.PacketsSent = parsed[2]
|
||||
n.Errout = parsed[3]
|
||||
n.Dropin = parsed[4]
|
||||
n.Dropout = parsed[4]
|
||||
}
|
||||
|
||||
iocs[n.Name] = n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
netstat, err := exec.LookPath("netstat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, netstat, "-inb")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out2, err := invoke.CommandWithContext(ctx, netstat, "-ind")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iocs := make(map[string]IOCountersStat)
|
||||
|
||||
lines := strings.Split(string(out), "\n")
|
||||
ret := make([]IOCountersStat, 0, len(lines)-1)
|
||||
|
||||
err = ParseNetstat(string(out), "inb", iocs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = ParseNetstat(string(out2), "ind", iocs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ioc := range iocs {
|
||||
ret = append(ret, ioc)
|
||||
}
|
||||
|
||||
if pernic == false {
|
||||
return getIOCountersAll(ret)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
||||
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)
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, errors.New("NetFilterCounters not implemented for openbsd")
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
// Not Implemented for OpenBSD
|
||||
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")
|
||||
}
|
||||
|
||||
func parseNetstatLine(line string) (ConnectionStat, error) {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 5 {
|
||||
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, remote string, family uint32) (laddr, 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
|
||||
}
|
223
internal/gopsutil/net/net_unix.go
Normal file
223
internal/gopsutil/net/net_unix.go
Normal file
|
@ -0,0 +1,223 @@
|
|||
//go:build freebsd || darwin
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
return ConnectionsPid(kind, 0)
|
||||
}
|
||||
|
||||
// Return a list of network connections opened returning at most `max`
|
||||
// connections for each running process.
|
||||
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 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) {
|
||||
var ret []ConnectionStat
|
||||
|
||||
args := []string{"-i"}
|
||||
switch strings.ToLower(kind) {
|
||||
default:
|
||||
fallthrough
|
||||
case "":
|
||||
fallthrough
|
||||
case "all":
|
||||
fallthrough
|
||||
case "inet":
|
||||
args = append(args, "tcp", "-i", "udp")
|
||||
case "inet4":
|
||||
args = append(args, "4")
|
||||
case "inet6":
|
||||
args = append(args, "6")
|
||||
case "tcp":
|
||||
args = append(args, "tcp")
|
||||
case "tcp4":
|
||||
args = append(args, "4tcp")
|
||||
case "tcp6":
|
||||
args = append(args, "6tcp")
|
||||
case "udp":
|
||||
args = append(args, "udp")
|
||||
case "udp4":
|
||||
args = append(args, "6udp")
|
||||
case "udp6":
|
||||
args = append(args, "6udp")
|
||||
case "unix":
|
||||
args = []string{"-U"}
|
||||
}
|
||||
|
||||
r, err := common.CallLsofWithContext(ctx, invoke, pid, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rr := range r {
|
||||
if strings.HasPrefix(rr, "COMMAND") {
|
||||
continue
|
||||
}
|
||||
n, err := parseNetLine(rr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, n)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
var constMap = map[string]int{
|
||||
"unix": syscall.AF_UNIX,
|
||||
"TCP": syscall.SOCK_STREAM,
|
||||
"UDP": syscall.SOCK_DGRAM,
|
||||
"IPv4": syscall.AF_INET,
|
||||
"IPv6": syscall.AF_INET6,
|
||||
}
|
||||
|
||||
func parseNetLine(line string) (ConnectionStat, error) {
|
||||
f := strings.Fields(line)
|
||||
if len(f) < 8 {
|
||||
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])
|
||||
if err != nil {
|
||||
return ConnectionStat{}, err
|
||||
}
|
||||
fd, err := strconv.Atoi(strings.Trim(f[3], "u"))
|
||||
if err != nil {
|
||||
return ConnectionStat{}, fmt.Errorf("unknown fd, %s", f[3])
|
||||
}
|
||||
netFamily, ok := constMap[f[4]]
|
||||
if !ok {
|
||||
return ConnectionStat{}, fmt.Errorf("unknown family, %s", f[4])
|
||||
}
|
||||
netType, ok := constMap[f[7]]
|
||||
if !ok {
|
||||
return ConnectionStat{}, fmt.Errorf("unknown type, %s", f[7])
|
||||
}
|
||||
|
||||
var laddr, raddr Addr
|
||||
if f[7] == "unix" {
|
||||
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{
|
||||
Fd: uint32(fd),
|
||||
Family: uint32(netFamily),
|
||||
Type: uint32(netType),
|
||||
Laddr: laddr,
|
||||
Raddr: raddr,
|
||||
Pid: int32(pid),
|
||||
}
|
||||
if len(f) == 10 {
|
||||
n.Status = strings.Trim(f[9], "()")
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func parseNetAddr(line string) (laddr, raddr Addr, err error) {
|
||||
parse := func(l string) (Addr, error) {
|
||||
host, port, err := net.SplitHostPort(l)
|
||||
if err != nil {
|
||||
return Addr{}, fmt.Errorf("wrong addr, %s", l)
|
||||
}
|
||||
lport, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return Addr{}, err
|
||||
}
|
||||
return Addr{IP: host, Port: uint32(lport)}, nil
|
||||
}
|
||||
|
||||
addrs := strings.Split(line, "->")
|
||||
if len(addrs) == 0 {
|
||||
return laddr, raddr, fmt.Errorf("wrong netaddr, %s", line)
|
||||
}
|
||||
laddr, err = parse(addrs[0])
|
||||
if len(addrs) == 2 { // remote addr exists
|
||||
raddr, err = parse(addrs[1])
|
||||
if err != nil {
|
||||
return laddr, raddr, err
|
||||
}
|
||||
}
|
||||
|
||||
return laddr, raddr, err
|
||||
}
|
||||
|
||||
// Return up to `max` network connections opened by a process.
|
||||
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 a list of network connections opened, omitting `Uids`.
|
||||
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
||||
// removed from the API in the future.
|
||||
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
|
||||
}
|
||||
|
||||
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
779
internal/gopsutil/net/net_windows.go
Normal file
779
internal/gopsutil/net/net_windows.go
Normal file
|
@ -0,0 +1,779 @@
|
|||
//go:build windows
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
var (
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable")
|
||||
procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2")
|
||||
)
|
||||
|
||||
const (
|
||||
TCPTableBasicListener = iota
|
||||
TCPTableBasicConnections
|
||||
TCPTableBasicAll
|
||||
TCPTableOwnerPIDListener
|
||||
TCPTableOwnerPIDConnections
|
||||
TCPTableOwnerPIDAll
|
||||
TCPTableOwnerModuleListener
|
||||
TCPTableOwnerModuleConnections
|
||||
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},
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/ethr/blob/aecdaf923970e5a9b4c461b4e2e3963d781ad2cc/plt_windows.go#L114-L170
|
||||
type guid struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
const (
|
||||
maxStringSize = 256
|
||||
maxPhysAddressLength = 32
|
||||
pad0for64_4for32 = 0
|
||||
)
|
||||
|
||||
type mibIfRow2 struct {
|
||||
InterfaceLuid uint64
|
||||
InterfaceIndex uint32
|
||||
InterfaceGuid guid
|
||||
Alias [maxStringSize + 1]uint16
|
||||
Description [maxStringSize + 1]uint16
|
||||
PhysicalAddressLength uint32
|
||||
PhysicalAddress [maxPhysAddressLength]uint8
|
||||
PermanentPhysicalAddress [maxPhysAddressLength]uint8
|
||||
Mtu uint32
|
||||
Type uint32
|
||||
TunnelType uint32
|
||||
MediaType uint32
|
||||
PhysicalMediumType uint32
|
||||
AccessType uint32
|
||||
DirectionType uint32
|
||||
InterfaceAndOperStatusFlags uint32
|
||||
OperStatus uint32
|
||||
AdminStatus uint32
|
||||
MediaConnectState uint32
|
||||
NetworkGuid guid
|
||||
ConnectionType uint32
|
||||
padding1 [pad0for64_4for32]byte
|
||||
TransmitLinkSpeed uint64
|
||||
ReceiveLinkSpeed uint64
|
||||
InOctets uint64
|
||||
InUcastPkts uint64
|
||||
InNUcastPkts uint64
|
||||
InDiscards uint64
|
||||
InErrors uint64
|
||||
InUnknownProtos uint64
|
||||
InUcastOctets uint64
|
||||
InMulticastOctets uint64
|
||||
InBroadcastOctets uint64
|
||||
OutOctets uint64
|
||||
OutUcastPkts uint64
|
||||
OutNUcastPkts uint64
|
||||
OutDiscards uint64
|
||||
OutErrors uint64
|
||||
OutUcastOctets uint64
|
||||
OutMulticastOctets uint64
|
||||
OutBroadcastOctets uint64
|
||||
OutQLen uint64
|
||||
}
|
||||
|
||||
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
||||
return IOCountersWithContext(context.Background(), pernic)
|
||||
}
|
||||
|
||||
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
||||
ifs, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var counters []IOCountersStat
|
||||
|
||||
err = procGetIfEntry2.Find()
|
||||
if err == nil { // Vista+, uint64 values (issue#693)
|
||||
for _, ifi := range ifs {
|
||||
c := IOCountersStat{
|
||||
Name: ifi.Name,
|
||||
}
|
||||
|
||||
row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)}
|
||||
ret, _, err := procGetIfEntry2.Call(uintptr(unsafe.Pointer(&row)))
|
||||
if ret != 0 {
|
||||
return nil, os.NewSyscallError("GetIfEntry2", err)
|
||||
}
|
||||
c.BytesSent = uint64(row.OutOctets)
|
||||
c.BytesRecv = uint64(row.InOctets)
|
||||
c.PacketsSent = uint64(row.OutUcastPkts)
|
||||
c.PacketsRecv = uint64(row.InUcastPkts)
|
||||
c.Errin = uint64(row.InErrors)
|
||||
c.Errout = uint64(row.OutErrors)
|
||||
c.Dropin = uint64(row.InDiscards)
|
||||
c.Dropout = uint64(row.OutDiscards)
|
||||
|
||||
counters = append(counters, c)
|
||||
}
|
||||
} else { // WinXP fallback, uint32 values
|
||||
for _, ifi := range ifs {
|
||||
c := IOCountersStat{
|
||||
Name: ifi.Name,
|
||||
}
|
||||
|
||||
row := windows.MibIfRow{Index: uint32(ifi.Index)}
|
||||
err = windows.GetIfEntry(&row)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("GetIfEntry", err)
|
||||
}
|
||||
c.BytesSent = uint64(row.OutOctets)
|
||||
c.BytesRecv = uint64(row.InOctets)
|
||||
c.PacketsSent = uint64(row.OutUcastPkts)
|
||||
c.PacketsRecv = uint64(row.InUcastPkts)
|
||||
c.Errin = uint64(row.InErrors)
|
||||
c.Errout = uint64(row.OutErrors)
|
||||
c.Dropin = uint64(row.InDiscards)
|
||||
c.Dropout = uint64(row.OutDiscards)
|
||||
|
||||
counters = append(counters, c)
|
||||
}
|
||||
}
|
||||
|
||||
if !pernic {
|
||||
return getIOCountersAll(counters)
|
||||
}
|
||||
return counters, nil
|
||||
}
|
||||
|
||||
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
||||
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 a list of network connections
|
||||
// Available kind:
|
||||
//
|
||||
// reference to netConnectionKindMap
|
||||
func Connections(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
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`
|
||||
// connections for each running process.
|
||||
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 a list of network connections opened, omitting `Uids`.
|
||||
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
||||
// removed from the API in the future.
|
||||
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
||||
}
|
||||
|
||||
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
||||
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
||||
}
|
||||
|
||||
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
||||
}
|
||||
|
||||
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
||||
}
|
||||
|
||||
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
|
||||
}
|
||||
|
||||
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
||||
return []ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func FilterCounters() ([]FilterStat, error) {
|
||||
return FilterCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
||||
return ConntrackStatsWithContext(context.Background(), percpu)
|
||||
}
|
||||
|
||||
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
// NetProtoCounters returns network statistics for the entire system
|
||||
// If protocols is empty then all protocols are returned, otherwise
|
||||
// just the protocols in the list are returned.
|
||||
// Not Implemented for Windows
|
||||
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
||||
return ProtoCountersWithContext(context.Background(), protocols)
|
||||
}
|
||||
|
||||
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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()
|
||||
}
|
322
internal/gopsutil/process/process.go
Normal file
322
internal/gopsutil/process/process.go
Normal file
|
@ -0,0 +1,322 @@
|
|||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"runtime"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/mem"
|
||||
)
|
||||
|
||||
var (
|
||||
invoke common.Invoker = common.Invoke{}
|
||||
ErrorNoChildren = errors.New("process does not have children")
|
||||
ErrorProcessNotRunning = errors.New("process does not exist")
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
Pid int32 `json:"pid"`
|
||||
name string
|
||||
status string
|
||||
parent int32
|
||||
numCtxSwitches *NumCtxSwitchesStat
|
||||
uids []int32
|
||||
gids []int32
|
||||
groups []int32
|
||||
numThreads int32
|
||||
memInfo *MemoryInfoStat
|
||||
sigInfo *SignalInfoStat
|
||||
createTime int64
|
||||
|
||||
lastCPUTimes *cpu.TimesStat
|
||||
lastCPUTime time.Time
|
||||
|
||||
tgid int32
|
||||
}
|
||||
|
||||
type OpenFilesStat struct {
|
||||
Path string `json:"path"`
|
||||
Fd uint64 `json:"fd"`
|
||||
}
|
||||
|
||||
type MemoryInfoStat struct {
|
||||
RSS uint64 `json:"rss"` // bytes
|
||||
VMS uint64 `json:"vms"` // bytes
|
||||
HWM uint64 `json:"hwm"` // 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 {
|
||||
Resource int32 `json:"resource"`
|
||||
Soft int32 `json:"soft"` // TODO too small. needs to be uint64
|
||||
Hard int32 `json:"hard"` // TODO too small. needs to be uint64
|
||||
Used uint64 `json:"used"`
|
||||
}
|
||||
|
||||
type IOCountersStat struct {
|
||||
ReadCount uint64 `json:"readCount"`
|
||||
WriteCount uint64 `json:"writeCount"`
|
||||
ReadBytes uint64 `json:"readBytes"`
|
||||
WriteBytes uint64 `json:"writeBytes"`
|
||||
}
|
||||
|
||||
type NumCtxSwitchesStat struct {
|
||||
Voluntary int64 `json:"voluntary"`
|
||||
Involuntary int64 `json:"involuntary"`
|
||||
}
|
||||
|
||||
type PageFaultsStat struct {
|
||||
MinorFaults uint64 `json:"minorFaults"`
|
||||
MajorFaults uint64 `json:"majorFaults"`
|
||||
ChildMinorFaults uint64 `json:"childMinorFaults"`
|
||||
ChildMajorFaults uint64 `json:"childMajorFaults"`
|
||||
}
|
||||
|
||||
// Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h
|
||||
// from libc6-dev package in Ubuntu 16.10
|
||||
const (
|
||||
RLIMIT_CPU int32 = 0
|
||||
RLIMIT_FSIZE int32 = 1
|
||||
RLIMIT_DATA int32 = 2
|
||||
RLIMIT_STACK int32 = 3
|
||||
RLIMIT_CORE int32 = 4
|
||||
RLIMIT_RSS int32 = 5
|
||||
RLIMIT_NPROC int32 = 6
|
||||
RLIMIT_NOFILE int32 = 7
|
||||
RLIMIT_MEMLOCK int32 = 8
|
||||
RLIMIT_AS int32 = 9
|
||||
RLIMIT_LOCKS int32 = 10
|
||||
RLIMIT_SIGPENDING int32 = 11
|
||||
RLIMIT_MSGQUEUE int32 = 12
|
||||
RLIMIT_NICE int32 = 13
|
||||
RLIMIT_RTPRIO int32 = 14
|
||||
RLIMIT_RTTIME int32 = 15
|
||||
)
|
||||
|
||||
func (p Process) String() string {
|
||||
s, _ := json.Marshal(p)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (o OpenFilesStat) String() string {
|
||||
s, _ := json.Marshal(o)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (m MemoryInfoStat) String() string {
|
||||
s, _ := json.Marshal(m)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (r RlimitStat) String() string {
|
||||
s, _ := json.Marshal(r)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (i IOCountersStat) String() string {
|
||||
s, _ := json.Marshal(i)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (p NumCtxSwitchesStat) String() string {
|
||||
s, _ := json.Marshal(p)
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Pids returns a slice of process ID list which are running now.
|
||||
func Pids() ([]int32, error) {
|
||||
return PidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func PidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
pids, err := pidsWithContext(ctx)
|
||||
sort.Slice(pids, func(i, j int) bool { return pids[i] < pids[j] })
|
||||
return pids, err
|
||||
}
|
||||
|
||||
// NewProcess creates a new Process instance, it only stores the pid and
|
||||
// checks that the process exists. Other method on Process can be used
|
||||
// to get more information about the process. An error will be returned
|
||||
// if the process does not exist.
|
||||
func NewProcess(pid int32) (*Process, error) {
|
||||
p := &Process{Pid: pid}
|
||||
|
||||
exists, err := PidExists(pid)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
if !exists {
|
||||
return p, ErrorProcessNotRunning
|
||||
}
|
||||
_, err = p.CreateTime()
|
||||
return p, err
|
||||
}
|
||||
|
||||
func PidExists(pid int32) (bool, error) {
|
||||
return PidExistsWithContext(context.Background(), pid)
|
||||
}
|
||||
|
||||
// Background returns true if the process is in background, false otherwise.
|
||||
func (p *Process) Background() (bool, error) {
|
||||
return p.BackgroundWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) {
|
||||
fg, err := p.ForegroundWithContext(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !fg, err
|
||||
}
|
||||
|
||||
// If interval is 0, return difference from last call(non-blocking).
|
||||
// If interval > 0, wait interval sec and return diffrence between start and end.
|
||||
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()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
if interval > 0 {
|
||||
p.lastCPUTimes = cpuTimes
|
||||
p.lastCPUTime = now
|
||||
if err := common.Sleep(ctx, interval); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cpuTimes, err = p.Times()
|
||||
now = time.Now()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
if p.lastCPUTimes == nil {
|
||||
// invoked first time
|
||||
p.lastCPUTimes = cpuTimes
|
||||
p.lastCPUTime = now
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
numcpu := runtime.NumCPU()
|
||||
delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu)
|
||||
ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu)
|
||||
p.lastCPUTimes = cpuTimes
|
||||
p.lastCPUTime = now
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// IsRunning returns whether the process is still running or not.
|
||||
func (p *Process) IsRunning() (bool, error) {
|
||||
return p.IsRunningWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) {
|
||||
createTime, err := p.CreateTimeWithContext(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
p2, err := NewProcess(p.Pid)
|
||||
if err == ErrorProcessNotRunning {
|
||||
return false, nil
|
||||
}
|
||||
createTime2, err := p2.CreateTimeWithContext(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return createTime == createTime2, nil
|
||||
}
|
||||
|
||||
// CreateTime returns created time of the process in milliseconds since the epoch, in UTC.
|
||||
func (p *Process) CreateTime() (int64, error) {
|
||||
return p.CreateTimeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) {
|
||||
if p.createTime != 0 {
|
||||
return p.createTime, nil
|
||||
}
|
||||
createTime, err := p.createTimeWithContext(ctx)
|
||||
p.createTime = createTime
|
||||
return p.createTime, err
|
||||
}
|
||||
|
||||
func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 {
|
||||
if delta == 0 {
|
||||
return 0
|
||||
}
|
||||
delta_proc := t2.Total() - t1.Total()
|
||||
overall_percent := ((delta_proc / delta) * 100) * float64(numcpu)
|
||||
return overall_percent
|
||||
}
|
||||
|
||||
// MemoryPercent returns how many percent of the total RAM this process uses
|
||||
func (p *Process) MemoryPercent() (float32, error) {
|
||||
return p.MemoryPercentWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) {
|
||||
machineMemory, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
total := machineMemory.Total
|
||||
|
||||
processMemory, err := p.MemoryInfo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
used := processMemory.RSS
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Groups returns all group IDs(include supplementary groups) of the process as a slice of the int
|
||||
func (p *Process) Groups() ([]int32, error) {
|
||||
return p.GroupsWithContext(context.Background())
|
||||
}
|
689
internal/gopsutil/process/process_darwin.go
Normal file
689
internal/gopsutil/process/process_darwin.go
Normal file
|
@ -0,0 +1,689 @@
|
|||
//go:build darwin
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/net"
|
||||
)
|
||||
|
||||
// copied from sys/sysctl.h
|
||||
const (
|
||||
CTLKern = 1 // "high kernel": proc, limits
|
||||
KernProc = 14 // struct: process entries
|
||||
KernProcPID = 1 // by process id
|
||||
KernProcProc = 8 // only return procs
|
||||
KernProcAll = 0 // everything
|
||||
KernProcPathname = 12 // path to executable
|
||||
)
|
||||
|
||||
const (
|
||||
ClockTicks = 100 // C.sysconf(C._SC_CLK_TCK)
|
||||
)
|
||||
|
||||
type _Ctype_struct___0 struct {
|
||||
Pad uint64
|
||||
}
|
||||
|
||||
// MemoryInfoExStat is different between OSes
|
||||
type MemoryInfoExStat struct{}
|
||||
|
||||
type MemoryMapsStat struct{}
|
||||
|
||||
func pidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
var ret []int32
|
||||
|
||||
pids, err := callPsWithContext(ctx, "pid", 0, false)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
for _, pid := range pids {
|
||||
v, err := strconv.Atoi(pid[0])
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret = append(ret, int32(v))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) Ppid() (int32, error) {
|
||||
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])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int32(v), err
|
||||
}
|
||||
|
||||
func (p *Process) Name() (string, error) {
|
||||
return p.NameWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := common.IntToString(k.Proc.P_comm[:])
|
||||
|
||||
if len(name) >= 15 {
|
||||
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(cmdlineSlice) > 0 {
|
||||
extendedName := filepath.Base(cmdlineSlice[0])
|
||||
if strings.HasPrefix(extendedName, p.name) {
|
||||
name = extendedName
|
||||
} else {
|
||||
name = cmdlineSlice[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return p.ExeWithContext(context.Background())
|
||||
}
|
||||
|
||||
// Cmdline returns the command line arguments of the process as a string with
|
||||
// each argument separated by 0x20 ascii character.
|
||||
func (p *Process) Cmdline() (string, error) {
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(r[0], " "), err
|
||||
}
|
||||
|
||||
// CmdlineSlice returns the command line arguments of the process as a slice with each
|
||||
// element being an argument. Because of current deficiencies in the way that the command
|
||||
// line arguments are found, single arguments that have spaces in the will actually be
|
||||
// reported as two separate items. In order to do something better CGO would be needed
|
||||
// to use the native darwin functions.
|
||||
func (p *Process) CmdlineSlice() ([]string, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return r[0], err
|
||||
}
|
||||
|
||||
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
|
||||
r, err := callPsWithContext(ctx, "etime", p.Pid, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
elapsedSegments := strings.Split(strings.Replace(r[0][0], "-", ":", 1), ":")
|
||||
var elapsedDurations []time.Duration
|
||||
for i := len(elapsedSegments) - 1; i >= 0; i-- {
|
||||
p, err := strconv.ParseInt(elapsedSegments[i], 10, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
elapsedDurations = append(elapsedDurations, time.Duration(p))
|
||||
}
|
||||
|
||||
elapsed := time.Duration(elapsedDurations[0]) * time.Second
|
||||
if len(elapsedDurations) > 1 {
|
||||
elapsed += time.Duration(elapsedDurations[1]) * time.Minute
|
||||
}
|
||||
if len(elapsedDurations) > 2 {
|
||||
elapsed += time.Duration(elapsedDurations[2]) * time.Hour
|
||||
}
|
||||
if len(elapsedDurations) > 3 {
|
||||
elapsed += time.Duration(elapsedDurations[3]) * time.Hour * 24
|
||||
}
|
||||
|
||||
start := time.Now().Add(-elapsed)
|
||||
return start.Unix() * 1000, nil
|
||||
}
|
||||
|
||||
func (p *Process) Cwd() (string, error) {
|
||||
return p.CwdWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Parent() (*Process, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rr {
|
||||
if strings.HasPrefix(r, "p") { // skip if process
|
||||
continue
|
||||
}
|
||||
l := string(r)
|
||||
v, err := strconv.Atoi(strings.Replace(l, "R", "", 1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewProcess(int32(v))
|
||||
}
|
||||
return nil, fmt.Errorf("could not find parent line")
|
||||
}
|
||||
|
||||
func (p *Process) Status() (string, error) {
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return r[0][0][0:1], err
|
||||
}
|
||||
|
||||
func (p *Process) Foreground() (bool, error) {
|
||||
return p.ForegroundWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
|
||||
// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
|
||||
pid := p.Pid
|
||||
ps, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid)))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.IndexByte(string(out), '+') != -1, nil
|
||||
}
|
||||
|
||||
func (p *Process) Uids() ([]int32, error) {
|
||||
return p.UidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// See: http://unix.superglobalmegacorp.com/Net2/newsrc/sys/ucred.h.html
|
||||
userEffectiveUID := int32(k.Eproc.Ucred.UID)
|
||||
|
||||
return []int32{userEffectiveUID}, nil
|
||||
}
|
||||
|
||||
func (p *Process) Gids() ([]int32, error) {
|
||||
return p.GidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gids := make([]int32, 0, 3)
|
||||
gids = append(gids, int32(k.Eproc.Pcred.P_rgid), int32(k.Eproc.Ucred.Ngroups), int32(k.Eproc.Pcred.P_svgid))
|
||||
|
||||
return gids, nil
|
||||
}
|
||||
|
||||
func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups := make([]int32, k.Eproc.Ucred.Ngroups)
|
||||
for i := int16(0); i < k.Eproc.Ucred.Ngroups; i++ {
|
||||
groups[i] = int32(k.Eproc.Ucred.Groups[i])
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (p *Process) Terminal() (string, error) {
|
||||
return p.TerminalWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
/*
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ttyNr := uint64(k.Eproc.Tdev)
|
||||
termmap, err := getTerminalMap()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return termmap[ttyNr], nil
|
||||
*/
|
||||
}
|
||||
|
||||
func (p *Process) Nice() (int32, error) {
|
||||
return p.NiceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(k.Proc.P_nice), nil
|
||||
}
|
||||
|
||||
func (p *Process) IOnice() (int32, error) {
|
||||
return p.IOniceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
return rlimit, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) IOCounters() (*IOCountersStat, error) {
|
||||
return p.IOCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
|
||||
return p.NumCtxSwitchesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumFDs() (int32, error) {
|
||||
return p.NumFDsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumThreads() (int32, error) {
|
||||
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 {
|
||||
return 0, err
|
||||
}
|
||||
return int32(len(r)), nil
|
||||
}
|
||||
|
||||
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) {
|
||||
ret := make(map[int32]*cpu.TimesStat)
|
||||
return ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func convertCPUTimes(s string) (ret float64, err error) {
|
||||
var t int
|
||||
var _tmp string
|
||||
if strings.Contains(s, ":") {
|
||||
_t := strings.Split(s, ":")
|
||||
switch len(_t) {
|
||||
case 3:
|
||||
hour, err := strconv.Atoi(_t[0])
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
t += hour * 60 * 60 * ClockTicks
|
||||
|
||||
mins, err := strconv.Atoi(_t[1])
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
t += mins * 60 * ClockTicks
|
||||
_tmp = _t[2]
|
||||
case 2:
|
||||
mins, err := strconv.Atoi(_t[0])
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
t += mins * 60 * ClockTicks
|
||||
_tmp = _t[1]
|
||||
case 1, 0:
|
||||
_tmp = s
|
||||
default:
|
||||
return ret, fmt.Errorf("wrong cpu time string")
|
||||
}
|
||||
} else {
|
||||
_tmp = s
|
||||
}
|
||||
|
||||
_t := strings.Split(_tmp, ".")
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
h, _ := strconv.Atoi(_t[0])
|
||||
t += h * ClockTicks
|
||||
h, _ = strconv.Atoi(_t[1])
|
||||
t += h
|
||||
return float64(t) / ClockTicks, nil
|
||||
}
|
||||
|
||||
func (p *Process) Times() (*cpu.TimesStat, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
utime, err := convertCPUTimes(r[0][0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stime, err := convertCPUTimes(r[0][1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &cpu.TimesStat{
|
||||
CPU: "cpu",
|
||||
User: utime,
|
||||
System: stime,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinity() ([]int32, error) {
|
||||
return p.CPUAffinityWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
rss, err := strconv.Atoi(r[0][0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vms, err := strconv.Atoi(r[0][1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pagein, err := strconv.Atoi(r[0][2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &MemoryInfoStat{
|
||||
RSS: uint64(rss) * 1024,
|
||||
VMS: uint64(vms) * 1024,
|
||||
Swap: uint64(pagein),
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
|
||||
return p.MemoryInfoExWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) PageFaults() (*PageFaultsStat, error) {
|
||||
return p.PageFaultsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Children() ([]*Process, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcess(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
|
||||
return p.OpenFilesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Connections returns a slice of net.ConnectionStat used by the process at most `max`
|
||||
func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) {
|
||||
return p.ConnectionsMaxWithContext(context.Background(), max)
|
||||
}
|
||||
|
||||
func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
|
||||
return net.ConnectionsPidMax("all", p.Pid, max)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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 parseKinfoProc(buf []byte) (KinfoProc, error) {
|
||||
var k KinfoProc
|
||||
br := bytes.NewReader(buf)
|
||||
|
||||
err := common.Read(br, binary.LittleEndian, &k)
|
||||
if err != nil {
|
||||
return k, err
|
||||
}
|
||||
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// Returns a proc as defined here:
|
||||
// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/kinfo_proc.h.html
|
||||
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}
|
||||
length := uint64(unsafe.Sizeof(KinfoProc{}))
|
||||
buf := make([]byte, length)
|
||||
_, _, syserr := unix.Syscall6(
|
||||
202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146
|
||||
uintptr(unsafe.Pointer(&mib[0])),
|
||||
uintptr(len(mib)),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if syserr != 0 {
|
||||
return nil, syserr
|
||||
}
|
||||
k, err := parseKinfoProc(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
// call ps command.
|
||||
// Return value deletes Header line(you must not input wrong arg).
|
||||
// And splited by Space. Caller have responsibility to manage.
|
||||
// If passed arg pid is 0, get information from all process.
|
||||
func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) {
|
||||
bin, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return [][]string{}, err
|
||||
}
|
||||
|
||||
var cmd []string
|
||||
if pid == 0 { // will get from all processes.
|
||||
cmd = []string{"-ax", "-o", arg}
|
||||
} else if threadOption {
|
||||
cmd = []string{"-x", "-o", arg, "-M", "-p", strconv.Itoa(int(pid))}
|
||||
} else {
|
||||
cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))}
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, bin, cmd...)
|
||||
if err != nil {
|
||||
return [][]string{}, err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
var ret [][]string
|
||||
for _, l := range lines[1:] {
|
||||
var lr []string
|
||||
for _, r := range strings.Split(l, " ") {
|
||||
if r == "" {
|
||||
continue
|
||||
}
|
||||
lr = append(lr, strings.TrimSpace(r))
|
||||
}
|
||||
if len(lr) != 0 {
|
||||
ret = append(ret, lr)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
234
internal/gopsutil/process/process_darwin_386.go
Normal file
234
internal/gopsutil/process/process_darwin_386.go
Normal file
|
@ -0,0 +1,234 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_darwin.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type UGid_t uint32
|
||||
|
||||
type KinfoProc struct {
|
||||
Proc ExternProc
|
||||
Eproc Eproc
|
||||
}
|
||||
|
||||
type Eproc struct {
|
||||
Paddr *uint64
|
||||
Sess *Session
|
||||
Pcred Upcred
|
||||
Ucred Uucred
|
||||
Pad_cgo_0 [4]byte
|
||||
Vm Vmspace
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Jobc int16
|
||||
Pad_cgo_1 [2]byte
|
||||
Tdev int32
|
||||
Tpgid int32
|
||||
Pad_cgo_2 [4]byte
|
||||
Tsess *Session
|
||||
Wmesg [8]int8
|
||||
Xsize int32
|
||||
Xrssize int16
|
||||
Xccount int16
|
||||
Xswrss int16
|
||||
Pad_cgo_3 [2]byte
|
||||
Flag int32
|
||||
Login [12]int8
|
||||
Spare [4]int32
|
||||
Pad_cgo_4 [4]byte
|
||||
}
|
||||
|
||||
type Proc struct{}
|
||||
|
||||
type Session struct{}
|
||||
|
||||
type ucred struct {
|
||||
Link _Ctype_struct___0
|
||||
Ref uint64
|
||||
Posix Posix_cred
|
||||
Label *Label
|
||||
Audit Au_session
|
||||
}
|
||||
|
||||
type Uucred struct {
|
||||
Ref int32
|
||||
UID uint32
|
||||
Ngroups int16
|
||||
Pad_cgo_0 [2]byte
|
||||
Groups [16]uint32
|
||||
}
|
||||
|
||||
type Upcred struct {
|
||||
Pc_lock [72]int8
|
||||
Pc_ucred *ucred
|
||||
P_ruid uint32
|
||||
P_svuid uint32
|
||||
P_rgid uint32
|
||||
P_svgid uint32
|
||||
P_refcnt int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Vmspace struct {
|
||||
Dummy int32
|
||||
Pad_cgo_0 [4]byte
|
||||
Dummy2 *int8
|
||||
Dummy3 [5]int32
|
||||
Pad_cgo_1 [4]byte
|
||||
Dummy4 [3]*int8
|
||||
}
|
||||
|
||||
type Sigacts struct{}
|
||||
|
||||
type ExternProc struct {
|
||||
P_un [16]byte
|
||||
P_vmspace uint64
|
||||
P_sigacts uint64
|
||||
Pad_cgo_0 [3]byte
|
||||
P_flag int32
|
||||
P_stat int8
|
||||
P_pid int32
|
||||
P_oppid int32
|
||||
P_dupfd int32
|
||||
Pad_cgo_1 [4]byte
|
||||
User_stack uint64
|
||||
Exit_thread uint64
|
||||
P_debugger int32
|
||||
Sigwait int32
|
||||
P_estcpu uint32
|
||||
P_cpticks int32
|
||||
P_pctcpu uint32
|
||||
Pad_cgo_2 [4]byte
|
||||
P_wchan uint64
|
||||
P_wmesg uint64
|
||||
P_swtime uint32
|
||||
P_slptime uint32
|
||||
P_realtimer Itimerval
|
||||
P_rtime Timeval
|
||||
P_uticks uint64
|
||||
P_sticks uint64
|
||||
P_iticks uint64
|
||||
P_traceflag int32
|
||||
Pad_cgo_3 [4]byte
|
||||
P_tracep uint64
|
||||
P_siglist int32
|
||||
Pad_cgo_4 [4]byte
|
||||
P_textvp uint64
|
||||
P_holdcnt int32
|
||||
P_sigmask uint32
|
||||
P_sigignore uint32
|
||||
P_sigcatch uint32
|
||||
P_priority uint8
|
||||
P_usrpri uint8
|
||||
P_nice int8
|
||||
P_comm [17]int8
|
||||
Pad_cgo_5 [4]byte
|
||||
P_pgrp uint64
|
||||
P_addr uint64
|
||||
P_xstat uint16
|
||||
P_acflag uint16
|
||||
Pad_cgo_6 [4]byte
|
||||
P_ru uint64
|
||||
}
|
||||
|
||||
type Itimerval struct {
|
||||
Interval Timeval
|
||||
Value Timeval
|
||||
}
|
||||
|
||||
type Vnode struct{}
|
||||
|
||||
type Pgrp struct{}
|
||||
|
||||
type UserStruct struct{}
|
||||
|
||||
type Au_session struct {
|
||||
Aia_p *AuditinfoAddr
|
||||
Mask AuMask
|
||||
}
|
||||
|
||||
type Posix_cred struct {
|
||||
UID uint32
|
||||
Ruid uint32
|
||||
Svuid uint32
|
||||
Ngroups int16
|
||||
Pad_cgo_0 [2]byte
|
||||
Groups [16]uint32
|
||||
Rgid uint32
|
||||
Svgid uint32
|
||||
Gmuid uint32
|
||||
Flags int32
|
||||
}
|
||||
|
||||
type Label struct{}
|
||||
|
||||
type AuditinfoAddr struct {
|
||||
Auid uint32
|
||||
Mask AuMask
|
||||
Termid AuTidAddr
|
||||
Asid int32
|
||||
Flags uint64
|
||||
}
|
||||
type AuMask struct {
|
||||
Success uint32
|
||||
Failure uint32
|
||||
}
|
||||
type AuTidAddr struct {
|
||||
Port int32
|
||||
Type uint32
|
||||
Addr [4]uint32
|
||||
}
|
||||
|
||||
type UcredQueue struct {
|
||||
Next *ucred
|
||||
Prev **ucred
|
||||
}
|
234
internal/gopsutil/process/process_darwin_amd64.go
Normal file
234
internal/gopsutil/process/process_darwin_amd64.go
Normal file
|
@ -0,0 +1,234 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_darwin.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type UGid_t uint32
|
||||
|
||||
type KinfoProc struct {
|
||||
Proc ExternProc
|
||||
Eproc Eproc
|
||||
}
|
||||
|
||||
type Eproc struct {
|
||||
Paddr *uint64
|
||||
Sess *Session
|
||||
Pcred Upcred
|
||||
Ucred Uucred
|
||||
Pad_cgo_0 [4]byte
|
||||
Vm Vmspace
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Jobc int16
|
||||
Pad_cgo_1 [2]byte
|
||||
Tdev int32
|
||||
Tpgid int32
|
||||
Pad_cgo_2 [4]byte
|
||||
Tsess *Session
|
||||
Wmesg [8]int8
|
||||
Xsize int32
|
||||
Xrssize int16
|
||||
Xccount int16
|
||||
Xswrss int16
|
||||
Pad_cgo_3 [2]byte
|
||||
Flag int32
|
||||
Login [12]int8
|
||||
Spare [4]int32
|
||||
Pad_cgo_4 [4]byte
|
||||
}
|
||||
|
||||
type Proc struct{}
|
||||
|
||||
type Session struct{}
|
||||
|
||||
type ucred struct {
|
||||
Link _Ctype_struct___0
|
||||
Ref uint64
|
||||
Posix Posix_cred
|
||||
Label *Label
|
||||
Audit Au_session
|
||||
}
|
||||
|
||||
type Uucred struct {
|
||||
Ref int32
|
||||
UID uint32
|
||||
Ngroups int16
|
||||
Pad_cgo_0 [2]byte
|
||||
Groups [16]uint32
|
||||
}
|
||||
|
||||
type Upcred struct {
|
||||
Pc_lock [72]int8
|
||||
Pc_ucred *ucred
|
||||
P_ruid uint32
|
||||
P_svuid uint32
|
||||
P_rgid uint32
|
||||
P_svgid uint32
|
||||
P_refcnt int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Vmspace struct {
|
||||
Dummy int32
|
||||
Pad_cgo_0 [4]byte
|
||||
Dummy2 *int8
|
||||
Dummy3 [5]int32
|
||||
Pad_cgo_1 [4]byte
|
||||
Dummy4 [3]*int8
|
||||
}
|
||||
|
||||
type Sigacts struct{}
|
||||
|
||||
type ExternProc struct {
|
||||
P_un [16]byte
|
||||
P_vmspace uint64
|
||||
P_sigacts uint64
|
||||
Pad_cgo_0 [3]byte
|
||||
P_flag int32
|
||||
P_stat int8
|
||||
P_pid int32
|
||||
P_oppid int32
|
||||
P_dupfd int32
|
||||
Pad_cgo_1 [4]byte
|
||||
User_stack uint64
|
||||
Exit_thread uint64
|
||||
P_debugger int32
|
||||
Sigwait int32
|
||||
P_estcpu uint32
|
||||
P_cpticks int32
|
||||
P_pctcpu uint32
|
||||
Pad_cgo_2 [4]byte
|
||||
P_wchan uint64
|
||||
P_wmesg uint64
|
||||
P_swtime uint32
|
||||
P_slptime uint32
|
||||
P_realtimer Itimerval
|
||||
P_rtime Timeval
|
||||
P_uticks uint64
|
||||
P_sticks uint64
|
||||
P_iticks uint64
|
||||
P_traceflag int32
|
||||
Pad_cgo_3 [4]byte
|
||||
P_tracep uint64
|
||||
P_siglist int32
|
||||
Pad_cgo_4 [4]byte
|
||||
P_textvp uint64
|
||||
P_holdcnt int32
|
||||
P_sigmask uint32
|
||||
P_sigignore uint32
|
||||
P_sigcatch uint32
|
||||
P_priority uint8
|
||||
P_usrpri uint8
|
||||
P_nice int8
|
||||
P_comm [17]int8
|
||||
Pad_cgo_5 [4]byte
|
||||
P_pgrp uint64
|
||||
P_addr uint64
|
||||
P_xstat uint16
|
||||
P_acflag uint16
|
||||
Pad_cgo_6 [4]byte
|
||||
P_ru uint64
|
||||
}
|
||||
|
||||
type Itimerval struct {
|
||||
Interval Timeval
|
||||
Value Timeval
|
||||
}
|
||||
|
||||
type Vnode struct{}
|
||||
|
||||
type Pgrp struct{}
|
||||
|
||||
type UserStruct struct{}
|
||||
|
||||
type Au_session struct {
|
||||
Aia_p *AuditinfoAddr
|
||||
Mask AuMask
|
||||
}
|
||||
|
||||
type Posix_cred struct {
|
||||
UID uint32
|
||||
Ruid uint32
|
||||
Svuid uint32
|
||||
Ngroups int16
|
||||
Pad_cgo_0 [2]byte
|
||||
Groups [16]uint32
|
||||
Rgid uint32
|
||||
Svgid uint32
|
||||
Gmuid uint32
|
||||
Flags int32
|
||||
}
|
||||
|
||||
type Label struct{}
|
||||
|
||||
type AuditinfoAddr struct {
|
||||
Auid uint32
|
||||
Mask AuMask
|
||||
Termid AuTidAddr
|
||||
Asid int32
|
||||
Flags uint64
|
||||
}
|
||||
type AuMask struct {
|
||||
Success uint32
|
||||
Failure uint32
|
||||
}
|
||||
type AuTidAddr struct {
|
||||
Port int32
|
||||
Type uint32
|
||||
Addr [4]uint32
|
||||
}
|
||||
|
||||
type UcredQueue struct {
|
||||
Next *ucred
|
||||
Prev **ucred
|
||||
}
|
205
internal/gopsutil/process/process_darwin_arm64.go
Normal file
205
internal/gopsutil/process/process_darwin_arm64.go
Normal file
|
@ -0,0 +1,205 @@
|
|||
//go:build darwin && arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs process/types_darwin.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type UGid_t uint32
|
||||
|
||||
type KinfoProc struct {
|
||||
Proc ExternProc
|
||||
Eproc Eproc
|
||||
}
|
||||
|
||||
type Eproc struct {
|
||||
Paddr *Proc
|
||||
Sess *Session
|
||||
Pcred Upcred
|
||||
Ucred Uucred
|
||||
Vm Vmspace
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Jobc int16
|
||||
Tdev int32
|
||||
Tpgid int32
|
||||
Tsess *Session
|
||||
Wmesg [8]int8
|
||||
Xsize int32
|
||||
Xrssize int16
|
||||
Xccount int16
|
||||
Xswrss int16
|
||||
Flag int32
|
||||
Login [12]int8
|
||||
Spare [4]int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Proc struct{}
|
||||
|
||||
type Session struct{}
|
||||
|
||||
type ucred struct{}
|
||||
|
||||
type Uucred struct {
|
||||
Ref int32
|
||||
UID uint32
|
||||
Ngroups int16
|
||||
Groups [16]uint32
|
||||
}
|
||||
|
||||
type Upcred struct {
|
||||
Pc_lock [72]int8
|
||||
Pc_ucred *ucred
|
||||
P_ruid uint32
|
||||
P_svuid uint32
|
||||
P_rgid uint32
|
||||
P_svgid uint32
|
||||
P_refcnt int32
|
||||
Pad_cgo_0 [4]byte
|
||||
}
|
||||
|
||||
type Vmspace struct {
|
||||
Dummy int32
|
||||
Dummy2 *int8
|
||||
Dummy3 [5]int32
|
||||
Dummy4 [3]*int8
|
||||
}
|
||||
|
||||
type Sigacts struct{}
|
||||
|
||||
type ExternProc struct {
|
||||
P_un [16]byte
|
||||
P_vmspace *Vmspace
|
||||
P_sigacts *Sigacts
|
||||
P_flag int32
|
||||
P_stat int8
|
||||
P_pid int32
|
||||
P_oppid int32
|
||||
P_dupfd int32
|
||||
User_stack *int8
|
||||
Exit_thread *byte
|
||||
P_debugger int32
|
||||
Sigwait int32
|
||||
P_estcpu uint32
|
||||
P_cpticks int32
|
||||
P_pctcpu uint32
|
||||
P_wchan *byte
|
||||
P_wmesg *int8
|
||||
P_swtime uint32
|
||||
P_slptime uint32
|
||||
P_realtimer Itimerval
|
||||
P_rtime Timeval
|
||||
P_uticks uint64
|
||||
P_sticks uint64
|
||||
P_iticks uint64
|
||||
P_traceflag int32
|
||||
P_tracep *Vnode
|
||||
P_siglist int32
|
||||
P_textvp *Vnode
|
||||
P_holdcnt int32
|
||||
P_sigmask uint32
|
||||
P_sigignore uint32
|
||||
P_sigcatch uint32
|
||||
P_priority uint8
|
||||
P_usrpri uint8
|
||||
P_nice int8
|
||||
P_comm [17]int8
|
||||
P_pgrp *Pgrp
|
||||
P_addr *UserStruct
|
||||
P_xstat uint16
|
||||
P_acflag uint16
|
||||
P_ru *Rusage
|
||||
}
|
||||
|
||||
type Itimerval struct {
|
||||
Interval Timeval
|
||||
Value Timeval
|
||||
}
|
||||
|
||||
type Vnode struct{}
|
||||
|
||||
type Pgrp struct{}
|
||||
|
||||
type UserStruct struct{}
|
||||
|
||||
type Au_session struct {
|
||||
Aia_p *AuditinfoAddr
|
||||
Mask AuMask
|
||||
}
|
||||
|
||||
type Posix_cred struct{}
|
||||
|
||||
type Label struct{}
|
||||
|
||||
type AuditinfoAddr struct {
|
||||
Auid uint32
|
||||
Mask AuMask
|
||||
Termid AuTidAddr
|
||||
Asid int32
|
||||
Flags uint64
|
||||
}
|
||||
type AuMask struct {
|
||||
Success uint32
|
||||
Failure uint32
|
||||
}
|
||||
type AuTidAddr struct {
|
||||
Port int32
|
||||
Type uint32
|
||||
Addr [4]uint32
|
||||
}
|
||||
|
||||
type UcredQueue struct {
|
||||
Next *ucred
|
||||
Prev **ucred
|
||||
}
|
30
internal/gopsutil/process/process_darwin_cgo.go
Normal file
30
internal/gopsutil/process/process_darwin_cgo.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
//go:build darwin && cgo
|
||||
|
||||
package process
|
||||
|
||||
// #include <stdlib.h>
|
||||
// #include <libproc.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
var c C.char // need a var for unsafe.Sizeof need a var
|
||||
const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c)
|
||||
buffer := (*C.char)(C.malloc(C.size_t(bufsize)))
|
||||
defer C.free(unsafe.Pointer(buffer))
|
||||
|
||||
ret, err := C.proc_pidpath(C.int(p.Pid), unsafe.Pointer(buffer), C.uint32_t(bufsize))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ret <= 0 {
|
||||
return "", fmt.Errorf("unknown error: proc_pidpath returned %d", ret)
|
||||
}
|
||||
|
||||
return C.GoString(buffer), nil
|
||||
}
|
33
internal/gopsutil/process/process_darwin_nocgo.go
Normal file
33
internal/gopsutil/process/process_darwin_nocgo.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
//go:build darwin && !cgo
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
lsof_bin, err := exec.LookPath("lsof")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, lsof_bin, "-p", strconv.Itoa(int(p.Pid)), "-Fpfn")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("bad call to lsof: %s", err)
|
||||
}
|
||||
txtFound := 0
|
||||
lines := strings.Split(string(out), "\n")
|
||||
for i := 1; i < len(lines); i++ {
|
||||
if lines[i] == "ftxt" {
|
||||
txtFound++
|
||||
if txtFound == 2 {
|
||||
return lines[i-1][1:], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("missing txt data returned by lsof")
|
||||
}
|
371
internal/gopsutil/process/process_fallback.go
Normal file
371
internal/gopsutil/process/process_fallback.go
Normal file
|
@ -0,0 +1,371 @@
|
|||
//go:build !darwin && !linux && !freebsd && !openbsd && !windows
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/net"
|
||||
)
|
||||
|
||||
type MemoryMapsStat struct {
|
||||
Path string `json:"path"`
|
||||
Rss uint64 `json:"rss"`
|
||||
Size uint64 `json:"size"`
|
||||
Pss uint64 `json:"pss"`
|
||||
SharedClean uint64 `json:"sharedClean"`
|
||||
SharedDirty uint64 `json:"sharedDirty"`
|
||||
PrivateClean uint64 `json:"privateClean"`
|
||||
PrivateDirty uint64 `json:"privateDirty"`
|
||||
Referenced uint64 `json:"referenced"`
|
||||
Anonymous uint64 `json:"anonymous"`
|
||||
Swap uint64 `json:"swap"`
|
||||
}
|
||||
|
||||
type MemoryInfoExStat struct{}
|
||||
|
||||
func pidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
return []int32{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Processes() ([]*Process, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) {
|
||||
pids, err := PidsWithContext(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, i := range pids {
|
||||
if i == pid {
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (p *Process) Ppid() (int32, error) {
|
||||
return p.PpidWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Name() (string, error) {
|
||||
return p.NameWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return p.ExeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Cmdline() (string, error) {
|
||||
return p.CmdlineWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineSlice() ([]string, error) {
|
||||
return p.CmdlineSliceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
|
||||
return []string{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Cwd() (string, error) {
|
||||
return p.CwdWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Parent() (*Process, error) {
|
||||
return p.ParentWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Status() (string, error) {
|
||||
return p.StatusWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Foreground() (bool, error) {
|
||||
return p.ForegroundWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
|
||||
return false, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Uids() ([]int32, error) {
|
||||
return p.UidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
return []int32{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Gids() ([]int32, error) {
|
||||
return p.GidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
return []int32{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
|
||||
return []int32{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Terminal() (string, error) {
|
||||
return p.TerminalWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Nice() (int32, error) {
|
||||
return p.NiceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) IOnice() (int32, error) {
|
||||
return p.IOniceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *Process) IOCounters() (*IOCountersStat, error) {
|
||||
return p.IOCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
|
||||
return p.NumCtxSwitchesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumFDs() (int32, error) {
|
||||
return p.NumFDsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumThreads() (int32, error) {
|
||||
return p.NumThreadsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinity() ([]int32, error) {
|
||||
return p.CPUAffinityWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
|
||||
return p.MemoryInfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
|
||||
return p.MemoryInfoExWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) PageFaults() (*PageFaultsStat, error) {
|
||||
return p.PageFaultsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Children() ([]*Process, error) {
|
||||
return p.ChildrenWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
|
||||
return p.OpenFilesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
|
||||
return []OpenFilesStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) {
|
||||
return p.ConnectionsMaxWithContext(context.Background(), max)
|
||||
}
|
||||
|
||||
func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
|
||||
return []net.ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *Process) Suspend() error {
|
||||
return p.SuspendWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) SuspendWithContext(ctx context.Context) error {
|
||||
return common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Resume() error {
|
||||
return p.ResumeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ResumeWithContext(ctx context.Context) error {
|
||||
return common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Terminate() error {
|
||||
return p.TerminateWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) TerminateWithContext(ctx context.Context) error {
|
||||
return common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Kill() error {
|
||||
return p.KillWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) KillWithContext(ctx context.Context) error {
|
||||
return common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Username() (string, error) {
|
||||
return p.UsernameWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
549
internal/gopsutil/process/process_freebsd.go
Normal file
549
internal/gopsutil/process/process_freebsd.go
Normal file
|
@ -0,0 +1,549 @@
|
|||
//go:build freebsd
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
cpu "github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
|
||||
net "github.com/gofiber/fiber/v2/internal/gopsutil/net"
|
||||
)
|
||||
|
||||
// MemoryInfoExStat is different between OSes
|
||||
type MemoryInfoExStat struct{}
|
||||
|
||||
type MemoryMapsStat struct{}
|
||||
|
||||
func pidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
var ret []int32
|
||||
procs, err := Processes()
|
||||
if err != nil {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
for _, p := range procs {
|
||||
ret = append(ret, p.Pid)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) Ppid() (int32, error) {
|
||||
return p.PpidWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return k.Ppid, nil
|
||||
}
|
||||
|
||||
func (p *Process) Name() (string, error) {
|
||||
return p.NameWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := common.IntToString(k.Comm[:])
|
||||
|
||||
if len(name) >= 15 {
|
||||
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(cmdlineSlice) > 0 {
|
||||
extendedName := filepath.Base(cmdlineSlice[0])
|
||||
if strings.HasPrefix(extendedName, p.name) {
|
||||
name = extendedName
|
||||
} else {
|
||||
name = cmdlineSlice[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return p.ExeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ret := strings.FieldsFunc(string(buf), func(r rune) bool {
|
||||
if r == '\u0000' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
return strings.Join(ret, " "), nil
|
||||
}
|
||||
|
||||
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}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if buf[len(buf)-1] == 0 {
|
||||
buf = buf[:len(buf)-1]
|
||||
}
|
||||
parts := bytes.Split(buf, []byte{0})
|
||||
var strParts []string
|
||||
for _, p := range parts {
|
||||
strParts = append(strParts, string(p))
|
||||
}
|
||||
|
||||
return strParts, nil
|
||||
}
|
||||
|
||||
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Cwd() (string, error) {
|
||||
return p.CwdWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Parent() (*Process, error) {
|
||||
return p.ParentWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
|
||||
return p, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Status() (string, error) {
|
||||
return p.StatusWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var s string
|
||||
switch k.Stat {
|
||||
case SIDL:
|
||||
s = "I"
|
||||
case SRUN:
|
||||
s = "R"
|
||||
case SSLEEP:
|
||||
s = "S"
|
||||
case SSTOP:
|
||||
s = "T"
|
||||
case SZOMB:
|
||||
s = "Z"
|
||||
case SWAIT:
|
||||
s = "W"
|
||||
case SLOCK:
|
||||
s = "L"
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (p *Process) Foreground() (bool, error) {
|
||||
return p.ForegroundWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
|
||||
// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
|
||||
pid := p.Pid
|
||||
ps, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid)))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.IndexByte(string(out), '+') != -1, nil
|
||||
}
|
||||
|
||||
func (p *Process) Uids() ([]int32, error) {
|
||||
return p.UidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uids := make([]int32, 0, 3)
|
||||
|
||||
uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid))
|
||||
|
||||
return uids, nil
|
||||
}
|
||||
|
||||
func (p *Process) Gids() ([]int32, error) {
|
||||
return p.GidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gids := make([]int32, 0, 3)
|
||||
gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid))
|
||||
|
||||
return gids, nil
|
||||
}
|
||||
|
||||
func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups := make([]int32, k.Ngroups)
|
||||
for i := int16(0); i < k.Ngroups; i++ {
|
||||
groups[i] = int32(k.Groups[i])
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (p *Process) Terminal() (string, error) {
|
||||
return p.TerminalWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ttyNr := uint64(k.Tdev)
|
||||
|
||||
termmap, err := getTerminalMap()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return termmap[ttyNr], nil
|
||||
}
|
||||
|
||||
func (p *Process) Nice() (int32, error) {
|
||||
return p.NiceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(k.Nice), nil
|
||||
}
|
||||
|
||||
func (p *Process) IOnice() (int32, error) {
|
||||
return p.IOniceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
return rlimit, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) IOCounters() (*IOCountersStat, error) {
|
||||
return p.IOCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IOCountersStat{
|
||||
ReadCount: uint64(k.Rusage.Inblock),
|
||||
WriteCount: uint64(k.Rusage.Oublock),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
|
||||
return p.NumCtxSwitchesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumFDs() (int32, error) {
|
||||
return p.NumFDsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumThreads() (int32, error) {
|
||||
return p.NumThreadsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return k.Numthreads, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
ret := make(map[int32]*cpu.TimesStat)
|
||||
return ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cpu.TimesStat{
|
||||
CPU: "cpu",
|
||||
User: float64(k.Rusage.Utime.Sec) + float64(k.Rusage.Utime.Usec)/1000000,
|
||||
System: float64(k.Rusage.Stime.Sec) + float64(k.Rusage.Stime.Usec)/1000000,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinity() ([]int32, error) {
|
||||
return p.CPUAffinityWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
|
||||
return p.MemoryInfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := unix.Sysctl("vm.stats.vm.v_page_size")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageSize := common.LittleEndian.Uint16([]byte(v))
|
||||
|
||||
return &MemoryInfoStat{
|
||||
RSS: uint64(k.Rssize) * uint64(pageSize),
|
||||
VMS: uint64(k.Size),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
|
||||
return p.MemoryInfoExWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) PageFaults() (*PageFaultsStat, error) {
|
||||
return p.PageFaultsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Children() ([]*Process, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcess(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
|
||||
return p.OpenFilesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Connections returns a slice of net.ConnectionStat used by the process at most `max`
|
||||
func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) {
|
||||
return p.ConnectionsMaxWithContext(context.Background(), max)
|
||||
}
|
||||
|
||||
func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
|
||||
return []net.ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Processes() ([]*Process, error) {
|
||||
return ProcessesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
|
||||
results := []*Process{}
|
||||
|
||||
mib := []int32{CTLKern, KernProc, KernProcProc, 0}
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
// get kinfo_proc size
|
||||
count := int(length / uint64(sizeOfKinfoProc))
|
||||
|
||||
// parse buf to procs
|
||||
for i := 0; i < count; i++ {
|
||||
b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc]
|
||||
k, err := parseKinfoProc(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p, err := NewProcess(int32(k.Pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func parseKinfoProc(buf []byte) (KinfoProc, error) {
|
||||
var k KinfoProc
|
||||
br := bytes.NewReader(buf)
|
||||
err := common.Read(br, binary.LittleEndian, &k)
|
||||
return k, err
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
buf, length, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length != sizeOfKinfoProc {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k, err := parseKinfoProc(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &k, nil
|
||||
}
|
192
internal/gopsutil/process/process_freebsd_386.go
Normal file
192
internal/gopsutil/process/process_freebsd_386.go
Normal file
|
@ -0,0 +1,192 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
CTLKern = 1
|
||||
KernProc = 14
|
||||
KernProcPID = 1
|
||||
KernProcProc = 8
|
||||
KernProcPathname = 12
|
||||
KernProcArgs = 7
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x4
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfKinfoVmentry = 0x488
|
||||
sizeOfKinfoProc = 0x300
|
||||
)
|
||||
|
||||
const (
|
||||
SIDL = 1
|
||||
SRUN = 2
|
||||
SSLEEP = 3
|
||||
SSTOP = 4
|
||||
SZOMB = 5
|
||||
SWAIT = 6
|
||||
SLOCK = 7
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int32
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int32
|
||||
Nsec int32
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int32
|
||||
Usec int32
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int32
|
||||
Ixrss int32
|
||||
Idrss int32
|
||||
Isrss int32
|
||||
Minflt int32
|
||||
Majflt int32
|
||||
Nswap int32
|
||||
Inblock int32
|
||||
Oublock int32
|
||||
Msgsnd int32
|
||||
Msgrcv int32
|
||||
Nsignals int32
|
||||
Nvcsw int32
|
||||
Nivcsw int32
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur int64
|
||||
Max int64
|
||||
}
|
||||
|
||||
type KinfoProc struct {
|
||||
Structsize int32
|
||||
Layout int32
|
||||
Args int32 /* pargs */
|
||||
Paddr int32 /* proc */
|
||||
Addr int32 /* user */
|
||||
Tracep int32 /* vnode */
|
||||
Textvp int32 /* vnode */
|
||||
Fd int32 /* filedesc */
|
||||
Vmspace int32 /* vmspace */
|
||||
Wchan int32
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Tpgid int32
|
||||
Sid int32
|
||||
Tsid int32
|
||||
Jobc int16
|
||||
Spare_short1 int16
|
||||
Tdev uint32
|
||||
Siglist [16]byte /* sigset */
|
||||
Sigmask [16]byte /* sigset */
|
||||
Sigignore [16]byte /* sigset */
|
||||
Sigcatch [16]byte /* sigset */
|
||||
Uid uint32
|
||||
Ruid uint32
|
||||
Svuid uint32
|
||||
Rgid uint32
|
||||
Svgid uint32
|
||||
Ngroups int16
|
||||
Spare_short2 int16
|
||||
Groups [16]uint32
|
||||
Size uint32
|
||||
Rssize int32
|
||||
Swrss int32
|
||||
Tsize int32
|
||||
Dsize int32
|
||||
Ssize int32
|
||||
Xstat uint16
|
||||
Acflag uint16
|
||||
Pctcpu uint32
|
||||
Estcpu uint32
|
||||
Slptime uint32
|
||||
Swtime uint32
|
||||
Cow uint32
|
||||
Runtime uint64
|
||||
Start Timeval
|
||||
Childtime Timeval
|
||||
Flag int32
|
||||
Kiflag int32
|
||||
Traceflag int32
|
||||
Stat int8
|
||||
Nice int8
|
||||
Lock int8
|
||||
Rqindex int8
|
||||
Oncpu uint8
|
||||
Lastcpu uint8
|
||||
Tdname [17]int8
|
||||
Wmesg [9]int8
|
||||
Login [18]int8
|
||||
Lockname [9]int8
|
||||
Comm [20]int8
|
||||
Emul [17]int8
|
||||
Loginclass [18]int8
|
||||
Sparestrings [50]int8
|
||||
Spareints [7]int32
|
||||
Flag2 int32
|
||||
Fibnum int32
|
||||
Cr_flags uint32
|
||||
Jid int32
|
||||
Numthreads int32
|
||||
Tid int32
|
||||
Pri Priority
|
||||
Rusage Rusage
|
||||
Rusage_ch Rusage
|
||||
Pcb int32 /* pcb */
|
||||
Kstack int32
|
||||
Udata int32
|
||||
Tdaddr int32 /* thread */
|
||||
Spareptrs [6]int32
|
||||
Sparelongs [12]int32
|
||||
Sflag int32
|
||||
Tdflags int32
|
||||
}
|
||||
|
||||
type Priority struct {
|
||||
Class uint8
|
||||
Level uint8
|
||||
Native uint8
|
||||
User uint8
|
||||
}
|
||||
|
||||
type KinfoVmentry struct {
|
||||
Structsize int32
|
||||
Type int32
|
||||
Start uint64
|
||||
End uint64
|
||||
Offset uint64
|
||||
Vn_fileid uint64
|
||||
Vn_fsid uint32
|
||||
Flags int32
|
||||
Resident int32
|
||||
Private_resident int32
|
||||
Protection int32
|
||||
Ref_count int32
|
||||
Shadow_count int32
|
||||
Vn_type int32
|
||||
Vn_size uint64
|
||||
Vn_rdev uint32
|
||||
Vn_mode uint16
|
||||
Status uint16
|
||||
X_kve_ispare [12]int32
|
||||
Path [1024]int8
|
||||
}
|
192
internal/gopsutil/process/process_freebsd_amd64.go
Normal file
192
internal/gopsutil/process/process_freebsd_amd64.go
Normal file
|
@ -0,0 +1,192 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
CTLKern = 1
|
||||
KernProc = 14
|
||||
KernProcPID = 1
|
||||
KernProcProc = 8
|
||||
KernProcPathname = 12
|
||||
KernProcArgs = 7
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfKinfoVmentry = 0x488
|
||||
sizeOfKinfoProc = 0x440
|
||||
)
|
||||
|
||||
const (
|
||||
SIDL = 1
|
||||
SRUN = 2
|
||||
SSLEEP = 3
|
||||
SSTOP = 4
|
||||
SZOMB = 5
|
||||
SWAIT = 6
|
||||
SLOCK = 7
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur int64
|
||||
Max int64
|
||||
}
|
||||
|
||||
type KinfoProc struct {
|
||||
Structsize int32
|
||||
Layout int32
|
||||
Args int64 /* pargs */
|
||||
Paddr int64 /* proc */
|
||||
Addr int64 /* user */
|
||||
Tracep int64 /* vnode */
|
||||
Textvp int64 /* vnode */
|
||||
Fd int64 /* filedesc */
|
||||
Vmspace int64 /* vmspace */
|
||||
Wchan int64
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Tpgid int32
|
||||
Sid int32
|
||||
Tsid int32
|
||||
Jobc int16
|
||||
Spare_short1 int16
|
||||
Tdev uint32
|
||||
Siglist [16]byte /* sigset */
|
||||
Sigmask [16]byte /* sigset */
|
||||
Sigignore [16]byte /* sigset */
|
||||
Sigcatch [16]byte /* sigset */
|
||||
Uid uint32
|
||||
Ruid uint32
|
||||
Svuid uint32
|
||||
Rgid uint32
|
||||
Svgid uint32
|
||||
Ngroups int16
|
||||
Spare_short2 int16
|
||||
Groups [16]uint32
|
||||
Size uint64
|
||||
Rssize int64
|
||||
Swrss int64
|
||||
Tsize int64
|
||||
Dsize int64
|
||||
Ssize int64
|
||||
Xstat uint16
|
||||
Acflag uint16
|
||||
Pctcpu uint32
|
||||
Estcpu uint32
|
||||
Slptime uint32
|
||||
Swtime uint32
|
||||
Cow uint32
|
||||
Runtime uint64
|
||||
Start Timeval
|
||||
Childtime Timeval
|
||||
Flag int64
|
||||
Kiflag int64
|
||||
Traceflag int32
|
||||
Stat int8
|
||||
Nice int8
|
||||
Lock int8
|
||||
Rqindex int8
|
||||
Oncpu uint8
|
||||
Lastcpu uint8
|
||||
Tdname [17]int8
|
||||
Wmesg [9]int8
|
||||
Login [18]int8
|
||||
Lockname [9]int8
|
||||
Comm [20]int8
|
||||
Emul [17]int8
|
||||
Loginclass [18]int8
|
||||
Sparestrings [50]int8
|
||||
Spareints [7]int32
|
||||
Flag2 int32
|
||||
Fibnum int32
|
||||
Cr_flags uint32
|
||||
Jid int32
|
||||
Numthreads int32
|
||||
Tid int32
|
||||
Pri Priority
|
||||
Rusage Rusage
|
||||
Rusage_ch Rusage
|
||||
Pcb int64 /* pcb */
|
||||
Kstack int64
|
||||
Udata int64
|
||||
Tdaddr int64 /* thread */
|
||||
Spareptrs [6]int64
|
||||
Sparelongs [12]int64
|
||||
Sflag int64
|
||||
Tdflags int64
|
||||
}
|
||||
|
||||
type Priority struct {
|
||||
Class uint8
|
||||
Level uint8
|
||||
Native uint8
|
||||
User uint8
|
||||
}
|
||||
|
||||
type KinfoVmentry struct {
|
||||
Structsize int32
|
||||
Type int32
|
||||
Start uint64
|
||||
End uint64
|
||||
Offset uint64
|
||||
Vn_fileid uint64
|
||||
Vn_fsid uint32
|
||||
Flags int32
|
||||
Resident int32
|
||||
Private_resident int32
|
||||
Protection int32
|
||||
Ref_count int32
|
||||
Shadow_count int32
|
||||
Vn_type int32
|
||||
Vn_size uint64
|
||||
Vn_rdev uint32
|
||||
Vn_mode uint16
|
||||
Status uint16
|
||||
X_kve_ispare [12]int32
|
||||
Path [1024]int8
|
||||
}
|
192
internal/gopsutil/process/process_freebsd_arm.go
Normal file
192
internal/gopsutil/process/process_freebsd_arm.go
Normal file
|
@ -0,0 +1,192 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
CTLKern = 1
|
||||
KernProc = 14
|
||||
KernProcPID = 1
|
||||
KernProcProc = 8
|
||||
KernProcPathname = 12
|
||||
KernProcArgs = 7
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x4
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfKinfoVmentry = 0x488
|
||||
sizeOfKinfoProc = 0x440
|
||||
)
|
||||
|
||||
const (
|
||||
SIDL = 1
|
||||
SRUN = 2
|
||||
SSLEEP = 3
|
||||
SSTOP = 4
|
||||
SZOMB = 5
|
||||
SWAIT = 6
|
||||
SLOCK = 7
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int32
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int32
|
||||
Ixrss int32
|
||||
Idrss int32
|
||||
Isrss int32
|
||||
Minflt int32
|
||||
Majflt int32
|
||||
Nswap int32
|
||||
Inblock int32
|
||||
Oublock int32
|
||||
Msgsnd int32
|
||||
Msgrcv int32
|
||||
Nsignals int32
|
||||
Nvcsw int32
|
||||
Nivcsw int32
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur int32
|
||||
Max int32
|
||||
}
|
||||
|
||||
type KinfoProc struct {
|
||||
Structsize int32
|
||||
Layout int32
|
||||
Args int32 /* pargs */
|
||||
Paddr int32 /* proc */
|
||||
Addr int32 /* user */
|
||||
Tracep int32 /* vnode */
|
||||
Textvp int32 /* vnode */
|
||||
Fd int32 /* filedesc */
|
||||
Vmspace int32 /* vmspace */
|
||||
Wchan int32
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Tpgid int32
|
||||
Sid int32
|
||||
Tsid int32
|
||||
Jobc int16
|
||||
Spare_short1 int16
|
||||
Tdev uint32
|
||||
Siglist [16]byte /* sigset */
|
||||
Sigmask [16]byte /* sigset */
|
||||
Sigignore [16]byte /* sigset */
|
||||
Sigcatch [16]byte /* sigset */
|
||||
Uid uint32
|
||||
Ruid uint32
|
||||
Svuid uint32
|
||||
Rgid uint32
|
||||
Svgid uint32
|
||||
Ngroups int16
|
||||
Spare_short2 int16
|
||||
Groups [16]uint32
|
||||
Size uint32
|
||||
Rssize int32
|
||||
Swrss int32
|
||||
Tsize int32
|
||||
Dsize int32
|
||||
Ssize int32
|
||||
Xstat uint16
|
||||
Acflag uint16
|
||||
Pctcpu uint32
|
||||
Estcpu uint32
|
||||
Slptime uint32
|
||||
Swtime uint32
|
||||
Cow uint32
|
||||
Runtime uint64
|
||||
Start Timeval
|
||||
Childtime Timeval
|
||||
Flag int32
|
||||
Kiflag int32
|
||||
Traceflag int32
|
||||
Stat int8
|
||||
Nice int8
|
||||
Lock int8
|
||||
Rqindex int8
|
||||
Oncpu uint8
|
||||
Lastcpu uint8
|
||||
Tdname [17]int8
|
||||
Wmesg [9]int8
|
||||
Login [18]int8
|
||||
Lockname [9]int8
|
||||
Comm [20]int8
|
||||
Emul [17]int8
|
||||
Loginclass [18]int8
|
||||
Sparestrings [50]int8
|
||||
Spareints [4]int32
|
||||
Flag2 int32
|
||||
Fibnum int32
|
||||
Cr_flags uint32
|
||||
Jid int32
|
||||
Numthreads int32
|
||||
Tid int32
|
||||
Pri Priority
|
||||
Rusage Rusage
|
||||
Rusage_ch Rusage
|
||||
Pcb int32 /* pcb */
|
||||
Kstack int32
|
||||
Udata int32
|
||||
Tdaddr int32 /* thread */
|
||||
Spareptrs [6]int64
|
||||
Sparelongs [12]int64
|
||||
Sflag int64
|
||||
Tdflags int64
|
||||
}
|
||||
|
||||
type Priority struct {
|
||||
Class uint8
|
||||
Level uint8
|
||||
Native uint8
|
||||
User uint8
|
||||
}
|
||||
|
||||
type KinfoVmentry struct {
|
||||
Structsize int32
|
||||
Type int32
|
||||
Start uint64
|
||||
End uint64
|
||||
Offset uint64
|
||||
Vn_fileid uint64
|
||||
Vn_fsid uint32
|
||||
Flags int32
|
||||
Resident int32
|
||||
Private_resident int32
|
||||
Protection int32
|
||||
Ref_count int32
|
||||
Shadow_count int32
|
||||
Vn_type int32
|
||||
Vn_size uint64
|
||||
Vn_rdev uint32
|
||||
Vn_mode uint16
|
||||
Status uint16
|
||||
X_kve_ispare [12]int32
|
||||
Path [1024]int8
|
||||
}
|
201
internal/gopsutil/process/process_freebsd_arm64.go
Normal file
201
internal/gopsutil/process/process_freebsd_arm64.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
//go:build freebsd && arm64
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs process/types_freebsd.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
CTLKern = 1
|
||||
KernProc = 14
|
||||
KernProcPID = 1
|
||||
KernProcProc = 8
|
||||
KernProcPathname = 12
|
||||
KernProcArgs = 7
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfKinfoVmentry = 0x488
|
||||
sizeOfKinfoProc = 0x440
|
||||
)
|
||||
|
||||
const (
|
||||
SIDL = 1
|
||||
SRUN = 2
|
||||
SSLEEP = 3
|
||||
SSTOP = 4
|
||||
SZOMB = 5
|
||||
SWAIT = 6
|
||||
SLOCK = 7
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur int64
|
||||
Max int64
|
||||
}
|
||||
|
||||
type KinfoProc struct {
|
||||
Structsize int32
|
||||
Layout int32
|
||||
Args *int64 /* pargs */
|
||||
Paddr *int64 /* proc */
|
||||
Addr *int64 /* user */
|
||||
Tracep *int64 /* vnode */
|
||||
Textvp *int64 /* vnode */
|
||||
Fd *int64 /* filedesc */
|
||||
Vmspace *int64 /* vmspace */
|
||||
Wchan *byte
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Pgid int32
|
||||
Tpgid int32
|
||||
Sid int32
|
||||
Tsid int32
|
||||
Jobc int16
|
||||
Spare_short1 int16
|
||||
Tdev_freebsd11 uint32
|
||||
Siglist [16]byte /* sigset */
|
||||
Sigmask [16]byte /* sigset */
|
||||
Sigignore [16]byte /* sigset */
|
||||
Sigcatch [16]byte /* sigset */
|
||||
Uid uint32
|
||||
Ruid uint32
|
||||
Svuid uint32
|
||||
Rgid uint32
|
||||
Svgid uint32
|
||||
Ngroups int16
|
||||
Spare_short2 int16
|
||||
Groups [16]uint32
|
||||
Size uint64
|
||||
Rssize int64
|
||||
Swrss int64
|
||||
Tsize int64
|
||||
Dsize int64
|
||||
Ssize int64
|
||||
Xstat uint16
|
||||
Acflag uint16
|
||||
Pctcpu uint32
|
||||
Estcpu uint32
|
||||
Slptime uint32
|
||||
Swtime uint32
|
||||
Cow uint32
|
||||
Runtime uint64
|
||||
Start Timeval
|
||||
Childtime Timeval
|
||||
Flag int64
|
||||
Kiflag int64
|
||||
Traceflag int32
|
||||
Stat uint8
|
||||
Nice int8
|
||||
Lock uint8
|
||||
Rqindex uint8
|
||||
Oncpu_old uint8
|
||||
Lastcpu_old uint8
|
||||
Tdname [17]uint8
|
||||
Wmesg [9]uint8
|
||||
Login [18]uint8
|
||||
Lockname [9]uint8
|
||||
Comm [20]int8
|
||||
Emul [17]uint8
|
||||
Loginclass [18]uint8
|
||||
Moretdname [4]uint8
|
||||
Sparestrings [46]uint8
|
||||
Spareints [2]int32
|
||||
Tdev uint64
|
||||
Oncpu int32
|
||||
Lastcpu int32
|
||||
Tracer int32
|
||||
Flag2 int32
|
||||
Fibnum int32
|
||||
Cr_flags uint32
|
||||
Jid int32
|
||||
Numthreads int32
|
||||
Tid int32
|
||||
Pri Priority
|
||||
Rusage Rusage
|
||||
Rusage_ch Rusage
|
||||
Pcb *int64 /* pcb */
|
||||
Kstack *byte
|
||||
Udata *byte
|
||||
Tdaddr *int64 /* thread */
|
||||
Spareptrs [6]*byte
|
||||
Sparelongs [12]int64
|
||||
Sflag int64
|
||||
Tdflags int64
|
||||
}
|
||||
|
||||
type Priority struct {
|
||||
Class uint8
|
||||
Level uint8
|
||||
Native uint8
|
||||
User uint8
|
||||
}
|
||||
|
||||
type KinfoVmentry struct {
|
||||
Structsize int32
|
||||
Type int32
|
||||
Start uint64
|
||||
End uint64
|
||||
Offset uint64
|
||||
Vn_fileid uint64
|
||||
Vn_fsid_freebsd11 uint32
|
||||
Flags int32
|
||||
Resident int32
|
||||
Private_resident int32
|
||||
Protection int32
|
||||
Ref_count int32
|
||||
Shadow_count int32
|
||||
Vn_type int32
|
||||
Vn_size uint64
|
||||
Vn_rdev_freebsd11 uint32
|
||||
Vn_mode uint16
|
||||
Status uint16
|
||||
Vn_fsid uint64
|
||||
Vn_rdev uint64
|
||||
X_kve_ispare [8]int32
|
||||
Path [1024]uint8
|
||||
}
|
1299
internal/gopsutil/process/process_linux.go
Normal file
1299
internal/gopsutil/process/process_linux.go
Normal file
File diff suppressed because it is too large
Load diff
570
internal/gopsutil/process/process_openbsd.go
Normal file
570
internal/gopsutil/process/process_openbsd.go
Normal file
|
@ -0,0 +1,570 @@
|
|||
//go:build openbsd
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"C"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
cpu "github.com/gofiber/fiber/v2/internal/gopsutil/cpu"
|
||||
mem "github.com/gofiber/fiber/v2/internal/gopsutil/mem"
|
||||
net "github.com/gofiber/fiber/v2/internal/gopsutil/net"
|
||||
)
|
||||
|
||||
// MemoryInfoExStat is different between OSes
|
||||
type MemoryInfoExStat struct{}
|
||||
|
||||
type MemoryMapsStat struct{}
|
||||
|
||||
func pidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
var ret []int32
|
||||
procs, err := Processes()
|
||||
if err != nil {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
for _, p := range procs {
|
||||
ret = append(ret, p.Pid)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) Ppid() (int32, error) {
|
||||
return p.PpidWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return k.Ppid, nil
|
||||
}
|
||||
|
||||
func (p *Process) Name() (string, error) {
|
||||
return p.NameWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NameWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := common.IntToString(k.Comm[:])
|
||||
|
||||
if len(name) >= 15 {
|
||||
cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(cmdlineSlice) > 0 {
|
||||
extendedName := filepath.Base(cmdlineSlice[0])
|
||||
if strings.HasPrefix(extendedName, p.name) {
|
||||
name = extendedName
|
||||
} else {
|
||||
name = cmdlineSlice[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (p *Process) Tgid() (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Exe() (string, error) {
|
||||
return p.ExeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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}
|
||||
buf, _, err := common.CallSyscall(mib)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argc := 0
|
||||
argvp := unsafe.Pointer(&buf[0])
|
||||
argv := *(**C.char)(unsafe.Pointer(argvp))
|
||||
size := unsafe.Sizeof(argv)
|
||||
var strParts []string
|
||||
|
||||
for argv != nil {
|
||||
strParts = append(strParts, C.GoString(argv))
|
||||
|
||||
argc++
|
||||
argv = *(**C.char)(unsafe.Pointer(uintptr(argvp) + uintptr(argc)*size))
|
||||
}
|
||||
return strParts, nil
|
||||
}
|
||||
|
||||
func (p *Process) Cmdline() (string, error) {
|
||||
return p.CmdlineWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
|
||||
argv, err := p.CmdlineSlice()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(argv, " "), nil
|
||||
}
|
||||
|
||||
func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Cwd() (string, error) {
|
||||
return p.CwdWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
|
||||
return "", common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Parent() (*Process, error) {
|
||||
return p.ParentWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
|
||||
return p, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Status() (string, error) {
|
||||
return p.StatusWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var s string
|
||||
switch k.Stat {
|
||||
case SIDL:
|
||||
case SRUN:
|
||||
case SONPROC:
|
||||
s = "R"
|
||||
case SSLEEP:
|
||||
s = "S"
|
||||
case SSTOP:
|
||||
s = "T"
|
||||
case SDEAD:
|
||||
s = "Z"
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (p *Process) Foreground() (bool, error) {
|
||||
return p.ForegroundWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
|
||||
// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
|
||||
pid := p.Pid
|
||||
ps, err := exec.LookPath("ps")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
out, err := invoke.CommandWithContext(ctx, ps, "-o", "stat=", "-p", strconv.Itoa(int(pid)))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.IndexByte(string(out), '+') != -1, nil
|
||||
}
|
||||
|
||||
func (p *Process) Uids() ([]int32, error) {
|
||||
return p.UidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uids := make([]int32, 0, 3)
|
||||
|
||||
uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid))
|
||||
|
||||
return uids, nil
|
||||
}
|
||||
|
||||
func (p *Process) Gids() ([]int32, error) {
|
||||
return p.GidsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gids := make([]int32, 0, 3)
|
||||
gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid))
|
||||
|
||||
return gids, nil
|
||||
}
|
||||
|
||||
func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return k.Groups, nil
|
||||
}
|
||||
|
||||
func (p *Process) Terminal() (string, error) {
|
||||
return p.TerminalWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ttyNr := uint64(k.Tdev)
|
||||
|
||||
termmap, err := getTerminalMap()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return termmap[ttyNr], nil
|
||||
}
|
||||
|
||||
func (p *Process) Nice() (int32, error) {
|
||||
return p.NiceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(k.Nice), nil
|
||||
}
|
||||
|
||||
func (p *Process) IOnice() (int32, error) {
|
||||
return p.IOniceWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
return rlimit, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) IOCounters() (*IOCountersStat, error) {
|
||||
return p.IOCountersWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IOCountersStat{
|
||||
ReadCount: uint64(k.Uru_inblock),
|
||||
WriteCount: uint64(k.Uru_oublock),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) {
|
||||
return p.NumCtxSwitchesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) NumFDs() (int32, error) {
|
||||
return p.NumFDsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
|
||||
return 0, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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 */
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
ret := make(map[int32]*cpu.TimesStat)
|
||||
return ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cpu.TimesStat{
|
||||
CPU: "cpu",
|
||||
User: float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000,
|
||||
System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinity() ([]int32, error) {
|
||||
return p.CPUAffinityWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfo() (*MemoryInfoStat, error) {
|
||||
return p.MemoryInfoWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
|
||||
k, err := p.getKProc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageSize, err := mem.GetPageSize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MemoryInfoStat{
|
||||
RSS: uint64(k.Vm_rssize) * pageSize,
|
||||
VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) +
|
||||
uint64(k.Vm_ssize),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) {
|
||||
return p.MemoryInfoExWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) PageFaults() (*PageFaultsStat, error) {
|
||||
return p.PageFaultsWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func (p *Process) Children() ([]*Process, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]*Process, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
np, err := NewProcess(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, np)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *Process) OpenFiles() ([]OpenFilesStat, error) {
|
||||
return p.OpenFilesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
|
||||
return nil, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) {
|
||||
return p.ConnectionsMaxWithContext(context.Background(), max)
|
||||
}
|
||||
|
||||
func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
|
||||
return []net.ConnectionStat{}, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
return &ret, common.ErrNotImplementedError
|
||||
}
|
||||
|
||||
func Processes() ([]*Process, error) {
|
||||
return ProcessesWithContext(context.Background())
|
||||
}
|
||||
|
||||
func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
|
||||
results := []*Process{}
|
||||
|
||||
buf, length, err := CallKernProcSyscall(KernProcAll, 0)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
// get kinfo_proc size
|
||||
count := int(length / uint64(sizeOfKinfoProc))
|
||||
|
||||
// parse buf to procs
|
||||
for i := 0; i < count; i++ {
|
||||
b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc]
|
||||
k, err := parseKinfoProc(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p, err := NewProcess(int32(k.Pid))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func parseKinfoProc(buf []byte) (KinfoProc, error) {
|
||||
var k KinfoProc
|
||||
br := bytes.NewReader(buf)
|
||||
err := common.Read(br, binary.LittleEndian, &k)
|
||||
return k, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length != sizeOfKinfoProc {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k, err := parseKinfoProc(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &k, nil
|
||||
}
|
||||
|
||||
func CallKernProcSyscall(op, arg int32) ([]byte, uint64, error) {
|
||||
return CallKernProcSyscallWithContext(context.Background(), op, arg)
|
||||
}
|
||||
|
||||
func CallKernProcSyscallWithContext(ctx context.Context, op, arg int32) ([]byte, uint64, error) {
|
||||
mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0}
|
||||
mibptr := unsafe.Pointer(&mib[0])
|
||||
miblen := uint64(len(mib))
|
||||
length := uint64(0)
|
||||
_, _, err := unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return nil, length, err
|
||||
}
|
||||
|
||||
count := int32(length / uint64(sizeOfKinfoProc))
|
||||
mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count}
|
||||
mibptr = unsafe.Pointer(&mib[0])
|
||||
miblen = uint64(len(mib))
|
||||
// get proc info itself
|
||||
buf := make([]byte, length)
|
||||
_, _, err = unix.Syscall6(
|
||||
unix.SYS___SYSCTL,
|
||||
uintptr(mibptr),
|
||||
uintptr(miblen),
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&length)),
|
||||
0,
|
||||
0)
|
||||
if err != 0 {
|
||||
return buf, length, err
|
||||
}
|
||||
|
||||
return buf, length, nil
|
||||
}
|
201
internal/gopsutil/process/process_openbsd_386.go
Normal file
201
internal/gopsutil/process/process_openbsd_386.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
//go:build openbsd && 386
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs process/types_openbsd.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
CTLKern = 1
|
||||
KernProc = 66
|
||||
KernProcAll = 0
|
||||
KernProcPID = 1
|
||||
KernProcProc = 8
|
||||
KernProcPathname = 12
|
||||
KernProcArgs = 55
|
||||
KernProcArgv = 1
|
||||
KernProcEnv = 3
|
||||
)
|
||||
|
||||
const (
|
||||
ArgMax = 256 * 1024
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x4
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x4
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfKinfoVmentry = 0x38
|
||||
sizeOfKinfoProc = 0x264
|
||||
)
|
||||
|
||||
const (
|
||||
SIDL = 1
|
||||
SRUN = 2
|
||||
SSLEEP = 3
|
||||
SSTOP = 4
|
||||
SZOMB = 5
|
||||
SDEAD = 6
|
||||
SONPROC = 7
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int32
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int32
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int32
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int32
|
||||
Ixrss int32
|
||||
Idrss int32
|
||||
Isrss int32
|
||||
Minflt int32
|
||||
Majflt int32
|
||||
Nswap int32
|
||||
Inblock int32
|
||||
Oublock int32
|
||||
Msgsnd int32
|
||||
Msgrcv int32
|
||||
Nsignals int32
|
||||
Nvcsw int32
|
||||
Nivcsw int32
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type KinfoProc struct {
|
||||
Forw uint64
|
||||
Back uint64
|
||||
Paddr uint64
|
||||
Addr uint64
|
||||
Fd uint64
|
||||
Stats uint64
|
||||
Limit uint64
|
||||
Vmspace uint64
|
||||
Sigacts uint64
|
||||
Sess uint64
|
||||
Tsess uint64
|
||||
Ru uint64
|
||||
Eflag int32
|
||||
Exitsig int32
|
||||
Flag int32
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Sid int32
|
||||
X_pgid int32
|
||||
Tpgid int32
|
||||
Uid uint32
|
||||
Ruid uint32
|
||||
Gid uint32
|
||||
Rgid uint32
|
||||
Groups [16]uint32
|
||||
Ngroups int16
|
||||
Jobc int16
|
||||
Tdev uint32
|
||||
Estcpu uint32
|
||||
Rtime_sec uint32
|
||||
Rtime_usec uint32
|
||||
Cpticks int32
|
||||
Pctcpu uint32
|
||||
Swtime uint32
|
||||
Slptime uint32
|
||||
Schedflags int32
|
||||
Uticks uint64
|
||||
Sticks uint64
|
||||
Iticks uint64
|
||||
Tracep uint64
|
||||
Traceflag int32
|
||||
Holdcnt int32
|
||||
Siglist int32
|
||||
Sigmask uint32
|
||||
Sigignore uint32
|
||||
Sigcatch uint32
|
||||
Stat int8
|
||||
Priority uint8
|
||||
Usrpri uint8
|
||||
Nice uint8
|
||||
Xstat uint16
|
||||
Acflag uint16
|
||||
Comm [24]int8
|
||||
Wmesg [8]int8
|
||||
Wchan uint64
|
||||
Login [32]int8
|
||||
Vm_rssize int32
|
||||
Vm_tsize int32
|
||||
Vm_dsize int32
|
||||
Vm_ssize int32
|
||||
Uvalid int64
|
||||
Ustart_sec uint64
|
||||
Ustart_usec uint32
|
||||
Uutime_sec uint32
|
||||
Uutime_usec uint32
|
||||
Ustime_sec uint32
|
||||
Ustime_usec uint32
|
||||
Uru_maxrss uint64
|
||||
Uru_ixrss uint64
|
||||
Uru_idrss uint64
|
||||
Uru_isrss uint64
|
||||
Uru_minflt uint64
|
||||
Uru_majflt uint64
|
||||
Uru_nswap uint64
|
||||
Uru_inblock uint64
|
||||
Uru_oublock uint64
|
||||
Uru_msgsnd uint64
|
||||
Uru_msgrcv uint64
|
||||
Uru_nsignals uint64
|
||||
Uru_nvcsw uint64
|
||||
Uru_nivcsw uint64
|
||||
Uctime_sec uint32
|
||||
Uctime_usec uint32
|
||||
Psflags int32
|
||||
Spare int32
|
||||
Svuid uint32
|
||||
Svgid uint32
|
||||
Emul [8]int8
|
||||
Rlim_rss_cur uint64
|
||||
Cpuid uint64
|
||||
Vm_map_size uint64
|
||||
Tid int32
|
||||
Rtableid uint32
|
||||
}
|
||||
|
||||
type Priority struct{}
|
||||
|
||||
type KinfoVmentry struct {
|
||||
Start uint32
|
||||
End uint32
|
||||
Guard uint32
|
||||
Fspace uint32
|
||||
Fspace_augment uint32
|
||||
Offset uint64
|
||||
Wired_count int32
|
||||
Etype int32
|
||||
Protection int32
|
||||
Max_protection int32
|
||||
Advice int32
|
||||
Inheritance int32
|
||||
Flags uint8
|
||||
Pad_cgo_0 [3]byte
|
||||
}
|
200
internal/gopsutil/process/process_openbsd_amd64.go
Normal file
200
internal/gopsutil/process/process_openbsd_amd64.go
Normal file
|
@ -0,0 +1,200 @@
|
|||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_openbsd.go
|
||||
|
||||
package process
|
||||
|
||||
const (
|
||||
CTLKern = 1
|
||||
KernProc = 66
|
||||
KernProcAll = 0
|
||||
KernProcPID = 1
|
||||
KernProcProc = 8
|
||||
KernProcPathname = 12
|
||||
KernProcArgs = 55
|
||||
KernProcArgv = 1
|
||||
KernProcEnv = 3
|
||||
)
|
||||
|
||||
const (
|
||||
ArgMax = 256 * 1024
|
||||
)
|
||||
|
||||
const (
|
||||
sizeofPtr = 0x8
|
||||
sizeofShort = 0x2
|
||||
sizeofInt = 0x4
|
||||
sizeofLong = 0x8
|
||||
sizeofLongLong = 0x8
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfKinfoVmentry = 0x50
|
||||
sizeOfKinfoProc = 0x268
|
||||
)
|
||||
|
||||
const (
|
||||
SIDL = 1
|
||||
SRUN = 2
|
||||
SSLEEP = 3
|
||||
SSTOP = 4
|
||||
SZOMB = 5
|
||||
SDEAD = 6
|
||||
SONPROC = 7
|
||||
)
|
||||
|
||||
type (
|
||||
_C_short int16
|
||||
_C_int int32
|
||||
_C_long int64
|
||||
_C_long_long int64
|
||||
)
|
||||
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
type Rusage struct {
|
||||
Utime Timeval
|
||||
Stime Timeval
|
||||
Maxrss int64
|
||||
Ixrss int64
|
||||
Idrss int64
|
||||
Isrss int64
|
||||
Minflt int64
|
||||
Majflt int64
|
||||
Nswap int64
|
||||
Inblock int64
|
||||
Oublock int64
|
||||
Msgsnd int64
|
||||
Msgrcv int64
|
||||
Nsignals int64
|
||||
Nvcsw int64
|
||||
Nivcsw int64
|
||||
}
|
||||
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
type KinfoProc struct {
|
||||
Forw uint64
|
||||
Back uint64
|
||||
Paddr uint64
|
||||
Addr uint64
|
||||
Fd uint64
|
||||
Stats uint64
|
||||
Limit uint64
|
||||
Vmspace uint64
|
||||
Sigacts uint64
|
||||
Sess uint64
|
||||
Tsess uint64
|
||||
Ru uint64
|
||||
Eflag int32
|
||||
Exitsig int32
|
||||
Flag int32
|
||||
Pid int32
|
||||
Ppid int32
|
||||
Sid int32
|
||||
X_pgid int32
|
||||
Tpgid int32
|
||||
Uid uint32
|
||||
Ruid uint32
|
||||
Gid uint32
|
||||
Rgid uint32
|
||||
Groups [16]uint32
|
||||
Ngroups int16
|
||||
Jobc int16
|
||||
Tdev uint32
|
||||
Estcpu uint32
|
||||
Rtime_sec uint32
|
||||
Rtime_usec uint32
|
||||
Cpticks int32
|
||||
Pctcpu uint32
|
||||
Swtime uint32
|
||||
Slptime uint32
|
||||
Schedflags int32
|
||||
Uticks uint64
|
||||
Sticks uint64
|
||||
Iticks uint64
|
||||
Tracep uint64
|
||||
Traceflag int32
|
||||
Holdcnt int32
|
||||
Siglist int32
|
||||
Sigmask uint32
|
||||
Sigignore uint32
|
||||
Sigcatch uint32
|
||||
Stat int8
|
||||
Priority uint8
|
||||
Usrpri uint8
|
||||
Nice uint8
|
||||
Xstat uint16
|
||||
Acflag uint16
|
||||
Comm [24]int8
|
||||
Wmesg [8]int8
|
||||
Wchan uint64
|
||||
Login [32]int8
|
||||
Vm_rssize int32
|
||||
Vm_tsize int32
|
||||
Vm_dsize int32
|
||||
Vm_ssize int32
|
||||
Uvalid int64
|
||||
Ustart_sec uint64
|
||||
Ustart_usec uint32
|
||||
Uutime_sec uint32
|
||||
Uutime_usec uint32
|
||||
Ustime_sec uint32
|
||||
Ustime_usec uint32
|
||||
Pad_cgo_0 [4]byte
|
||||
Uru_maxrss uint64
|
||||
Uru_ixrss uint64
|
||||
Uru_idrss uint64
|
||||
Uru_isrss uint64
|
||||
Uru_minflt uint64
|
||||
Uru_majflt uint64
|
||||
Uru_nswap uint64
|
||||
Uru_inblock uint64
|
||||
Uru_oublock uint64
|
||||
Uru_msgsnd uint64
|
||||
Uru_msgrcv uint64
|
||||
Uru_nsignals uint64
|
||||
Uru_nvcsw uint64
|
||||
Uru_nivcsw uint64
|
||||
Uctime_sec uint32
|
||||
Uctime_usec uint32
|
||||
Psflags int32
|
||||
Spare int32
|
||||
Svuid uint32
|
||||
Svgid uint32
|
||||
Emul [8]int8
|
||||
Rlim_rss_cur uint64
|
||||
Cpuid uint64
|
||||
Vm_map_size uint64
|
||||
Tid int32
|
||||
Rtableid uint32
|
||||
}
|
||||
|
||||
type Priority struct{}
|
||||
|
||||
type KinfoVmentry struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
Guard uint64
|
||||
Fspace uint64
|
||||
Fspace_augment uint64
|
||||
Offset uint64
|
||||
Wired_count int32
|
||||
Etype int32
|
||||
Protection int32
|
||||
Max_protection int32
|
||||
Advice int32
|
||||
Inheritance int32
|
||||
Flags uint8
|
||||
Pad_cgo_0 [7]byte
|
||||
}
|
194
internal/gopsutil/process/process_posix.go
Normal file
194
internal/gopsutil/process/process_posix.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
//go:build linux || freebsd || openbsd || darwin
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
// POSIX
|
||||
func getTerminalMap() (map[uint64]string, error) {
|
||||
ret := make(map[uint64]string)
|
||||
var termfiles []string
|
||||
|
||||
d, err := os.Open("/dev")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
devnames, err := d.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, devname := range devnames {
|
||||
if strings.HasPrefix(devname, "/dev/tty") {
|
||||
termfiles = append(termfiles, "/dev/tty/"+devname)
|
||||
}
|
||||
}
|
||||
|
||||
var ptsnames []string
|
||||
ptsd, err := os.Open("/dev/pts")
|
||||
if err != nil {
|
||||
ptsnames, _ = filepath.Glob("/dev/ttyp*")
|
||||
if ptsnames == nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer ptsd.Close()
|
||||
|
||||
if ptsnames == nil {
|
||||
defer ptsd.Close()
|
||||
ptsnames, err = ptsd.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ptsname := range ptsnames {
|
||||
termfiles = append(termfiles, "/dev/pts/"+ptsname)
|
||||
}
|
||||
} else {
|
||||
termfiles = ptsnames
|
||||
}
|
||||
|
||||
for _, name := range termfiles {
|
||||
stat := unix.Stat_t{}
|
||||
if err = unix.Stat(name, &stat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rdev := uint64(stat.Rdev)
|
||||
ret[rdev] = strings.Replace(name, "/dev", "", -1)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) {
|
||||
if pid <= 0 {
|
||||
return false, fmt.Errorf("invalid pid %v", pid)
|
||||
}
|
||||
proc, err := os.FindProcess(int(pid))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(common.HostProc()); err == nil { // Means that proc filesystem exist
|
||||
// Checking PID existence based on existence of /<HOST_PROC>/proc/<PID> folder
|
||||
// This covers the case when running inside container with a different process namespace (by default)
|
||||
|
||||
_, err := os.Stat(common.HostProc(strconv.Itoa(int(pid))))
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
//'/proc' filesystem is not exist, checking of PID existence is done via signalling the process
|
||||
//Make sense only if we run in the same process namespace
|
||||
err = proc.Signal(syscall.Signal(0))
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if err.Error() == "os: process already finished" {
|
||||
return false, nil
|
||||
}
|
||||
errno, ok := err.(syscall.Errno)
|
||||
if !ok {
|
||||
return false, err
|
||||
}
|
||||
switch errno {
|
||||
case syscall.ESRCH:
|
||||
return false, nil
|
||||
case syscall.EPERM:
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// SendSignal sends a unix.Signal to the process.
|
||||
// Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported.
|
||||
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 {
|
||||
process, err := os.FindProcess(int(p.Pid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = process.Signal(sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Suspend sends SIGSTOP to the process.
|
||||
func (p *Process) Suspend() error {
|
||||
return p.SuspendWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) SuspendWithContext(ctx context.Context) error {
|
||||
return p.SendSignal(unix.SIGSTOP)
|
||||
}
|
||||
|
||||
// Resume sends SIGCONT to the process.
|
||||
func (p *Process) Resume() error {
|
||||
return p.ResumeWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) ResumeWithContext(ctx context.Context) error {
|
||||
return p.SendSignal(unix.SIGCONT)
|
||||
}
|
||||
|
||||
// Terminate sends SIGTERM to the process.
|
||||
func (p *Process) Terminate() error {
|
||||
return p.TerminateWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) TerminateWithContext(ctx context.Context) error {
|
||||
return p.SendSignal(unix.SIGTERM)
|
||||
}
|
||||
|
||||
// Kill sends SIGKILL to the process.
|
||||
func (p *Process) Kill() error {
|
||||
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.
|
||||
func (p *Process) Username() (string, error) {
|
||||
return p.UsernameWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
|
||||
uids, err := p.Uids()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(uids) > 0 {
|
||||
u, err := user.LookupId(strconv.Itoa(int(uids[0])))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.Username, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
1024
internal/gopsutil/process/process_windows.go
Normal file
1024
internal/gopsutil/process/process_windows.go
Normal file
File diff suppressed because it is too large
Load diff
102
internal/gopsutil/process/process_windows_386.go
Normal file
102
internal/gopsutil/process/process_windows_386.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
//go:build windows
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
type PROCESS_MEMORY_COUNTERS struct {
|
||||
CB uint32
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize uint32
|
||||
WorkingSetSize uint32
|
||||
QuotaPeakPagedPoolUsage uint32
|
||||
QuotaPagedPoolUsage uint32
|
||||
QuotaPeakNonPagedPoolUsage uint32
|
||||
QuotaNonPagedPoolUsage uint32
|
||||
PagefileUsage uint32
|
||||
PeakPagefileUsage uint32
|
||||
}
|
||||
|
||||
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
|
||||
if is32BitProcess {
|
||||
// we are on a 32-bit process reading an external 32-bit process
|
||||
var info processBasicInformation32
|
||||
|
||||
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
|
||||
uintptr(procHandle),
|
||||
uintptr(common.ProcessBasicInformation),
|
||||
uintptr(unsafe.Pointer(&info)),
|
||||
uintptr(unsafe.Sizeof(info)),
|
||||
uintptr(0),
|
||||
)
|
||||
if int(ret) >= 0 {
|
||||
return uint64(info.PebBaseAddress)
|
||||
}
|
||||
} else {
|
||||
// we are on a 32-bit process reading an external 64-bit process
|
||||
if common.ProcNtWow64QueryInformationProcess64.Find() == nil { // avoid panic
|
||||
var info processBasicInformation64
|
||||
|
||||
ret, _, _ := common.ProcNtWow64QueryInformationProcess64.Call(
|
||||
uintptr(procHandle),
|
||||
uintptr(common.ProcessBasicInformation),
|
||||
uintptr(unsafe.Pointer(&info)),
|
||||
uintptr(unsafe.Sizeof(info)),
|
||||
uintptr(0),
|
||||
)
|
||||
if int(ret) >= 0 {
|
||||
return info.PebBaseAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return 0 on error
|
||||
return 0
|
||||
}
|
||||
|
||||
func readProcessMemory(h syscall.Handle, is32BitProcess bool, address uint64, size uint) []byte {
|
||||
if is32BitProcess {
|
||||
var read uint
|
||||
|
||||
buffer := make([]byte, size)
|
||||
|
||||
ret, _, _ := common.ProcNtReadVirtualMemory.Call(
|
||||
uintptr(h),
|
||||
uintptr(address),
|
||||
uintptr(unsafe.Pointer(&buffer[0])),
|
||||
uintptr(size),
|
||||
uintptr(unsafe.Pointer(&read)),
|
||||
)
|
||||
if int(ret) >= 0 && read > 0 {
|
||||
return buffer[:read]
|
||||
}
|
||||
} else {
|
||||
// reading a 64-bit process from a 32-bit one
|
||||
if common.ProcNtWow64ReadVirtualMemory64.Find() == nil { // avoid panic
|
||||
var read uint64
|
||||
|
||||
buffer := make([]byte, size)
|
||||
|
||||
ret, _, _ := common.ProcNtWow64ReadVirtualMemory64.Call(
|
||||
uintptr(h),
|
||||
uintptr(address&0xFFFFFFFF), // the call expects a 64-bit value
|
||||
uintptr(address>>32),
|
||||
uintptr(unsafe.Pointer(&buffer[0])),
|
||||
uintptr(size), // the call expects a 64-bit value
|
||||
uintptr(0), // but size is 32-bit so pass zero as the high dword
|
||||
uintptr(unsafe.Pointer(&read)),
|
||||
)
|
||||
if int(ret) >= 0 && read > 0 {
|
||||
return buffer[:uint(read)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we reach here, an error happened
|
||||
return nil
|
||||
}
|
76
internal/gopsutil/process/process_windows_amd64.go
Normal file
76
internal/gopsutil/process/process_windows_amd64.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
//go:build windows
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/gopsutil/common"
|
||||
)
|
||||
|
||||
type PROCESS_MEMORY_COUNTERS struct {
|
||||
CB uint32
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize uint64
|
||||
WorkingSetSize uint64
|
||||
QuotaPeakPagedPoolUsage uint64
|
||||
QuotaPagedPoolUsage uint64
|
||||
QuotaPeakNonPagedPoolUsage uint64
|
||||
QuotaNonPagedPoolUsage uint64
|
||||
PagefileUsage uint64
|
||||
PeakPagefileUsage uint64
|
||||
}
|
||||
|
||||
func queryPebAddress(procHandle syscall.Handle, is32BitProcess bool) uint64 {
|
||||
if is32BitProcess {
|
||||
// we are on a 64-bit process reading an external 32-bit process
|
||||
var wow64 uint
|
||||
|
||||
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
|
||||
uintptr(procHandle),
|
||||
uintptr(common.ProcessWow64Information),
|
||||
uintptr(unsafe.Pointer(&wow64)),
|
||||
uintptr(unsafe.Sizeof(wow64)),
|
||||
uintptr(0),
|
||||
)
|
||||
if int(ret) >= 0 {
|
||||
return uint64(wow64)
|
||||
}
|
||||
} else {
|
||||
// we are on a 64-bit process reading an external 64-bit process
|
||||
var info processBasicInformation64
|
||||
|
||||
ret, _, _ := common.ProcNtQueryInformationProcess.Call(
|
||||
uintptr(procHandle),
|
||||
uintptr(common.ProcessBasicInformation),
|
||||
uintptr(unsafe.Pointer(&info)),
|
||||
uintptr(unsafe.Sizeof(info)),
|
||||
uintptr(0),
|
||||
)
|
||||
if int(ret) >= 0 {
|
||||
return info.PebBaseAddress
|
||||
}
|
||||
}
|
||||
|
||||
// return 0 on error
|
||||
return 0
|
||||
}
|
||||
|
||||
func readProcessMemory(procHandle syscall.Handle, _ bool, address uint64, size uint) []byte {
|
||||
var read uint
|
||||
|
||||
buffer := make([]byte, size)
|
||||
|
||||
ret, _, _ := common.ProcNtReadVirtualMemory.Call(
|
||||
uintptr(procHandle),
|
||||
uintptr(address),
|
||||
uintptr(unsafe.Pointer(&buffer[0])),
|
||||
uintptr(size),
|
||||
uintptr(unsafe.Pointer(&read)),
|
||||
)
|
||||
if int(ret) >= 0 && read > 0 {
|
||||
return buffer[:read]
|
||||
}
|
||||
return nil
|
||||
}
|
163
internal/gopsutil/process/types_darwin.go
Normal file
163
internal/gopsutil/process/types_darwin.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
// 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
|
||||
|
||||
//go: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 <stdint.h>
|
||||
#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
|
||||
AuMask C.struct_au_mask
|
||||
AuTidAddr C.struct_au_tid_addr
|
||||
)
|
||||
|
||||
// TAILQ(ucred)
|
||||
type UcredQueue C.struct_ucred_queue
|
95
internal/gopsutil/process/types_freebsd.go
Normal file
95
internal/gopsutil/process/types_freebsd.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
//go: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
|
103
internal/gopsutil/process/types_openbsd.go
Normal file
103
internal/gopsutil/process/types_openbsd.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
//go: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
|
Loading…
Add table
Add a link
Reference in a new issue