Upgrade dependencies

This commit is contained in:
Ingo Oppermann
2023-06-19 13:02:22 +02:00
parent 0a5661f7ab
commit 98e4dec13a
53 changed files with 508 additions and 258 deletions

6
go.mod
View File

@@ -8,9 +8,9 @@ require (
github.com/adhocore/gronx v1.6.3 github.com/adhocore/gronx v1.6.3
github.com/atrox/haikunatorgo/v2 v2.0.1 github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.18.0 github.com/caddyserver/certmagic v0.18.0
github.com/casbin/casbin/v2 v2.71.0 github.com/casbin/casbin/v2 v2.71.1
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230614141756-a25a5fc3c60e github.com/datarhei/core-client-go/v16 v16.11.1-0.20230614141756-a25a5fc3c60e
github.com/datarhei/gosrt v0.5.0 github.com/datarhei/gosrt v0.5.2
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
github.com/fujiwara/shapeio v1.0.0 github.com/fujiwara/shapeio v1.0.0
github.com/go-playground/validator/v10 v10.14.1 github.com/go-playground/validator/v10 v10.14.1
@@ -95,7 +95,7 @@ require (
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect github.com/prometheus/procfs v0.11.0 // indirect
github.com/rs/xid v1.5.0 // indirect github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect

12
go.sum
View File

@@ -36,8 +36,8 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/caddyserver/certmagic v0.18.0 h1:L22mJES1WllfLoHUcQUy4wVO7UfOsoL5wtg/Bj7kmIw= github.com/caddyserver/certmagic v0.18.0 h1:L22mJES1WllfLoHUcQUy4wVO7UfOsoL5wtg/Bj7kmIw=
github.com/caddyserver/certmagic v0.18.0/go.mod h1:e0YLTnXIopZ05bBWCLzpIf1Yvk27Q90FGUmGowFRDY8= github.com/caddyserver/certmagic v0.18.0/go.mod h1:e0YLTnXIopZ05bBWCLzpIf1Yvk27Q90FGUmGowFRDY8=
github.com/casbin/casbin/v2 v2.71.0 h1:pVzHKXkGgOXIjksEwnrOjNu5CE4xy6aAVzdR8td2gSc= github.com/casbin/casbin/v2 v2.71.1 h1:LRHyqM0S1LzM/K59PmfUIN0ZJfLgcOjL4OhOQI/FNXU=
github.com/casbin/casbin/v2 v2.71.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/casbin/casbin/v2 v2.71.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -48,8 +48,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230614141756-a25a5fc3c60e h1:iQKqGTyIdCyO7kY/G5MCKhzt3xZ5YPRubbJskVp5EvQ= github.com/datarhei/core-client-go/v16 v16.11.1-0.20230614141756-a25a5fc3c60e h1:iQKqGTyIdCyO7kY/G5MCKhzt3xZ5YPRubbJskVp5EvQ=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230614141756-a25a5fc3c60e/go.mod h1:6L0zr/NUwvaPsCTK/IL17m8JUEtgLp3BDtlsBREwacg= github.com/datarhei/core-client-go/v16 v16.11.1-0.20230614141756-a25a5fc3c60e/go.mod h1:6L0zr/NUwvaPsCTK/IL17m8JUEtgLp3BDtlsBREwacg=
github.com/datarhei/gosrt v0.5.0 h1:MhM8kb00nbWc+haNKU7ZdYgSm9pLdxdtas7tcERh8j8= github.com/datarhei/gosrt v0.5.2 h1:eagqZwEIiGPNJW0rLep3gwceObyaZ17+iKRc+l4VEpc=
github.com/datarhei/gosrt v0.5.0/go.mod h1:bcLf0p0FeZl+QY87b+Q8nGkyjyX6IDvI9y9raol8vng= github.com/datarhei/gosrt v0.5.2/go.mod h1:0308GQhAu5hxe2KYdbss901aKceSSKXnwCr8Vs++eiw=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo= github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw= github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -264,8 +264,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=

View File

@@ -420,13 +420,14 @@ func (s *server) handleSubscribe(conn srt.Conn) {
config := srt.DefaultConfig() config := srt.DefaultConfig()
config.StreamId = streamId config.StreamId = streamId
config.Latency = 200 * time.Millisecond // This might be a value obtained from the cluster config.Latency = 200 * time.Millisecond // This might be a value obtained from the cluster
host, port, err := config.UnmarshalURL(peerurl) address, err := config.UnmarshalURL(peerurl)
peerurl = config.MarshalURL(address)
if err != nil { if err != nil {
s.logger.Error().WithField("address", peerurl).WithError(err).Log("Parsing proxy address failed") s.logger.Error().WithField("address", peerurl).WithError(err).Log("Parsing proxy address failed")
s.log(identity, "PLAY", "NOTFOUND", si.Resource, "no publisher for this resource found", client) s.log(identity, "PLAY", "NOTFOUND", si.Resource, "no publisher for this resource found", client)
return return
} }
src, err := srt.Dial("srt", host+":"+port, config) src, err := srt.Dial("srt", address, config)
if err != nil { if err != nil {
s.logger.Error().WithField("address", peerurl).WithError(err).Log("Proxying address failed") s.logger.Error().WithField("address", peerurl).WithError(err).Log("Proxying address failed")
s.log(identity, "PLAY", "NOTFOUND", si.Resource, "no publisher for this resource found", client) s.log(identity, "PLAY", "NOTFOUND", si.Resource, "no publisher for this resource found", client)

View File

@@ -62,6 +62,10 @@ type EnforceContext struct {
MType string MType string
} }
func (e EnforceContext) GetCacheKey() string {
return "EnforceContext{" + e.RType + "-" + e.PType + "-" + e.EType + "-" + e.MType + "}"
}
// NewEnforcer creates an enforcer via file or DB. // NewEnforcer creates an enforcer via file or DB.
// //
// File: // File:

View File

@@ -217,18 +217,19 @@ func DefaultConfig() Config {
} }
// UnmarshalURL takes a SRT URL and parses out the configuration. A SRT URL is // UnmarshalURL takes a SRT URL and parses out the configuration. A SRT URL is
// srt://[host]:[port]?[key1]=[value1]&[key2]=[value2]... // srt://[host]:[port]?[key1]=[value1]&[key2]=[value2]... It returns the host:port
func (c *Config) UnmarshalURL(addr string) (string, string, error) { // of the URL.
u, err := url.Parse(addr) func (c *Config) UnmarshalURL(srturl string) (string, error) {
u, err := url.Parse(srturl)
if err != nil { if err != nil {
return "", "", err return "", err
} }
if u.Scheme != "srt" { if u.Scheme != "srt" {
return "", "", fmt.Errorf("the URL doesn't seem to be an srt:// URL") return "", fmt.Errorf("the URL doesn't seem to be an srt:// URL")
} }
return u.Hostname(), u.Port(), c.UnmarshalQuery(u.RawQuery) return u.Host, c.UnmarshalQuery(u.RawQuery)
} }
// UnmarshalQuery parses a query string and interprets it as a configuration // UnmarshalQuery parses a query string and interprets it as a configuration
@@ -471,9 +472,9 @@ func (c *Config) UnmarshalQuery(query string) error {
return nil return nil
} }
// MarshalURL returns the SRT URL for this config and the given host and port. // MarshalURL returns the SRT URL for this config and the given address (host:port).
func (c *Config) MarshalURL(host string, port uint) string { func (c *Config) MarshalURL(address string) string {
return "srt://" + host + ":" + strconv.FormatUint(uint64(port), 10) + "?" + c.MarshalQuery() return "srt://" + address + "?" + c.MarshalQuery()
} }
// MarshalQuery returns the corresponding query string for a configuration. // MarshalQuery returns the corresponding query string for a configuration.

View File

@@ -10,7 +10,6 @@ import (
"net" "net"
"os" "os"
"sync" "sync"
"syscall"
"time" "time"
"github.com/datarhei/gosrt/internal/circular" "github.com/datarhei/gosrt/internal/circular"
@@ -89,35 +88,18 @@ func Dial(network, address string, config Config) (Conn, error) {
config: config, config: config,
} }
raddr, err := net.ResolveUDPAddr("udp", address) netdialer := net.Dialer{
if err != nil { Control: DialControl(config),
return nil, fmt.Errorf("unable to resolve address: %w", err)
} }
pc, err := net.DialUDP("udp", nil, raddr) conn, err := netdialer.Dial("udp", address)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed dialing: %w", err) return nil, fmt.Errorf("failed dialing: %w", err)
} }
file, err := pc.File() pc, ok := conn.(*net.UDPConn)
if err != nil { if !ok {
return nil, err return nil, fmt.Errorf("failed dialing: connection is not a UDP connection")
}
// Set TOS
if config.IPTOS > 0 {
err = syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_TOS, config.IPTOS)
if err != nil {
return nil, fmt.Errorf("failed setting socket option TOS: %w", err)
}
}
// Set TTL
if config.IPTTL > 0 {
err = syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_TTL, config.IPTTL)
if err != nil {
return nil, fmt.Errorf("failed setting socket option TTL: %w", err)
}
} }
dl.pc = pc dl.pc = pc

View File

@@ -8,7 +8,6 @@ import (
"net" "net"
"os" "os"
"sync" "sync"
"syscall"
"time" "time"
"github.com/datarhei/gosrt/internal/crypto" "github.com/datarhei/gosrt/internal/crypto"
@@ -191,36 +190,7 @@ func Listen(network, address string, config Config) (Listener, error) {
} }
lc := net.ListenConfig{ lc := net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error { Control: ListenControl(config),
var opErr error
err := c.Control(func(fd uintptr) {
// Set REUSEADDR
opErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
if opErr != nil {
return
}
// Set TOS
if config.IPTOS > 0 {
opErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, config.IPTOS)
if opErr != nil {
return
}
}
// Set TTL
if config.IPTTL > 0 {
opErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, config.IPTTL)
if opErr != nil {
return
}
}
})
if err != nil {
return err
}
return opErr
},
} }
lp, err := lc.ListenPacket(context.Background(), "udp", address) lp, err := lc.ListenPacket(context.Background(), "udp", address)

65
vendor/github.com/datarhei/gosrt/net.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
//go:build !windows
package srt
import "syscall"
func ListenControl(config Config) func(network, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
var opErr error
err := c.Control(func(fd uintptr) {
// Set REUSEADDR
opErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
if opErr != nil {
return
}
// Set TOS
if config.IPTOS > 0 {
opErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, config.IPTOS)
if opErr != nil {
return
}
}
// Set TTL
if config.IPTTL > 0 {
opErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, config.IPTTL)
if opErr != nil {
return
}
}
})
if err != nil {
return err
}
return opErr
}
}
func DialControl(config Config) func(network string, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
var opErr error
err := c.Control(func(fd uintptr) {
// Set TOS
if config.IPTOS > 0 {
opErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, config.IPTOS)
if opErr != nil {
return
}
}
// Set TTL
if config.IPTTL > 0 {
opErr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, config.IPTTL)
if opErr != nil {
return
}
}
})
if err != nil {
return err
}
return opErr
}
}

69
vendor/github.com/datarhei/gosrt/net_windows.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
//go:build windows
package srt
import (
"syscall"
"golang.org/x/sys/windows"
)
func ListenControl(config Config) func(network, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
var opErr error
err := c.Control(func(fd uintptr) {
// Set REUSEADDR
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)
if opErr != nil {
return
}
// Set TOS
if config.IPTOS > 0 {
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_TOS, config.IPTOS)
if opErr != nil {
return
}
}
// Set TTL
if config.IPTTL > 0 {
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_TTL, config.IPTTL)
if opErr != nil {
return
}
}
})
if err != nil {
return err
}
return opErr
}
}
func DialControl(config Config) func(network string, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
var opErr error
err := c.Control(func(fd uintptr) {
// Set TOS
if config.IPTOS > 0 {
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_TOS, config.IPTOS)
if opErr != nil {
return
}
}
// Set TTL
if config.IPTTL > 0 {
opErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_TTL, config.IPTTL)
if opErr != nil {
return
}
}
})
if err != nil {
return err
}
return opErr
}
}

View File

@@ -2,6 +2,7 @@
linters: linters:
enable: enable:
- godot - godot
- misspell
- revive - revive
linter-settings: linter-settings:
@@ -10,3 +11,5 @@ linter-settings:
exclude: exclude:
# Ignore "See: URL" # Ignore "See: URL"
- 'See:' - 'See:'
misspell:
locale: US

View File

@@ -51,11 +51,11 @@ ensure the `fixtures` directory is up to date by removing the existing directory
extracting the ttar file using `make fixtures/.unpacked` or just `make test`. extracting the ttar file using `make fixtures/.unpacked` or just `make test`.
```bash ```bash
rm -rf fixtures rm -rf testdata/fixtures
make test make test
``` ```
Next, make the required changes to the extracted files in the `fixtures` directory. When Next, make the required changes to the extracted files in the `fixtures` directory. When
the changes are complete, run `make update_fixtures` to create a new `fixtures.ttar` file the changes are complete, run `make update_fixtures` to create a new `fixtures.ttar` file
based on the updated `fixtures` directory. And finally, verify the changes using based on the updated `fixtures` directory. And finally, verify the changes using
`git diff fixtures.ttar`. `git diff testdata/fixtures.ttar`.

View File

@@ -55,7 +55,7 @@ type ARPEntry struct {
func (fs FS) GatherARPEntries() ([]ARPEntry, error) { func (fs FS) GatherARPEntries() ([]ARPEntry, error) {
data, err := os.ReadFile(fs.proc.Path("net/arp")) data, err := os.ReadFile(fs.proc.Path("net/arp"))
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading arp %q: %w", fs.proc.Path("net/arp"), err) return nil, fmt.Errorf("%s: error reading arp %s: %w", ErrFileRead, fs.proc.Path("net/arp"), err)
} }
return parseARPEntries(data) return parseARPEntries(data)
@@ -78,11 +78,11 @@ func parseARPEntries(data []byte) ([]ARPEntry, error) {
} else if width == expectedDataWidth { } else if width == expectedDataWidth {
entry, err := parseARPEntry(columns) entry, err := parseARPEntry(columns)
if err != nil { if err != nil {
return []ARPEntry{}, fmt.Errorf("failed to parse ARP entry: %w", err) return []ARPEntry{}, fmt.Errorf("%s: Failed to parse ARP entry: %v: %w", ErrFileParse, entry, err)
} }
entries = append(entries, entry) entries = append(entries, entry)
} else { } else {
return []ARPEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedDataWidth) return []ARPEntry{}, fmt.Errorf("%s: %d columns found, but expected %d: %w", ErrFileParse, width, expectedDataWidth, err)
} }
} }

View File

@@ -55,7 +55,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) {
parts := strings.Fields(line) parts := strings.Fields(line)
if len(parts) < 4 { if len(parts) < 4 {
return nil, fmt.Errorf("invalid number of fields when parsing buddyinfo") return nil, fmt.Errorf("%w: Invalid number of fields, found: %v", ErrFileParse, parts)
} }
node := strings.TrimRight(parts[1], ",") node := strings.TrimRight(parts[1], ",")
@@ -66,7 +66,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) {
bucketCount = arraySize bucketCount = arraySize
} else { } else {
if bucketCount != arraySize { if bucketCount != arraySize {
return nil, fmt.Errorf("mismatch in number of buddyinfo buckets, previous count %d, new count %d", bucketCount, arraySize) return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize)
} }
} }
@@ -74,7 +74,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) {
for i := 0; i < arraySize; i++ { for i := 0; i < arraySize; i++ {
sizes[i], err = strconv.ParseFloat(parts[i+4], 64) sizes[i], err = strconv.ParseFloat(parts[i+4], 64)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid value in buddyinfo: %w", err) return nil, fmt.Errorf("%s: Invalid valid in buddyinfo: %f: %w", ErrFileParse, sizes[i], err)
} }
} }

View File

@@ -79,7 +79,7 @@ func parseCPUInfoX86(info []byte) ([]CPUInfo, error) {
// find the first "processor" line // find the first "processor" line
firstLine := firstNonEmptyLine(scanner) firstLine := firstNonEmptyLine(scanner)
if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") { if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, firstLine)
} }
field := strings.SplitN(firstLine, ": ", 2) field := strings.SplitN(firstLine, ": ", 2)
v, err := strconv.ParseUint(field[1], 0, 32) v, err := strconv.ParseUint(field[1], 0, 32)
@@ -192,9 +192,10 @@ func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
scanner := bufio.NewScanner(bytes.NewReader(info)) scanner := bufio.NewScanner(bytes.NewReader(info))
firstLine := firstNonEmptyLine(scanner) firstLine := firstNonEmptyLine(scanner)
match, _ := regexp.MatchString("^[Pp]rocessor", firstLine) match, err := regexp.MatchString("^[Pp]rocessor", firstLine)
if !match || !strings.Contains(firstLine, ":") { if !match || !strings.Contains(firstLine, ":") {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%s: Cannot parse line: %q: %w", ErrFileParse, firstLine, err)
} }
field := strings.SplitN(firstLine, ": ", 2) field := strings.SplitN(firstLine, ": ", 2)
cpuinfo := []CPUInfo{} cpuinfo := []CPUInfo{}
@@ -258,7 +259,7 @@ func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) {
firstLine := firstNonEmptyLine(scanner) firstLine := firstNonEmptyLine(scanner)
if !strings.HasPrefix(firstLine, "vendor_id") || !strings.Contains(firstLine, ":") { if !strings.HasPrefix(firstLine, "vendor_id") || !strings.Contains(firstLine, ":") {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, firstLine)
} }
field := strings.SplitN(firstLine, ": ", 2) field := strings.SplitN(firstLine, ": ", 2)
cpuinfo := []CPUInfo{} cpuinfo := []CPUInfo{}
@@ -283,7 +284,7 @@ func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) {
if strings.HasPrefix(line, "processor") { if strings.HasPrefix(line, "processor") {
match := cpuinfoS390XProcessorRegexp.FindStringSubmatch(line) match := cpuinfoS390XProcessorRegexp.FindStringSubmatch(line)
if len(match) < 2 { if len(match) < 2 {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
} }
cpu := commonCPUInfo cpu := commonCPUInfo
v, err := strconv.ParseUint(match[1], 0, 32) v, err := strconv.ParseUint(match[1], 0, 32)
@@ -343,7 +344,7 @@ func parseCPUInfoMips(info []byte) ([]CPUInfo, error) {
// find the first "processor" line // find the first "processor" line
firstLine := firstNonEmptyLine(scanner) firstLine := firstNonEmptyLine(scanner)
if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") { if !strings.HasPrefix(firstLine, "system type") || !strings.Contains(firstLine, ":") {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
} }
field := strings.SplitN(firstLine, ": ", 2) field := strings.SplitN(firstLine, ": ", 2)
cpuinfo := []CPUInfo{} cpuinfo := []CPUInfo{}
@@ -421,7 +422,7 @@ func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) {
firstLine := firstNonEmptyLine(scanner) firstLine := firstNonEmptyLine(scanner)
if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") { if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
} }
field := strings.SplitN(firstLine, ": ", 2) field := strings.SplitN(firstLine, ": ", 2)
v, err := strconv.ParseUint(field[1], 0, 32) v, err := strconv.ParseUint(field[1], 0, 32)
@@ -466,7 +467,7 @@ func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) {
firstLine := firstNonEmptyLine(scanner) firstLine := firstNonEmptyLine(scanner)
if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") { if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
return nil, fmt.Errorf("invalid cpuinfo file: %q", firstLine) return nil, fmt.Errorf("%w: %q", ErrFileParse, firstLine)
} }
field := strings.SplitN(firstLine, ": ", 2) field := strings.SplitN(firstLine, ": ", 2)
v, err := strconv.ParseUint(field[1], 0, 32) v, err := strconv.ParseUint(field[1], 0, 32)

View File

@@ -55,12 +55,13 @@ func (fs FS) Crypto() ([]Crypto, error) {
path := fs.proc.Path("crypto") path := fs.proc.Path("crypto")
b, err := util.ReadFileNoStat(path) b, err := util.ReadFileNoStat(path)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading crypto %q: %w", path, err) return nil, fmt.Errorf("%s: Cannot read file %v: %w", ErrFileRead, b, err)
} }
crypto, err := parseCrypto(bytes.NewReader(b)) crypto, err := parseCrypto(bytes.NewReader(b))
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing crypto %q: %w", path, err) return nil, fmt.Errorf("%s: Cannot parse %v: %w", ErrFileParse, crypto, err)
} }
return crypto, nil return crypto, nil
@@ -83,7 +84,7 @@ func parseCrypto(r io.Reader) ([]Crypto, error) {
kv := strings.Split(text, ":") kv := strings.Split(text, ":")
if len(kv) != 2 { if len(kv) != 2 {
return nil, fmt.Errorf("malformed crypto line: %q", text) return nil, fmt.Errorf("%w: Cannot parae line: %q", ErrFileParse, text)
} }
k := strings.TrimSpace(kv[0]) k := strings.TrimSpace(kv[0])

View File

@@ -236,7 +236,7 @@ func (fs FS) Fscacheinfo() (Fscacheinfo, error) {
m, err := parseFscacheinfo(bytes.NewReader(b)) m, err := parseFscacheinfo(bytes.NewReader(b))
if err != nil { if err != nil {
return Fscacheinfo{}, fmt.Errorf("failed to parse Fscacheinfo: %w", err) return Fscacheinfo{}, fmt.Errorf("%s: Cannot parse %v: %w", ErrFileParse, m, err)
} }
return *m, nil return *m, nil
@@ -245,7 +245,7 @@ func (fs FS) Fscacheinfo() (Fscacheinfo, error) {
func setFSCacheFields(fields []string, setFields ...*uint64) error { func setFSCacheFields(fields []string, setFields ...*uint64) error {
var err error var err error
if len(fields) < len(setFields) { if len(fields) < len(setFields) {
return fmt.Errorf("Insufficient number of fields, expected %v, got %v", len(setFields), len(fields)) return fmt.Errorf("%s: Expected %d, but got %d: %w", ErrFileParse, len(setFields), len(fields), err)
} }
for i := range setFields { for i := range setFields {
@@ -263,7 +263,7 @@ func parseFscacheinfo(r io.Reader) (*Fscacheinfo, error) {
for s.Scan() { for s.Scan() {
fields := strings.Fields(s.Text()) fields := strings.Fields(s.Text())
if len(fields) < 2 { if len(fields) < 2 {
return nil, fmt.Errorf("malformed Fscacheinfo line: %q", s.Text()) return nil, fmt.Errorf("%w: malformed Fscacheinfo line: %q", ErrFileParse, s.Text())
} }
switch fields[0] { switch fields[0] {

View File

@@ -221,15 +221,16 @@ func parseIPPort(s string) (net.IP, uint16, error) {
case 46: case 46:
ip = net.ParseIP(s[1:40]) ip = net.ParseIP(s[1:40])
if ip == nil { if ip == nil {
return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40]) return nil, 0, fmt.Errorf("%s: Invalid IPv6 addr %s: %w", ErrFileParse, s[1:40], err)
} }
default: default:
return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s) return nil, 0, fmt.Errorf("%s: Unexpected IP:Port %s: %w", ErrFileParse, s, err)
} }
portString := s[len(s)-4:] portString := s[len(s)-4:]
if len(portString) != 4 { if len(portString) != 4 {
return nil, 0, fmt.Errorf("unexpected port string format: %s", portString) return nil, 0,
fmt.Errorf("%s: Unexpected port string format %s: %w", ErrFileParse, portString, err)
} }
port, err := strconv.ParseUint(portString, 16, 16) port, err := strconv.ParseUint(portString, 16, 16)
if err != nil { if err != nil {

View File

@@ -44,14 +44,14 @@ func parseLoad(loadavgBytes []byte) (*LoadAvg, error) {
loads := make([]float64, 3) loads := make([]float64, 3)
parts := strings.Fields(string(loadavgBytes)) parts := strings.Fields(string(loadavgBytes))
if len(parts) < 3 { if len(parts) < 3 {
return nil, fmt.Errorf("malformed loadavg line: too few fields in loadavg string: %q", string(loadavgBytes)) return nil, fmt.Errorf("%w: Malformed line %q", ErrFileParse, string(loadavgBytes))
} }
var err error var err error
for i, load := range parts[0:3] { for i, load := range parts[0:3] {
loads[i], err = strconv.ParseFloat(load, 64) loads[i], err = strconv.ParseFloat(load, 64)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not parse load %q: %w", load, err) return nil, fmt.Errorf("%s: Cannot parse load: %f: %w", ErrFileParse, loads[i], err)
} }
} }
return &LoadAvg{ return &LoadAvg{

View File

@@ -70,7 +70,7 @@ func (fs FS) MDStat() ([]MDStat, error) {
} }
mdstat, err := parseMDStat(data) mdstat, err := parseMDStat(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing mdstat %q: %w", fs.proc.Path("mdstat"), err) return nil, fmt.Errorf("%s: Cannot parse %v: %w", ErrFileParse, fs.proc.Path("mdstat"), err)
} }
return mdstat, nil return mdstat, nil
} }
@@ -90,13 +90,13 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
deviceFields := strings.Fields(line) deviceFields := strings.Fields(line)
if len(deviceFields) < 3 { if len(deviceFields) < 3 {
return nil, fmt.Errorf("not enough fields in mdline (expected at least 3): %s", line) return nil, fmt.Errorf("%s: Expected 3+ lines, got %q", ErrFileParse, line)
} }
mdName := deviceFields[0] // mdx mdName := deviceFields[0] // mdx
state := deviceFields[2] // active or inactive state := deviceFields[2] // active or inactive
if len(lines) <= i+3 { if len(lines) <= i+3 {
return nil, fmt.Errorf("error parsing %q: too few lines for md device", mdName) return nil, fmt.Errorf("%w: Too few lines for md device: %q", ErrFileParse, mdName)
} }
// Failed disks have the suffix (F) & Spare disks have the suffix (S). // Failed disks have the suffix (F) & Spare disks have the suffix (S).
@@ -105,7 +105,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
active, total, down, size, err := evalStatusLine(lines[i], lines[i+1]) active, total, down, size, err := evalStatusLine(lines[i], lines[i+1])
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing md device lines: %w", err) return nil, fmt.Errorf("%s: Cannot parse md device lines: %v: %w", ErrFileParse, active, err)
} }
syncLineIdx := i + 2 syncLineIdx := i + 2
@@ -140,7 +140,7 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
} else { } else {
syncedBlocks, pct, finish, speed, err = evalRecoveryLine(lines[syncLineIdx]) syncedBlocks, pct, finish, speed, err = evalRecoveryLine(lines[syncLineIdx])
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing sync line in md device %q: %w", mdName, err) return nil, fmt.Errorf("%s: Cannot parse sync line in md device: %q: %w", ErrFileParse, mdName, err)
} }
} }
} }
@@ -168,13 +168,13 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) { func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) {
statusFields := strings.Fields(statusLine) statusFields := strings.Fields(statusLine)
if len(statusFields) < 1 { if len(statusFields) < 1 {
return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q", statusLine) return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected statusline %q: %w", ErrFileParse, statusLine, err)
} }
sizeStr := statusFields[0] sizeStr := statusFields[0]
size, err = strconv.ParseInt(sizeStr, 10, 64) size, err = strconv.ParseInt(sizeStr, 10, 64)
if err != nil { if err != nil {
return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected statusline %q: %w", ErrFileParse, statusLine, err)
} }
if strings.Contains(deviceLine, "raid0") || strings.Contains(deviceLine, "linear") { if strings.Contains(deviceLine, "raid0") || strings.Contains(deviceLine, "linear") {
@@ -189,17 +189,17 @@ func evalStatusLine(deviceLine, statusLine string) (active, total, down, size in
matches := statusLineRE.FindStringSubmatch(statusLine) matches := statusLineRE.FindStringSubmatch(statusLine)
if len(matches) != 5 { if len(matches) != 5 {
return 0, 0, 0, 0, fmt.Errorf("couldn't find all the substring matches: %s", statusLine) return 0, 0, 0, 0, fmt.Errorf("%s: Could not fild all substring matches %s: %w", ErrFileParse, statusLine, err)
} }
total, err = strconv.ParseInt(matches[2], 10, 64) total, err = strconv.ParseInt(matches[2], 10, 64)
if err != nil { if err != nil {
return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected statusline %q: %w", ErrFileParse, statusLine, err)
} }
active, err = strconv.ParseInt(matches[3], 10, 64) active, err = strconv.ParseInt(matches[3], 10, 64)
if err != nil { if err != nil {
return 0, 0, 0, 0, fmt.Errorf("unexpected statusLine %q: %w", statusLine, err) return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected active %d: %w", ErrFileParse, active, err)
} }
down = int64(strings.Count(matches[4], "_")) down = int64(strings.Count(matches[4], "_"))
@@ -209,42 +209,42 @@ func evalStatusLine(deviceLine, statusLine string) (active, total, down, size in
func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, pct float64, finish float64, speed float64, err error) { func evalRecoveryLine(recoveryLine string) (syncedBlocks int64, pct float64, finish float64, speed float64, err error) {
matches := recoveryLineBlocksRE.FindStringSubmatch(recoveryLine) matches := recoveryLineBlocksRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 { if len(matches) != 2 {
return 0, 0, 0, 0, fmt.Errorf("unexpected recoveryLine: %s", recoveryLine) return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected recoveryLine %s: %w", ErrFileParse, recoveryLine, err)
} }
syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64) syncedBlocks, err = strconv.ParseInt(matches[1], 10, 64)
if err != nil { if err != nil {
return 0, 0, 0, 0, fmt.Errorf("error parsing int from recoveryLine %q: %w", recoveryLine, err) return 0, 0, 0, 0, fmt.Errorf("%s: Unexpected parsing of recoveryLine %q: %w", ErrFileParse, recoveryLine, err)
} }
// Get percentage complete // Get percentage complete
matches = recoveryLinePctRE.FindStringSubmatch(recoveryLine) matches = recoveryLinePctRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 { if len(matches) != 2 {
return syncedBlocks, 0, 0, 0, fmt.Errorf("unexpected recoveryLine matching percentage: %s", recoveryLine) return syncedBlocks, 0, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching percentage %s", ErrFileParse, recoveryLine)
} }
pct, err = strconv.ParseFloat(strings.TrimSpace(matches[1]), 64) pct, err = strconv.ParseFloat(strings.TrimSpace(matches[1]), 64)
if err != nil { if err != nil {
return syncedBlocks, 0, 0, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) return syncedBlocks, 0, 0, 0, fmt.Errorf("%w: Error parsing float from recoveryLine %q", ErrFileParse, recoveryLine)
} }
// Get time expected left to complete // Get time expected left to complete
matches = recoveryLineFinishRE.FindStringSubmatch(recoveryLine) matches = recoveryLineFinishRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 { if len(matches) != 2 {
return syncedBlocks, pct, 0, 0, fmt.Errorf("unexpected recoveryLine matching est. finish time: %s", recoveryLine) return syncedBlocks, pct, 0, 0, fmt.Errorf("%w: Unexpected recoveryLine matching est. finish time: %s", ErrFileParse, recoveryLine)
} }
finish, err = strconv.ParseFloat(matches[1], 64) finish, err = strconv.ParseFloat(matches[1], 64)
if err != nil { if err != nil {
return syncedBlocks, pct, 0, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) return syncedBlocks, pct, 0, 0, fmt.Errorf("%w: Unable to parse float from recoveryLine: %q", ErrFileParse, recoveryLine)
} }
// Get recovery speed // Get recovery speed
matches = recoveryLineSpeedRE.FindStringSubmatch(recoveryLine) matches = recoveryLineSpeedRE.FindStringSubmatch(recoveryLine)
if len(matches) != 2 { if len(matches) != 2 {
return syncedBlocks, pct, finish, 0, fmt.Errorf("unexpected recoveryLine matching speed: %s", recoveryLine) return syncedBlocks, pct, finish, 0, fmt.Errorf("%w: Unexpected recoveryLine value: %s", ErrFileParse, recoveryLine)
} }
speed, err = strconv.ParseFloat(matches[1], 64) speed, err = strconv.ParseFloat(matches[1], 64)
if err != nil { if err != nil {
return syncedBlocks, pct, finish, 0, fmt.Errorf("error parsing float from recoveryLine %q: %w", recoveryLine, err) return syncedBlocks, pct, finish, 0, fmt.Errorf("%s: Error parsing float from recoveryLine: %q: %w", ErrFileParse, recoveryLine, err)
} }
return syncedBlocks, pct, finish, speed, nil return syncedBlocks, pct, finish, speed, nil

View File

@@ -152,7 +152,7 @@ func (fs FS) Meminfo() (Meminfo, error) {
m, err := parseMemInfo(bytes.NewReader(b)) m, err := parseMemInfo(bytes.NewReader(b))
if err != nil { if err != nil {
return Meminfo{}, fmt.Errorf("failed to parse meminfo: %w", err) return Meminfo{}, fmt.Errorf("%s: %w", ErrFileParse, err)
} }
return *m, nil return *m, nil
@@ -165,7 +165,7 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) {
// Each line has at least a name and value; we ignore the unit. // Each line has at least a name and value; we ignore the unit.
fields := strings.Fields(s.Text()) fields := strings.Fields(s.Text())
if len(fields) < 2 { if len(fields) < 2 {
return nil, fmt.Errorf("malformed meminfo line: %q", s.Text()) return nil, fmt.Errorf("%w: Malformed line %q", ErrFileParse, s.Text())
} }
v, err := strconv.ParseUint(fields[1], 0, 64) v, err := strconv.ParseUint(fields[1], 0, 64)

View File

@@ -78,11 +78,11 @@ func parseMountInfoString(mountString string) (*MountInfo, error) {
mountInfo := strings.Split(mountString, " ") mountInfo := strings.Split(mountString, " ")
mountInfoLength := len(mountInfo) mountInfoLength := len(mountInfo)
if mountInfoLength < 10 { if mountInfoLength < 10 {
return nil, fmt.Errorf("couldn't find enough fields in mount string: %s", mountString) return nil, fmt.Errorf("%w: Too few fields in mount string: %s", ErrFileParse, mountString)
} }
if mountInfo[mountInfoLength-4] != "-" { if mountInfo[mountInfoLength-4] != "-" {
return nil, fmt.Errorf("couldn't find separator in expected field: %s", mountInfo[mountInfoLength-4]) return nil, fmt.Errorf("%w: couldn't find separator in expected field: %s", ErrFileParse, mountInfo[mountInfoLength-4])
} }
mount := &MountInfo{ mount := &MountInfo{
@@ -98,18 +98,18 @@ func parseMountInfoString(mountString string) (*MountInfo, error) {
mount.MountID, err = strconv.Atoi(mountInfo[0]) mount.MountID, err = strconv.Atoi(mountInfo[0])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse mount ID") return nil, fmt.Errorf("%w: mount ID: %q", ErrFileParse, mount.MountID)
} }
mount.ParentID, err = strconv.Atoi(mountInfo[1]) mount.ParentID, err = strconv.Atoi(mountInfo[1])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse parent ID") return nil, fmt.Errorf("%w: parent ID: %q", ErrFileParse, mount.ParentID)
} }
// Has optional fields, which is a space separated list of values. // Has optional fields, which is a space separated list of values.
// Example: shared:2 master:7 // Example: shared:2 master:7
if mountInfo[6] != "" { if mountInfo[6] != "" {
mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4]) mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4])
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("%s: %w", ErrFileParse, err)
} }
} }
return mount, nil return mount, nil

View File

@@ -266,7 +266,7 @@ func parseMountStats(r io.Reader) ([]*Mount, error) {
if len(ss) > deviceEntryLen { if len(ss) > deviceEntryLen {
// Only NFSv3 and v4 are supported for parsing statistics // Only NFSv3 and v4 are supported for parsing statistics
if m.Type != nfs3Type && m.Type != nfs4Type { if m.Type != nfs3Type && m.Type != nfs4Type {
return nil, fmt.Errorf("cannot parse MountStats for fstype %q", m.Type) return nil, fmt.Errorf("%w: Cannot parse MountStats for %q", ErrFileParse, m.Type)
} }
statVersion := strings.TrimPrefix(ss[8], statVersionPrefix) statVersion := strings.TrimPrefix(ss[8], statVersionPrefix)
@@ -290,7 +290,7 @@ func parseMountStats(r io.Reader) ([]*Mount, error) {
// device [device] mounted on [mount] with fstype [type] // device [device] mounted on [mount] with fstype [type]
func parseMount(ss []string) (*Mount, error) { func parseMount(ss []string) (*Mount, error) {
if len(ss) < deviceEntryLen { if len(ss) < deviceEntryLen {
return nil, fmt.Errorf("invalid device entry: %v", ss) return nil, fmt.Errorf("%w: Invalid device %q", ErrFileParse, ss)
} }
// Check for specific words appearing at specific indices to ensure // Check for specific words appearing at specific indices to ensure
@@ -308,7 +308,7 @@ func parseMount(ss []string) (*Mount, error) {
for _, f := range format { for _, f := range format {
if ss[f.i] != f.s { if ss[f.i] != f.s {
return nil, fmt.Errorf("invalid device entry: %v", ss) return nil, fmt.Errorf("%w: Invalid device %q", ErrFileParse, ss)
} }
} }
@@ -345,7 +345,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
switch ss[0] { switch ss[0] {
case fieldOpts: case fieldOpts:
if len(ss) < 2 { if len(ss) < 2 {
return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) return nil, fmt.Errorf("%w: Incomplete information for NFS stats: %v", ErrFileParse, ss)
} }
if stats.Opts == nil { if stats.Opts == nil {
stats.Opts = map[string]string{} stats.Opts = map[string]string{}
@@ -360,7 +360,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
} }
case fieldAge: case fieldAge:
if len(ss) < 2 { if len(ss) < 2 {
return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) return nil, fmt.Errorf("%w: Incomplete information for NFS stats: %v", ErrFileParse, ss)
} }
// Age integer is in seconds // Age integer is in seconds
d, err := time.ParseDuration(ss[1] + "s") d, err := time.ParseDuration(ss[1] + "s")
@@ -371,7 +371,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
stats.Age = d stats.Age = d
case fieldBytes: case fieldBytes:
if len(ss) < 2 { if len(ss) < 2 {
return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) return nil, fmt.Errorf("%w: Incomplete information for NFS stats: %v", ErrFileParse, ss)
} }
bstats, err := parseNFSBytesStats(ss[1:]) bstats, err := parseNFSBytesStats(ss[1:])
if err != nil { if err != nil {
@@ -381,7 +381,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
stats.Bytes = *bstats stats.Bytes = *bstats
case fieldEvents: case fieldEvents:
if len(ss) < 2 { if len(ss) < 2 {
return nil, fmt.Errorf("not enough information for NFS stats: %v", ss) return nil, fmt.Errorf("%w: Incomplete information for NFS events: %v", ErrFileParse, ss)
} }
estats, err := parseNFSEventsStats(ss[1:]) estats, err := parseNFSEventsStats(ss[1:])
if err != nil { if err != nil {
@@ -391,7 +391,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
stats.Events = *estats stats.Events = *estats
case fieldTransport: case fieldTransport:
if len(ss) < 3 { if len(ss) < 3 {
return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss) return nil, fmt.Errorf("%w: Incomplete information for NFS transport stats: %v", ErrFileParse, ss)
} }
tstats, err := parseNFSTransportStats(ss[1:], statVersion) tstats, err := parseNFSTransportStats(ss[1:], statVersion)
@@ -430,7 +430,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
// integer fields. // integer fields.
func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) { func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) {
if len(ss) != fieldBytesLen { if len(ss) != fieldBytesLen {
return nil, fmt.Errorf("invalid NFS bytes stats: %v", ss) return nil, fmt.Errorf("%w: Invalid NFS bytes stats: %v", ErrFileParse, ss)
} }
ns := make([]uint64, 0, fieldBytesLen) ns := make([]uint64, 0, fieldBytesLen)
@@ -459,7 +459,7 @@ func parseNFSBytesStats(ss []string) (*NFSBytesStats, error) {
// integer fields. // integer fields.
func parseNFSEventsStats(ss []string) (*NFSEventsStats, error) { func parseNFSEventsStats(ss []string) (*NFSEventsStats, error) {
if len(ss) != fieldEventsLen { if len(ss) != fieldEventsLen {
return nil, fmt.Errorf("invalid NFS events stats: %v", ss) return nil, fmt.Errorf("%w: invalid NFS events stats: %v", ErrFileParse, ss)
} }
ns := make([]uint64, 0, fieldEventsLen) ns := make([]uint64, 0, fieldEventsLen)
@@ -523,7 +523,7 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
} }
if len(ss) < minFields { if len(ss) < minFields {
return nil, fmt.Errorf("invalid NFS per-operations stats: %v", ss) return nil, fmt.Errorf("%w: invalid NFS per-operations stats: %v", ErrFileParse, ss)
} }
// Skip string operation name for integers // Skip string operation name for integers
@@ -576,10 +576,10 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats
} else if protocol == "udp" { } else if protocol == "udp" {
expectedLength = fieldTransport10UDPLen expectedLength = fieldTransport10UDPLen
} else { } else {
return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.0 statement: %v", protocol, ss) return nil, fmt.Errorf("%w: Invalid NFS protocol \"%s\" in stats 1.0 statement: %v", ErrFileParse, protocol, ss)
} }
if len(ss) != expectedLength { if len(ss) != expectedLength {
return nil, fmt.Errorf("invalid NFS transport stats 1.0 statement: %v", ss) return nil, fmt.Errorf("%w: Invalid NFS transport stats 1.0 statement: %v", ErrFileParse, ss)
} }
case statVersion11: case statVersion11:
var expectedLength int var expectedLength int
@@ -588,13 +588,13 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats
} else if protocol == "udp" { } else if protocol == "udp" {
expectedLength = fieldTransport11UDPLen expectedLength = fieldTransport11UDPLen
} else { } else {
return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.1 statement: %v", protocol, ss) return nil, fmt.Errorf("%w: invalid NFS protocol \"%s\" in stats 1.1 statement: %v", ErrFileParse, protocol, ss)
} }
if len(ss) != expectedLength { if len(ss) != expectedLength {
return nil, fmt.Errorf("invalid NFS transport stats 1.1 statement: %v", ss) return nil, fmt.Errorf("%w: invalid NFS transport stats 1.1 statement: %v", ErrFileParse, ss)
} }
default: default:
return nil, fmt.Errorf("unrecognized NFS transport stats version: %q", statVersion) return nil, fmt.Errorf("%s: Unrecognized NFS transport stats version: %q", ErrFileParse, statVersion)
} }
// Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay // Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay

View File

@@ -58,7 +58,7 @@ func readConntrackStat(path string) ([]ConntrackStatEntry, error) {
stat, err := parseConntrackStat(bytes.NewReader(b)) stat, err := parseConntrackStat(bytes.NewReader(b))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read conntrack stats from %q: %w", path, err) return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, path, err)
} }
return stat, nil return stat, nil
@@ -86,11 +86,12 @@ func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) {
func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) { func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) {
entries, err := util.ParseHexUint64s(fields) entries, err := util.ParseHexUint64s(fields)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid conntrackstat entry, couldn't parse fields: %s", err) return nil, fmt.Errorf("%s: Cannot parse entry: %d: %w", ErrFileParse, entries, err)
} }
numEntries := len(entries) numEntries := len(entries)
if numEntries < 16 || numEntries > 17 { if numEntries < 16 || numEntries > 17 {
return nil, fmt.Errorf("invalid conntrackstat entry, invalid number of fields: %d", numEntries) return nil,
fmt.Errorf("%w: invalid conntrackstat entry, invalid number of fields: %d", ErrFileParse, numEntries)
} }
stats := &ConntrackStatEntry{ stats := &ConntrackStatEntry{

View File

@@ -130,7 +130,7 @@ func parseIP(hexIP string) (net.IP, error) {
var byteIP []byte var byteIP []byte
byteIP, err := hex.DecodeString(hexIP) byteIP, err := hex.DecodeString(hexIP)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot parse address field in socket line %q", hexIP) return nil, fmt.Errorf("%s: Cannot parse socket field in %q: %w", ErrFileParse, hexIP, err)
} }
switch len(byteIP) { switch len(byteIP) {
case 4: case 4:
@@ -144,7 +144,7 @@ func parseIP(hexIP string) (net.IP, error) {
} }
return i, nil return i, nil
default: default:
return nil, fmt.Errorf("Unable to parse IP %s", hexIP) return nil, fmt.Errorf("%s: Unable to parse IP %s: %w", ErrFileParse, hexIP, nil)
} }
} }
@@ -153,7 +153,8 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) {
line := &netIPSocketLine{} line := &netIPSocketLine{}
if len(fields) < 10 { if len(fields) < 10 {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"cannot parse net socket line as it has less then 10 columns %q", "%w: Less than 10 columns found %q",
ErrFileParse,
strings.Join(fields, " "), strings.Join(fields, " "),
) )
} }
@@ -162,64 +163,65 @@ func parseNetIPSocketLine(fields []string) (*netIPSocketLine, error) {
// sl // sl
s := strings.Split(fields[0], ":") s := strings.Split(fields[0], ":")
if len(s) != 2 { if len(s) != 2 {
return nil, fmt.Errorf("cannot parse sl field in socket line %q", fields[0]) return nil, fmt.Errorf("%w: Unable to parse sl field in line %q", ErrFileParse, fields[0])
} }
if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil { if line.Sl, err = strconv.ParseUint(s[0], 0, 64); err != nil {
return nil, fmt.Errorf("cannot parse sl value in socket line: %w", err) return nil, fmt.Errorf("%s: Unable to parse sl field in %q: %w", ErrFileParse, line.Sl, err)
} }
// local_address // local_address
l := strings.Split(fields[1], ":") l := strings.Split(fields[1], ":")
if len(l) != 2 { if len(l) != 2 {
return nil, fmt.Errorf("cannot parse local_address field in socket line %q", fields[1]) return nil, fmt.Errorf("%w: Unable to parse local_address field in %q", ErrFileParse, fields[1])
} }
if line.LocalAddr, err = parseIP(l[0]); err != nil { if line.LocalAddr, err = parseIP(l[0]); err != nil {
return nil, err return nil, err
} }
if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil { if line.LocalPort, err = strconv.ParseUint(l[1], 16, 64); err != nil {
return nil, fmt.Errorf("cannot parse local_address port value in socket line: %w", err) return nil, fmt.Errorf("%s: Unable to parse local_address port value line %q: %w", ErrFileParse, line.LocalPort, err)
} }
// remote_address // remote_address
r := strings.Split(fields[2], ":") r := strings.Split(fields[2], ":")
if len(r) != 2 { if len(r) != 2 {
return nil, fmt.Errorf("cannot parse rem_address field in socket line %q", fields[1]) return nil, fmt.Errorf("%w: Unable to parse rem_address field in %q", ErrFileParse, fields[1])
} }
if line.RemAddr, err = parseIP(r[0]); err != nil { if line.RemAddr, err = parseIP(r[0]); err != nil {
return nil, err return nil, err
} }
if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil { if line.RemPort, err = strconv.ParseUint(r[1], 16, 64); err != nil {
return nil, fmt.Errorf("cannot parse rem_address port value in socket line: %w", err) return nil, fmt.Errorf("%s: Cannot parse rem_address port value in %q: %w", ErrFileParse, line.RemPort, err)
} }
// st // st
if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil { if line.St, err = strconv.ParseUint(fields[3], 16, 64); err != nil {
return nil, fmt.Errorf("cannot parse st value in socket line: %w", err) return nil, fmt.Errorf("%s: Cannot parse st value in %q: %w", ErrFileParse, line.St, err)
} }
// tx_queue and rx_queue // tx_queue and rx_queue
q := strings.Split(fields[4], ":") q := strings.Split(fields[4], ":")
if len(q) != 2 { if len(q) != 2 {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"cannot parse tx/rx queues in socket line as it has a missing colon %q", "%w: Missing colon for tx/rx queues in socket line %q",
ErrFileParse,
fields[4], fields[4],
) )
} }
if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil { if line.TxQueue, err = strconv.ParseUint(q[0], 16, 64); err != nil {
return nil, fmt.Errorf("cannot parse tx_queue value in socket line: %w", err) return nil, fmt.Errorf("%s: Cannot parse tx_queue value in %q: %w", ErrFileParse, line.TxQueue, err)
} }
if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil { if line.RxQueue, err = strconv.ParseUint(q[1], 16, 64); err != nil {
return nil, fmt.Errorf("cannot parse rx_queue value in socket line: %w", err) return nil, fmt.Errorf("%s: Cannot parse trx_queue value in %q: %w", ErrFileParse, line.RxQueue, err)
} }
// uid // uid
if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil { if line.UID, err = strconv.ParseUint(fields[7], 0, 64); err != nil {
return nil, fmt.Errorf("cannot parse uid value in socket line: %w", err) return nil, fmt.Errorf("%s: Cannot parse UID value in %q: %w", ErrFileParse, line.UID, err)
} }
// inode // inode
if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil { if line.Inode, err = strconv.ParseUint(fields[9], 0, 64); err != nil {
return nil, fmt.Errorf("cannot parse inode value in socket line: %w", err) return nil, fmt.Errorf("%s: Cannot parse inode value in %q: %w", ErrFileParse, line.Inode, err)
} }
return line, nil return line, nil

View File

@@ -131,7 +131,7 @@ func (ps NetProtocolStats) parseLine(rawLine string) (*NetProtocolStatLine, erro
} else if fields[6] == disabled { } else if fields[6] == disabled {
line.Slab = false line.Slab = false
} else { } else {
return nil, fmt.Errorf("unable to parse capability for protocol: %s", line.Name) return nil, fmt.Errorf("%w: capability for protocol: %s", ErrFileParse, line.Name)
} }
line.ModuleName = fields[7] line.ModuleName = fields[7]
@@ -173,7 +173,7 @@ func (pc *NetProtocolCapabilities) parseCapabilities(capabilities []string) erro
} else if capabilities[i] == "n" { } else if capabilities[i] == "n" {
*capabilityFields[i] = false *capabilityFields[i] = false
} else { } else {
return fmt.Errorf("unable to parse capability block for protocol: position %d", i) return fmt.Errorf("%w: capability block for protocol: position %d", ErrFileParse, i)
} }
} }
return nil return nil

143
vendor/github.com/prometheus/procfs/net_route.go generated vendored Normal file
View File

@@ -0,0 +1,143 @@
// Copyright 2023 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package procfs
import (
"bufio"
"bytes"
"fmt"
"io"
"strconv"
"strings"
"github.com/prometheus/procfs/internal/util"
)
const (
blackholeRepresentation string = "*"
blackholeIfaceName string = "blackhole"
routeLineColumns int = 11
)
// A NetRouteLine represents one line from net/route.
type NetRouteLine struct {
Iface string
Destination uint32
Gateway uint32
Flags uint32
RefCnt uint32
Use uint32
Metric uint32
Mask uint32
MTU uint32
Window uint32
IRTT uint32
}
func (fs FS) NetRoute() ([]NetRouteLine, error) {
return readNetRoute(fs.proc.Path("net", "route"))
}
func readNetRoute(path string) ([]NetRouteLine, error) {
b, err := util.ReadFileNoStat(path)
if err != nil {
return nil, err
}
routelines, err := parseNetRoute(bytes.NewReader(b))
if err != nil {
return nil, fmt.Errorf("failed to read net route from %s: %w", path, err)
}
return routelines, nil
}
func parseNetRoute(r io.Reader) ([]NetRouteLine, error) {
var routelines []NetRouteLine
scanner := bufio.NewScanner(r)
scanner.Scan()
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
routeline, err := parseNetRouteLine(fields)
if err != nil {
return nil, err
}
routelines = append(routelines, *routeline)
}
return routelines, nil
}
func parseNetRouteLine(fields []string) (*NetRouteLine, error) {
if len(fields) != routeLineColumns {
return nil, fmt.Errorf("invalid routeline, num of digits: %d", len(fields))
}
iface := fields[0]
if iface == blackholeRepresentation {
iface = blackholeIfaceName
}
destination, err := strconv.ParseUint(fields[1], 16, 32)
if err != nil {
return nil, err
}
gateway, err := strconv.ParseUint(fields[2], 16, 32)
if err != nil {
return nil, err
}
flags, err := strconv.ParseUint(fields[3], 10, 32)
if err != nil {
return nil, err
}
refcnt, err := strconv.ParseUint(fields[4], 10, 32)
if err != nil {
return nil, err
}
use, err := strconv.ParseUint(fields[5], 10, 32)
if err != nil {
return nil, err
}
metric, err := strconv.ParseUint(fields[6], 10, 32)
if err != nil {
return nil, err
}
mask, err := strconv.ParseUint(fields[7], 16, 32)
if err != nil {
return nil, err
}
mtu, err := strconv.ParseUint(fields[8], 10, 32)
if err != nil {
return nil, err
}
window, err := strconv.ParseUint(fields[9], 10, 32)
if err != nil {
return nil, err
}
irtt, err := strconv.ParseUint(fields[10], 10, 32)
if err != nil {
return nil, err
}
routeline := &NetRouteLine{
Iface: iface,
Destination: uint32(destination),
Gateway: uint32(gateway),
Flags: uint32(flags),
RefCnt: uint32(refcnt),
Use: uint32(use),
Metric: uint32(metric),
Mask: uint32(mask),
MTU: uint32(mtu),
Window: uint32(window),
IRTT: uint32(irtt),
}
return routeline, nil
}

View File

@@ -16,7 +16,6 @@ package procfs
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"strings" "strings"
@@ -70,7 +69,7 @@ func readSockstat(name string) (*NetSockstat, error) {
stat, err := parseSockstat(bytes.NewReader(b)) stat, err := parseSockstat(bytes.NewReader(b))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read sockstats from %q: %w", name, err) return nil, fmt.Errorf("%s: sockstats from %q: %w", ErrFileRead, name, err)
} }
return stat, nil return stat, nil
@@ -84,13 +83,13 @@ func parseSockstat(r io.Reader) (*NetSockstat, error) {
// Expect a minimum of a protocol and one key/value pair. // Expect a minimum of a protocol and one key/value pair.
fields := strings.Split(s.Text(), " ") fields := strings.Split(s.Text(), " ")
if len(fields) < 3 { if len(fields) < 3 {
return nil, fmt.Errorf("malformed sockstat line: %q", s.Text()) return nil, fmt.Errorf("%w: Malformed sockstat line: %q", ErrFileParse, s.Text())
} }
// The remaining fields are key/value pairs. // The remaining fields are key/value pairs.
kvs, err := parseSockstatKVs(fields[1:]) kvs, err := parseSockstatKVs(fields[1:])
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing sockstat key/value pairs from %q: %w", s.Text(), err) return nil, fmt.Errorf("%s: sockstat key/value pairs from %q: %w", ErrFileParse, s.Text(), err)
} }
// The first field is the protocol. We must trim its colon suffix. // The first field is the protocol. We must trim its colon suffix.
@@ -119,7 +118,7 @@ func parseSockstat(r io.Reader) (*NetSockstat, error) {
// parseSockstatKVs parses a string slice into a map of key/value pairs. // parseSockstatKVs parses a string slice into a map of key/value pairs.
func parseSockstatKVs(kvs []string) (map[string]int, error) { func parseSockstatKVs(kvs []string) (map[string]int, error) {
if len(kvs)%2 != 0 { if len(kvs)%2 != 0 {
return nil, errors.New("odd number of fields in key/value pairs") return nil, fmt.Errorf("%w:: Odd number of fields in key/value pairs %q", ErrFileParse, kvs)
} }
// Iterate two values at a time to gather key/value pairs. // Iterate two values at a time to gather key/value pairs.

View File

@@ -64,7 +64,7 @@ func (fs FS) NetSoftnetStat() ([]SoftnetStat, error) {
entries, err := parseSoftnet(bytes.NewReader(b)) entries, err := parseSoftnet(bytes.NewReader(b))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse /proc/net/softnet_stat: %w", err) return nil, fmt.Errorf("%s: /proc/net/softnet_stat: %w", ErrFileParse, err)
} }
return entries, nil return entries, nil
@@ -83,7 +83,7 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) {
softnetStat := SoftnetStat{} softnetStat := SoftnetStat{}
if width < minColumns { if width < minColumns {
return nil, fmt.Errorf("%d columns were detected, but at least %d were expected", width, minColumns) return nil, fmt.Errorf("%w: detected %d columns, but expected at least %d", ErrFileParse, width, minColumns)
} }
// Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2347 // Linux 2.6.23 https://elixir.bootlin.com/linux/v2.6.23/source/net/core/dev.c#L2347

View File

@@ -108,14 +108,14 @@ func parseNetUNIX(r io.Reader) (*NetUNIX, error) {
line := s.Text() line := s.Text()
item, err := nu.parseLine(line, hasInode, minFields) item, err := nu.parseLine(line, hasInode, minFields)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse /proc/net/unix data %q: %w", line, err) return nil, fmt.Errorf("%s: /proc/net/unix encountered data %q: %w", ErrFileParse, line, err)
} }
nu.Rows = append(nu.Rows, item) nu.Rows = append(nu.Rows, item)
} }
if err := s.Err(); err != nil { if err := s.Err(); err != nil {
return nil, fmt.Errorf("failed to scan /proc/net/unix data: %w", err) return nil, fmt.Errorf("%s: /proc/net/unix encountered data: %w", ErrFileParse, err)
} }
return &nu, nil return &nu, nil
@@ -126,7 +126,7 @@ func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine,
l := len(fields) l := len(fields)
if l < min { if l < min {
return nil, fmt.Errorf("expected at least %d fields but got %d", min, l) return nil, fmt.Errorf("%w: expected at least %d fields but got %d", ErrFileParse, min, l)
} }
// Field offsets are as follows: // Field offsets are as follows:
@@ -136,29 +136,29 @@ func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine,
users, err := u.parseUsers(fields[1]) users, err := u.parseUsers(fields[1])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse ref count %q: %w", fields[1], err) return nil, fmt.Errorf("%s: ref count %q: %w", ErrFileParse, fields[1], err)
} }
flags, err := u.parseFlags(fields[3]) flags, err := u.parseFlags(fields[3])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse flags %q: %w", fields[3], err) return nil, fmt.Errorf("%s: Unable to parse flags %q: %w", ErrFileParse, fields[3], err)
} }
typ, err := u.parseType(fields[4]) typ, err := u.parseType(fields[4])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse type %q: %w", fields[4], err) return nil, fmt.Errorf("%s: Failed to parse type %q: %w", ErrFileParse, fields[4], err)
} }
state, err := u.parseState(fields[5]) state, err := u.parseState(fields[5])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse state %q: %w", fields[5], err) return nil, fmt.Errorf("%s: Failed to parse state %q: %w", ErrFileParse, fields[5], err)
} }
var inode uint64 var inode uint64
if hasInode { if hasInode {
inode, err = u.parseInode(fields[6]) inode, err = u.parseInode(fields[6])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse inode %q: %w", fields[6], err) return nil, fmt.Errorf("%s failed to parse inode %q: %w", ErrFileParse, fields[6], err)
} }
} }

View File

@@ -68,7 +68,7 @@ func (fs FS) Wireless() ([]*Wireless, error) {
m, err := parseWireless(bytes.NewReader(b)) m, err := parseWireless(bytes.NewReader(b))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse wireless: %w", err) return nil, fmt.Errorf("%s: wireless: %w", ErrFileParse, err)
} }
return m, nil return m, nil
@@ -97,64 +97,64 @@ func parseWireless(r io.Reader) ([]*Wireless, error) {
parts := strings.Split(line, ":") parts := strings.Split(line, ":")
if len(parts) != 2 { if len(parts) != 2 {
return nil, fmt.Errorf("expected 2 parts after splitting line by ':', got %d for line %q", len(parts), line) return nil, fmt.Errorf("%w: expected 2 parts after splitting line by ':', got %d for line %q", ErrFileParse, len(parts), line)
} }
name := strings.TrimSpace(parts[0]) name := strings.TrimSpace(parts[0])
stats := strings.Fields(parts[1]) stats := strings.Fields(parts[1])
if len(stats) < 10 { if len(stats) < 10 {
return nil, fmt.Errorf("invalid number of fields in line %d, expected at least 10, got %d: %q", n, len(stats), line) return nil, fmt.Errorf("%w: invalid number of fields in line %d, expected 10+, got %d: %q", ErrFileParse, n, len(stats), line)
} }
status, err := strconv.ParseUint(stats[0], 16, 16) status, err := strconv.ParseUint(stats[0], 16, 16)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid status in line %d: %q", n, line) return nil, fmt.Errorf("%w: invalid status in line %d: %q", ErrFileParse, n, line)
} }
qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], ".")) qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], "."))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Quality:link as integer %q: %w", qlink, err) return nil, fmt.Errorf("%s: parse Quality:link as integer %q: %w", ErrFileParse, qlink, err)
} }
qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], ".")) qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], "."))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Quality:level as integer %q: %w", qlevel, err) return nil, fmt.Errorf("%s: Quality:level as integer %q: %w", ErrFileParse, qlevel, err)
} }
qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], ".")) qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], "."))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Quality:noise as integer %q: %w", qnoise, err) return nil, fmt.Errorf("%s: Quality:noise as integer %q: %w", ErrFileParse, qnoise, err)
} }
dnwid, err := strconv.Atoi(stats[4]) dnwid, err := strconv.Atoi(stats[4])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Discarded:nwid as integer %q: %w", dnwid, err) return nil, fmt.Errorf("%s: Discarded:nwid as integer %q: %w", ErrFileParse, dnwid, err)
} }
dcrypt, err := strconv.Atoi(stats[5]) dcrypt, err := strconv.Atoi(stats[5])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Discarded:crypt as integer %q: %w", dcrypt, err) return nil, fmt.Errorf("%s: Discarded:crypt as integer %q: %w", ErrFileParse, dcrypt, err)
} }
dfrag, err := strconv.Atoi(stats[6]) dfrag, err := strconv.Atoi(stats[6])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Discarded:frag as integer %q: %w", dfrag, err) return nil, fmt.Errorf("%s: Discarded:frag as integer %q: %w", ErrFileParse, dfrag, err)
} }
dretry, err := strconv.Atoi(stats[7]) dretry, err := strconv.Atoi(stats[7])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Discarded:retry as integer %q: %w", dretry, err) return nil, fmt.Errorf("%s: Discarded:retry as integer %q: %w", ErrFileParse, dretry, err)
} }
dmisc, err := strconv.Atoi(stats[8]) dmisc, err := strconv.Atoi(stats[8])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Discarded:misc as integer %q: %w", dmisc, err) return nil, fmt.Errorf("%s: Discarded:misc as integer %q: %w", ErrFileParse, dmisc, err)
} }
mbeacon, err := strconv.Atoi(stats[9]) mbeacon, err := strconv.Atoi(stats[9])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Missed:beacon as integer %q: %w", mbeacon, err) return nil, fmt.Errorf("%s: Missed:beacon as integer %q: %w", ErrFileParse, mbeacon, err)
} }
w := &Wireless{ w := &Wireless{
@@ -175,7 +175,7 @@ func parseWireless(r io.Reader) ([]*Wireless, error) {
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("failed to scan /proc/net/wireless: %w", err) return nil, fmt.Errorf("%s: Failed to scan /proc/net/wireless: %w", ErrFileRead, err)
} }
return interfaces, nil return interfaces, nil

View File

@@ -115,7 +115,7 @@ func (fs FS) NewXfrmStat() (XfrmStat, error) {
fields := strings.Fields(s.Text()) fields := strings.Fields(s.Text())
if len(fields) != 2 { if len(fields) != 2 {
return XfrmStat{}, fmt.Errorf("couldn't parse %q line %q", file.Name(), s.Text()) return XfrmStat{}, fmt.Errorf("%w: %q line %q", ErrFileParse, file.Name(), s.Text())
} }
name := fields[0] name := fields[0]

View File

@@ -15,6 +15,7 @@ package procfs
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@@ -35,6 +36,12 @@ type Proc struct {
// Procs represents a list of Proc structs. // Procs represents a list of Proc structs.
type Procs []Proc type Procs []Proc
var (
ErrFileParse = errors.New("Error Parsing File")
ErrFileRead = errors.New("Error Reading File")
ErrMountPoint = errors.New("Error Accessing Mount point")
)
func (p Procs) Len() int { return len(p) } func (p Procs) Len() int { return len(p) }
func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID } func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
@@ -42,7 +49,7 @@ func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
// Self returns a process for the current process read via /proc/self. // Self returns a process for the current process read via /proc/self.
func Self() (Proc, error) { func Self() (Proc, error) {
fs, err := NewFS(DefaultMountPoint) fs, err := NewFS(DefaultMountPoint)
if err != nil { if err != nil || errors.Unwrap(err) == ErrMountPoint {
return Proc{}, err return Proc{}, err
} }
return fs.Self() return fs.Self()
@@ -104,7 +111,7 @@ func (fs FS) AllProcs() (Procs, error) {
names, err := d.Readdirnames(-1) names, err := d.Readdirnames(-1)
if err != nil { if err != nil {
return Procs{}, fmt.Errorf("could not read %q: %w", d.Name(), err) return Procs{}, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err)
} }
p := Procs{} p := Procs{}
@@ -205,7 +212,7 @@ func (p Proc) FileDescriptors() ([]uintptr, error) {
for i, n := range names { for i, n := range names {
fd, err := strconv.ParseInt(n, 10, 32) fd, err := strconv.ParseInt(n, 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not parse fd %q: %w", n, err) return nil, fmt.Errorf("%s: Cannot parse line: %v: %w", ErrFileParse, i, err)
} }
fds[i] = uintptr(fd) fds[i] = uintptr(fd)
} }
@@ -290,7 +297,7 @@ func (p Proc) fileDescriptors() ([]string, error) {
names, err := d.Readdirnames(-1) names, err := d.Readdirnames(-1)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read %q: %w", d.Name(), err) return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err)
} }
return names, nil return names, nil

View File

@@ -51,7 +51,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) {
fields := strings.SplitN(cgroupStr, ":", 3) fields := strings.SplitN(cgroupStr, ":", 3)
if len(fields) < 3 { if len(fields) < 3 {
return nil, fmt.Errorf("at least 3 fields required, found %d fields in cgroup string: %s", len(fields), cgroupStr) return nil, fmt.Errorf("%w: 3+ fields required, found %d fields in cgroup string: %s", ErrFileParse, len(fields), cgroupStr)
} }
cgroup := &Cgroup{ cgroup := &Cgroup{
@@ -60,7 +60,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) {
} }
cgroup.HierarchyID, err = strconv.Atoi(fields[0]) cgroup.HierarchyID, err = strconv.Atoi(fields[0])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse hierarchy ID") return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, cgroup.HierarchyID)
} }
if fields[1] != "" { if fields[1] != "" {
ssNames := strings.Split(fields[1], ",") ssNames := strings.Split(fields[1], ",")

View File

@@ -46,7 +46,7 @@ func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) {
fields := strings.Fields(CgroupSummaryStr) fields := strings.Fields(CgroupSummaryStr)
// require at least 4 fields // require at least 4 fields
if len(fields) < 4 { if len(fields) < 4 {
return nil, fmt.Errorf("at least 4 fields required, found %d fields in cgroup info string: %s", len(fields), CgroupSummaryStr) return nil, fmt.Errorf("%w: 4+ fields required, found %d fields in cgroup info string: %s", ErrFileParse, len(fields), CgroupSummaryStr)
} }
CgroupSummary := &CgroupSummary{ CgroupSummary := &CgroupSummary{
@@ -54,15 +54,15 @@ func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) {
} }
CgroupSummary.Hierarchy, err = strconv.Atoi(fields[1]) CgroupSummary.Hierarchy, err = strconv.Atoi(fields[1])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse hierarchy ID") return nil, fmt.Errorf("%w: Unable to parse hierarchy ID from %q", ErrFileParse, fields[1])
} }
CgroupSummary.Cgroups, err = strconv.Atoi(fields[2]) CgroupSummary.Cgroups, err = strconv.Atoi(fields[2])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Cgroup Num") return nil, fmt.Errorf("%w: Unable to parse Cgroup Num from %q", ErrFileParse, fields[2])
} }
CgroupSummary.Enabled, err = strconv.Atoi(fields[3]) CgroupSummary.Enabled, err = strconv.Atoi(fields[3])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse Enabled") return nil, fmt.Errorf("%w: Unable to parse Enabled from %q", ErrFileParse, fields[3])
} }
return CgroupSummary, nil return CgroupSummary, nil
} }

View File

@@ -111,7 +111,7 @@ func parseInotifyInfo(line string) (*InotifyInfo, error) {
} }
return i, nil return i, nil
} }
return nil, fmt.Errorf("invalid inode entry: %q", line) return nil, fmt.Errorf("%w: invalid inode entry: %q", ErrFileParse, line)
} }
// ProcFDInfos represents a list of ProcFDInfo structs. // ProcFDInfos represents a list of ProcFDInfo structs.

View File

@@ -66,7 +66,7 @@ func parseInterrupts(r io.Reader) (Interrupts, error) {
continue continue
} }
if len(parts) < 2 { if len(parts) < 2 {
return nil, fmt.Errorf("not enough fields in interrupts (expected at least 2 fields but got %d): %s", len(parts), parts) return nil, fmt.Errorf("%w: Not enough fields in interrupts (expected 2+ fields but got %d): %s", ErrFileParse, len(parts), parts)
} }
intName := parts[0][:len(parts[0])-1] // remove trailing : intName := parts[0][:len(parts[0])-1] // remove trailing :

View File

@@ -103,7 +103,7 @@ func (p Proc) Limits() (ProcLimits, error) {
//fields := limitsMatch.Split(s.Text(), limitsFields) //fields := limitsMatch.Split(s.Text(), limitsFields)
fields := limitsMatch.FindStringSubmatch(s.Text()) fields := limitsMatch.FindStringSubmatch(s.Text())
if len(fields) != limitsFields { if len(fields) != limitsFields {
return ProcLimits{}, fmt.Errorf("couldn't parse %q line %q", f.Name(), s.Text()) return ProcLimits{}, fmt.Errorf("%w: couldn't parse %q line %q", ErrFileParse, f.Name(), s.Text())
} }
switch fields[1] { switch fields[1] {
@@ -154,7 +154,7 @@ func parseUint(s string) (uint64, error) {
} }
i, err := strconv.ParseUint(s, 10, 64) i, err := strconv.ParseUint(s, 10, 64)
if err != nil { if err != nil {
return 0, fmt.Errorf("couldn't parse value %q: %w", s, err) return 0, fmt.Errorf("%s: couldn't parse value %q: %w", ErrFileParse, s, err)
} }
return i, nil return i, nil
} }

View File

@@ -65,7 +65,7 @@ type ProcMap struct {
func parseDevice(s string) (uint64, error) { func parseDevice(s string) (uint64, error) {
toks := strings.Split(s, ":") toks := strings.Split(s, ":")
if len(toks) < 2 { if len(toks) < 2 {
return 0, fmt.Errorf("unexpected number of fields") return 0, fmt.Errorf("%w: unexpected number of fields, expected: 2, got: %q", ErrFileParse, len(toks))
} }
major, err := strconv.ParseUint(toks[0], 16, 0) major, err := strconv.ParseUint(toks[0], 16, 0)
@@ -95,7 +95,7 @@ func parseAddress(s string) (uintptr, error) {
func parseAddresses(s string) (uintptr, uintptr, error) { func parseAddresses(s string) (uintptr, uintptr, error) {
toks := strings.Split(s, "-") toks := strings.Split(s, "-")
if len(toks) < 2 { if len(toks) < 2 {
return 0, 0, fmt.Errorf("invalid address") return 0, 0, fmt.Errorf("%w: invalid address", ErrFileParse)
} }
saddr, err := parseAddress(toks[0]) saddr, err := parseAddress(toks[0])
@@ -114,7 +114,7 @@ func parseAddresses(s string) (uintptr, uintptr, error) {
// parsePermissions parses a token and returns any that are set. // parsePermissions parses a token and returns any that are set.
func parsePermissions(s string) (*ProcMapPermissions, error) { func parsePermissions(s string) (*ProcMapPermissions, error) {
if len(s) < 4 { if len(s) < 4 {
return nil, fmt.Errorf("invalid permissions token") return nil, fmt.Errorf("%w: invalid permissions token", ErrFileParse)
} }
perms := ProcMapPermissions{} perms := ProcMapPermissions{}
@@ -141,7 +141,7 @@ func parsePermissions(s string) (*ProcMapPermissions, error) {
func parseProcMap(text string) (*ProcMap, error) { func parseProcMap(text string) (*ProcMap, error) {
fields := strings.Fields(text) fields := strings.Fields(text)
if len(fields) < 5 { if len(fields) < 5 {
return nil, fmt.Errorf("truncated procmap entry") return nil, fmt.Errorf("%w: truncated procmap entry", ErrFileParse)
} }
saddr, eaddr, err := parseAddresses(fields[0]) saddr, eaddr, err := parseAddresses(fields[0])

View File

@@ -195,8 +195,8 @@ func parseProcNetstat(r io.Reader, fileName string) (ProcNetstat, error) {
// Remove trailing :. // Remove trailing :.
protocol := strings.TrimSuffix(nameParts[0], ":") protocol := strings.TrimSuffix(nameParts[0], ":")
if len(nameParts) != len(valueParts) { if len(nameParts) != len(valueParts) {
return procNetstat, fmt.Errorf("mismatch field count mismatch in %s: %s", return procNetstat, fmt.Errorf("%w: mismatch field count mismatch in %s: %s",
fileName, protocol) ErrFileParse, fileName, protocol)
} }
for i := 1; i < len(nameParts); i++ { for i := 1; i < len(nameParts); i++ {
value, err := strconv.ParseFloat(valueParts[i], 64) value, err := strconv.ParseFloat(valueParts[i], 64)

View File

@@ -40,7 +40,7 @@ func (p Proc) Namespaces() (Namespaces, error) {
names, err := d.Readdirnames(-1) names, err := d.Readdirnames(-1)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read contents of ns dir: %w", err) return nil, fmt.Errorf("%s: failed to read contents of ns dir: %w", ErrFileRead, err)
} }
ns := make(Namespaces, len(names)) ns := make(Namespaces, len(names))
@@ -52,13 +52,13 @@ func (p Proc) Namespaces() (Namespaces, error) {
fields := strings.SplitN(target, ":", 2) fields := strings.SplitN(target, ":", 2)
if len(fields) != 2 { if len(fields) != 2 {
return nil, fmt.Errorf("failed to parse namespace type and inode from %q", target) return nil, fmt.Errorf("%w: namespace type and inode from %q", ErrFileParse, target)
} }
typ := fields[0] typ := fields[0]
inode, err := strconv.ParseUint(strings.Trim(fields[1], "[]"), 10, 32) inode, err := strconv.ParseUint(strings.Trim(fields[1], "[]"), 10, 32)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse inode from %q: %w", fields[1], err) return nil, fmt.Errorf("%s: inode from %q: %w", ErrFileParse, fields[1], err)
} }
ns[name] = Namespace{typ, uint32(inode)} ns[name] = Namespace{typ, uint32(inode)}

View File

@@ -61,7 +61,7 @@ type PSIStats struct {
func (fs FS) PSIStatsForResource(resource string) (PSIStats, error) { func (fs FS) PSIStatsForResource(resource string) (PSIStats, error) {
data, err := util.ReadFileNoStat(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource))) data, err := util.ReadFileNoStat(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource)))
if err != nil { if err != nil {
return PSIStats{}, fmt.Errorf("psi_stats: unavailable for %q: %w", resource, err) return PSIStats{}, fmt.Errorf("%s: psi_stats: unavailable for %q: %w", ErrFileRead, resource, err)
} }
return parsePSIStats(resource, bytes.NewReader(data)) return parsePSIStats(resource, bytes.NewReader(data))

View File

@@ -159,8 +159,8 @@ func parseSnmp(r io.Reader, fileName string) (ProcSnmp, error) {
// Remove trailing :. // Remove trailing :.
protocol := strings.TrimSuffix(nameParts[0], ":") protocol := strings.TrimSuffix(nameParts[0], ":")
if len(nameParts) != len(valueParts) { if len(nameParts) != len(valueParts) {
return procSnmp, fmt.Errorf("mismatch field count mismatch in %s: %s", return procSnmp, fmt.Errorf("%w: mismatch field count mismatch in %s: %s",
fileName, protocol) ErrFileParse, fileName, protocol)
} }
for i := 1; i < len(nameParts); i++ { for i := 1; i < len(nameParts); i++ {
value, err := strconv.ParseFloat(valueParts[i], 64) value, err := strconv.ParseFloat(valueParts[i], 64)

View File

@@ -138,7 +138,7 @@ func (p Proc) Stat() (ProcStat, error) {
) )
if l < 0 || r < 0 { if l < 0 || r < 0 {
return ProcStat{}, fmt.Errorf("unexpected format, couldn't extract comm %q", data) return ProcStat{}, fmt.Errorf("%w: unexpected format, couldn't extract comm %q", ErrFileParse, data)
} }
s.Comm = string(data[l+1 : r]) s.Comm = string(data[l+1 : r])

View File

@@ -44,7 +44,7 @@ func (fs FS) SysctlInts(sysctl string) ([]int, error) {
vp := util.NewValueParser(f) vp := util.NewValueParser(f)
values[i] = vp.Int() values[i] = vp.Int()
if err := vp.Err(); err != nil { if err := vp.Err(); err != nil {
return nil, fmt.Errorf("field %d in sysctl %s is not a valid int: %w", i, sysctl, err) return nil, fmt.Errorf("%s: field %d in sysctl %s is not a valid int: %w", ErrFileParse, i, sysctl, err)
} }
} }
return values, nil return values, nil

View File

@@ -68,7 +68,7 @@ func parseV21SlabEntry(line string) (*Slab, error) {
l := slabSpace.ReplaceAllString(line, " ") l := slabSpace.ReplaceAllString(line, " ")
s := strings.Split(l, " ") s := strings.Split(l, " ")
if len(s) != 16 { if len(s) != 16 {
return nil, fmt.Errorf("unable to parse: %q", line) return nil, fmt.Errorf("%w: unable to parse: %q", ErrFileParse, line)
} }
var err error var err error
i := &Slab{Name: s[0]} i := &Slab{Name: s[0]}

View File

@@ -57,7 +57,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
) )
if !scanner.Scan() { if !scanner.Scan() {
return Softirqs{}, fmt.Errorf("softirqs empty") return Softirqs{}, fmt.Errorf("%w: softirqs empty", ErrFileRead)
} }
for scanner.Scan() { for scanner.Scan() {
@@ -74,7 +74,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.Hi = make([]uint64, len(perCPU)) softirqs.Hi = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.Hi[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.Hi[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (HI%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (HI%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "TIMER:": case parts[0] == "TIMER:":
@@ -82,7 +82,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.Timer = make([]uint64, len(perCPU)) softirqs.Timer = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.Timer[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.Timer[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (TIMER%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (TIMER%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "NET_TX:": case parts[0] == "NET_TX:":
@@ -90,7 +90,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.NetTx = make([]uint64, len(perCPU)) softirqs.NetTx = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.NetTx[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.NetTx[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (NET_TX%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (NET_TX%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "NET_RX:": case parts[0] == "NET_RX:":
@@ -98,7 +98,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.NetRx = make([]uint64, len(perCPU)) softirqs.NetRx = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.NetRx[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.NetRx[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (NET_RX%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (NET_RX%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "BLOCK:": case parts[0] == "BLOCK:":
@@ -106,7 +106,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.Block = make([]uint64, len(perCPU)) softirqs.Block = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.Block[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.Block[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (BLOCK%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (BLOCK%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "IRQ_POLL:": case parts[0] == "IRQ_POLL:":
@@ -114,7 +114,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.IRQPoll = make([]uint64, len(perCPU)) softirqs.IRQPoll = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.IRQPoll[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.IRQPoll[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (IRQ_POLL%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (IRQ_POLL%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "TASKLET:": case parts[0] == "TASKLET:":
@@ -122,7 +122,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.Tasklet = make([]uint64, len(perCPU)) softirqs.Tasklet = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.Tasklet[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.Tasklet[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (TASKLET%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (TASKLET%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "SCHED:": case parts[0] == "SCHED:":
@@ -130,7 +130,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.Sched = make([]uint64, len(perCPU)) softirqs.Sched = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.Sched[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.Sched[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (SCHED%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (SCHED%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "HRTIMER:": case parts[0] == "HRTIMER:":
@@ -138,7 +138,7 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.HRTimer = make([]uint64, len(perCPU)) softirqs.HRTimer = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.HRTimer[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.HRTimer[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (HRTIMER%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (HRTIMER%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "RCU:": case parts[0] == "RCU:":
@@ -146,14 +146,14 @@ func parseSoftirqs(r io.Reader) (Softirqs, error) {
softirqs.RCU = make([]uint64, len(perCPU)) softirqs.RCU = make([]uint64, len(perCPU))
for i, count := range perCPU { for i, count := range perCPU {
if softirqs.RCU[i], err = strconv.ParseUint(count, 10, 64); err != nil { if softirqs.RCU[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse %q (RCU%d): %w", count, i, err) return Softirqs{}, fmt.Errorf("%s: couldn't parse %q (RCU%d): %w", ErrFileParse, count, i, err)
} }
} }
} }
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return Softirqs{}, fmt.Errorf("couldn't parse softirqs: %w", err) return Softirqs{}, fmt.Errorf("%s: couldn't parse softirqs: %w", ErrFileParse, err)
} }
return softirqs, scanner.Err() return softirqs, scanner.Err()

View File

@@ -93,10 +93,10 @@ func parseCPUStat(line string) (CPUStat, int64, error) {
&cpuStat.Guest, &cpuStat.GuestNice) &cpuStat.Guest, &cpuStat.GuestNice)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return CPUStat{}, -1, fmt.Errorf("couldn't parse %q (cpu): %w", line, err) return CPUStat{}, -1, fmt.Errorf("%s: couldn't parse %q (cpu): %w", ErrFileParse, line, err)
} }
if count == 0 { if count == 0 {
return CPUStat{}, -1, fmt.Errorf("couldn't parse %q (cpu): 0 elements parsed", line) return CPUStat{}, -1, fmt.Errorf("%w: couldn't parse %q (cpu): 0 elements parsed", ErrFileParse, line)
} }
cpuStat.User /= userHZ cpuStat.User /= userHZ
@@ -116,7 +116,7 @@ func parseCPUStat(line string) (CPUStat, int64, error) {
cpuID, err := strconv.ParseInt(cpu[3:], 10, 64) cpuID, err := strconv.ParseInt(cpu[3:], 10, 64)
if err != nil { if err != nil {
return CPUStat{}, -1, fmt.Errorf("couldn't parse %q (cpu/cpuid): %w", line, err) return CPUStat{}, -1, fmt.Errorf("%s: couldn't parse %q (cpu/cpuid): %w", ErrFileParse, line, err)
} }
return cpuStat, cpuID, nil return cpuStat, cpuID, nil
@@ -136,7 +136,7 @@ func parseSoftIRQStat(line string) (SoftIRQStat, uint64, error) {
&softIRQStat.Hrtimer, &softIRQStat.Rcu) &softIRQStat.Hrtimer, &softIRQStat.Rcu)
if err != nil { if err != nil {
return SoftIRQStat{}, 0, fmt.Errorf("couldn't parse %q (softirq): %w", line, err) return SoftIRQStat{}, 0, fmt.Errorf("%s: couldn't parse %q (softirq): %w", ErrFileParse, line, err)
} }
return softIRQStat, total, nil return softIRQStat, total, nil
@@ -197,34 +197,34 @@ func parseStat(r io.Reader, fileName string) (Stat, error) {
switch { switch {
case parts[0] == "btime": case parts[0] == "btime":
if stat.BootTime, err = strconv.ParseUint(parts[1], 10, 64); err != nil { if stat.BootTime, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (btime): %w", parts[1], err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (btime): %w", ErrFileParse, parts[1], err)
} }
case parts[0] == "intr": case parts[0] == "intr":
if stat.IRQTotal, err = strconv.ParseUint(parts[1], 10, 64); err != nil { if stat.IRQTotal, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (intr): %w", parts[1], err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (intr): %w", ErrFileParse, parts[1], err)
} }
numberedIRQs := parts[2:] numberedIRQs := parts[2:]
stat.IRQ = make([]uint64, len(numberedIRQs)) stat.IRQ = make([]uint64, len(numberedIRQs))
for i, count := range numberedIRQs { for i, count := range numberedIRQs {
if stat.IRQ[i], err = strconv.ParseUint(count, 10, 64); err != nil { if stat.IRQ[i], err = strconv.ParseUint(count, 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (intr%d): %w", count, i, err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (intr%d): %w", ErrFileParse, count, i, err)
} }
} }
case parts[0] == "ctxt": case parts[0] == "ctxt":
if stat.ContextSwitches, err = strconv.ParseUint(parts[1], 10, 64); err != nil { if stat.ContextSwitches, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (ctxt): %w", parts[1], err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (ctxt): %w", ErrFileParse, parts[1], err)
} }
case parts[0] == "processes": case parts[0] == "processes":
if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil { if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (processes): %w", parts[1], err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (processes): %w", ErrFileParse, parts[1], err)
} }
case parts[0] == "procs_running": case parts[0] == "procs_running":
if stat.ProcessesRunning, err = strconv.ParseUint(parts[1], 10, 64); err != nil { if stat.ProcessesRunning, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (procs_running): %w", parts[1], err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (procs_running): %w", ErrFileParse, parts[1], err)
} }
case parts[0] == "procs_blocked": case parts[0] == "procs_blocked":
if stat.ProcessesBlocked, err = strconv.ParseUint(parts[1], 10, 64); err != nil { if stat.ProcessesBlocked, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q (procs_blocked): %w", parts[1], err) return Stat{}, fmt.Errorf("%s: couldn't parse %q (procs_blocked): %w", ErrFileParse, parts[1], err)
} }
case parts[0] == "softirq": case parts[0] == "softirq":
softIRQStats, total, err := parseSoftIRQStat(line) softIRQStats, total, err := parseSoftIRQStat(line)
@@ -247,7 +247,7 @@ func parseStat(r io.Reader, fileName string) (Stat, error) {
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return Stat{}, fmt.Errorf("couldn't parse %q: %w", fileName, err) return Stat{}, fmt.Errorf("%s: couldn't parse %q: %w", ErrFileParse, fileName, err)
} }
return stat, nil return stat, nil

View File

@@ -64,7 +64,7 @@ func parseSwapString(swapString string) (*Swap, error) {
swapFields := strings.Fields(swapString) swapFields := strings.Fields(swapString)
swapLength := len(swapFields) swapLength := len(swapFields)
if swapLength < 5 { if swapLength < 5 {
return nil, fmt.Errorf("too few fields in swap string: %s", swapString) return nil, fmt.Errorf("%w: too few fields in swap string: %s", ErrFileParse, swapString)
} }
swap := &Swap{ swap := &Swap{
@@ -74,15 +74,15 @@ func parseSwapString(swapString string) (*Swap, error) {
swap.Size, err = strconv.Atoi(swapFields[2]) swap.Size, err = strconv.Atoi(swapFields[2])
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid swap size: %s", swapFields[2]) return nil, fmt.Errorf("%s: invalid swap size: %s: %w", ErrFileParse, swapFields[2], err)
} }
swap.Used, err = strconv.Atoi(swapFields[3]) swap.Used, err = strconv.Atoi(swapFields[3])
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid swap used: %s", swapFields[3]) return nil, fmt.Errorf("%s: invalid swap used: %s: %w", ErrFileParse, swapFields[3], err)
} }
swap.Priority, err = strconv.Atoi(swapFields[4]) swap.Priority, err = strconv.Atoi(swapFields[4])
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid swap priority: %s", swapFields[4]) return nil, fmt.Errorf("%s: invalid swap priority: %s: %w", ErrFileParse, swapFields[4], err)
} }
return swap, nil return swap, nil

View File

@@ -45,7 +45,7 @@ func (fs FS) AllThreads(pid int) (Procs, error) {
names, err := d.Readdirnames(-1) names, err := d.Readdirnames(-1)
if err != nil { if err != nil {
return Procs{}, fmt.Errorf("could not read %q: %w", d.Name(), err) return Procs{}, fmt.Errorf("%s: could not read %q: %w", ErrFileRead, d.Name(), err)
} }
t := Procs{} t := Procs{}

View File

@@ -86,7 +86,7 @@ func (fs FS) VM() (*VM, error) {
return nil, err return nil, err
} }
if !file.Mode().IsDir() { if !file.Mode().IsDir() {
return nil, fmt.Errorf("%s is not a directory", path) return nil, fmt.Errorf("%w: %s is not a directory", ErrFileRead, path)
} }
files, err := os.ReadDir(path) files, err := os.ReadDir(path)

View File

@@ -75,11 +75,11 @@ var nodeZoneRE = regexp.MustCompile(`(\d+), zone\s+(\w+)`)
func (fs FS) Zoneinfo() ([]Zoneinfo, error) { func (fs FS) Zoneinfo() ([]Zoneinfo, error) {
data, err := os.ReadFile(fs.proc.Path("zoneinfo")) data, err := os.ReadFile(fs.proc.Path("zoneinfo"))
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading zoneinfo %q: %w", fs.proc.Path("zoneinfo"), err) return nil, fmt.Errorf("%s: error reading zoneinfo %q: %w", ErrFileRead, fs.proc.Path("zoneinfo"), err)
} }
zoneinfo, err := parseZoneinfo(data) zoneinfo, err := parseZoneinfo(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing zoneinfo %q: %w", fs.proc.Path("zoneinfo"), err) return nil, fmt.Errorf("%s: error parsing zoneinfo %q: %w", ErrFileParse, fs.proc.Path("zoneinfo"), err)
} }
return zoneinfo, nil return zoneinfo, nil
} }

6
vendor/modules.txt vendored
View File

@@ -57,7 +57,7 @@ github.com/boltdb/bolt
# github.com/caddyserver/certmagic v0.18.0 # github.com/caddyserver/certmagic v0.18.0
## explicit; go 1.19 ## explicit; go 1.19
github.com/caddyserver/certmagic github.com/caddyserver/certmagic
# github.com/casbin/casbin/v2 v2.71.0 # github.com/casbin/casbin/v2 v2.71.1
## explicit; go 1.13 ## explicit; go 1.13
github.com/casbin/casbin/v2 github.com/casbin/casbin/v2
github.com/casbin/casbin/v2/config github.com/casbin/casbin/v2/config
@@ -82,7 +82,7 @@ github.com/cpuguy83/go-md2man/v2/md2man
## explicit; go 1.18 ## explicit; go 1.18
github.com/datarhei/core-client-go/v16 github.com/datarhei/core-client-go/v16
github.com/datarhei/core-client-go/v16/api github.com/datarhei/core-client-go/v16/api
# github.com/datarhei/gosrt v0.5.0 # github.com/datarhei/gosrt v0.5.2
## explicit; go 1.18 ## explicit; go 1.18
github.com/datarhei/gosrt github.com/datarhei/gosrt
github.com/datarhei/gosrt/internal/circular github.com/datarhei/gosrt/internal/circular
@@ -327,7 +327,7 @@ github.com/prometheus/client_model/go
github.com/prometheus/common/expfmt github.com/prometheus/common/expfmt
github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
github.com/prometheus/common/model github.com/prometheus/common/model
# github.com/prometheus/procfs v0.10.1 # github.com/prometheus/procfs v0.11.0
## explicit; go 1.19 ## explicit; go 1.19
github.com/prometheus/procfs github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/fs