mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-26 13:48:59 +08:00

Executing the command does the lookup if needed and returns the same error when not found, no need to do it separately.
88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
//go:build freebsd || openbsd
|
|
// +build freebsd openbsd
|
|
|
|
package mem
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const swapCommand = "swapctl"
|
|
|
|
// swapctl column indexes
|
|
const (
|
|
nameCol = 0
|
|
totalKiBCol = 1
|
|
usedKiBCol = 2
|
|
)
|
|
|
|
func SwapDevices() ([]*SwapDevice, error) {
|
|
return SwapDevicesWithContext(context.Background())
|
|
}
|
|
|
|
func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
|
|
output, err := invoke.CommandWithContext(ctx, swapCommand, "-lk")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
|
|
}
|
|
|
|
return parseSwapctlOutput(string(output))
|
|
}
|
|
|
|
func parseSwapctlOutput(output string) ([]*SwapDevice, error) {
|
|
lines := strings.Split(output, "\n")
|
|
if len(lines) == 0 {
|
|
return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
|
|
}
|
|
|
|
// Check header headerFields are as expected.
|
|
header := lines[0]
|
|
header = strings.ToLower(header)
|
|
header = strings.ReplaceAll(header, ":", "")
|
|
headerFields := strings.Fields(header)
|
|
if len(headerFields) < usedKiBCol {
|
|
return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, header)
|
|
}
|
|
if headerFields[nameCol] != "device" {
|
|
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "device")
|
|
}
|
|
if headerFields[totalKiBCol] != "1kb-blocks" && headerFields[totalKiBCol] != "1k-blocks" {
|
|
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalKiBCol], "1kb-blocks")
|
|
}
|
|
if headerFields[usedKiBCol] != "used" {
|
|
return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[usedKiBCol], "used")
|
|
}
|
|
|
|
var swapDevices []*SwapDevice
|
|
for _, line := range lines[1:] {
|
|
if line == "" {
|
|
continue // the terminal line is typically empty
|
|
}
|
|
fields := strings.Fields(line)
|
|
if len(fields) < usedKiBCol {
|
|
return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
|
|
}
|
|
|
|
totalKiB, err := strconv.ParseUint(fields[totalKiBCol], 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
|
|
}
|
|
|
|
usedKiB, err := strconv.ParseUint(fields[usedKiBCol], 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
|
|
}
|
|
|
|
swapDevices = append(swapDevices, &SwapDevice{
|
|
Name: fields[nameCol],
|
|
UsedBytes: usedKiB * 1024,
|
|
FreeBytes: (totalKiB - usedKiB) * 1024,
|
|
})
|
|
}
|
|
|
|
return swapDevices, nil
|
|
}
|