1
0
Fork 0

Adding upstream version 2.52.6.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-17 06:50:16 +02:00
parent a960158181
commit 6d002e9543
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
441 changed files with 95392 additions and 0 deletions

61
internal/gopsutil/LICENSE Normal file
View 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

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

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

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

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

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

View file

@ -0,0 +1,7 @@
//go:build freebsd
package load
func getForkStat() (forkstat, error) {
return forkstat{}, nil
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View 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

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

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

View 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")
}

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

View 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")
}

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

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

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

View 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()
}

View 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())
}

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

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

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

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

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

View 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")
}

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

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

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

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

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

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

File diff suppressed because it is too large Load diff

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

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

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

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

File diff suppressed because it is too large Load diff

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

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

View 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

View 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

View 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