...

Source file src/github.com/shirou/gopsutil/mem/mem_bsd.go

Documentation: github.com/shirou/gopsutil/mem

     1  // +build freebsd openbsd
     2  
     3  package mem
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"os/exec"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  const swapCommand = "swapctl"
    14  
    15  // swapctl column indexes
    16  const (
    17  	nameCol     = 0
    18  	totalKiBCol = 1
    19  	usedKiBCol  = 2
    20  )
    21  
    22  func SwapDevices() ([]*SwapDevice, error) {
    23  	return SwapDevicesWithContext(context.Background())
    24  }
    25  
    26  func SwapDevicesWithContext(ctx context.Context) ([]*SwapDevice, error) {
    27  	swapCommandPath, err := exec.LookPath(swapCommand)
    28  	if err != nil {
    29  		return nil, fmt.Errorf("could not find command %q: %w", swapCommand, err)
    30  	}
    31  	output, err := invoke.CommandWithContext(ctx, swapCommandPath, "-lk")
    32  	if err != nil {
    33  		return nil, fmt.Errorf("could not execute %q: %w", swapCommand, err)
    34  	}
    35  
    36  	return parseSwapctlOutput(string(output))
    37  }
    38  
    39  func parseSwapctlOutput(output string) ([]*SwapDevice, error) {
    40  	lines := strings.Split(output, "\n")
    41  	if len(lines) == 0 {
    42  		return nil, fmt.Errorf("could not parse output of %q: no lines in %q", swapCommand, output)
    43  	}
    44  
    45  	// Check header headerFields are as expected.
    46  	header := lines[0]
    47  	header = strings.ToLower(header)
    48  	header = strings.ReplaceAll(header, ":", "")
    49  	headerFields := strings.Fields(header)
    50  	if len(headerFields) < usedKiBCol {
    51  		return nil, fmt.Errorf("couldn't parse %q: too few fields in header %q", swapCommand, header)
    52  	}
    53  	if headerFields[nameCol] != "device" {
    54  		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[nameCol], "device")
    55  	}
    56  	if headerFields[totalKiBCol] != "1kb-blocks" && headerFields[totalKiBCol] != "1k-blocks" {
    57  		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[totalKiBCol], "1kb-blocks")
    58  	}
    59  	if headerFields[usedKiBCol] != "used" {
    60  		return nil, fmt.Errorf("couldn't parse %q: expected %q to be %q", swapCommand, headerFields[usedKiBCol], "used")
    61  	}
    62  
    63  	var swapDevices []*SwapDevice
    64  	for _, line := range lines[1:] {
    65  		if line == "" {
    66  			continue // the terminal line is typically empty
    67  		}
    68  		fields := strings.Fields(line)
    69  		if len(fields) < usedKiBCol {
    70  			return nil, fmt.Errorf("couldn't parse %q: too few fields", swapCommand)
    71  		}
    72  
    73  		totalKiB, err := strconv.ParseUint(fields[totalKiBCol], 10, 64)
    74  		if err != nil {
    75  			return nil, fmt.Errorf("couldn't parse 'Size' column in %q: %w", swapCommand, err)
    76  		}
    77  
    78  		usedKiB, err := strconv.ParseUint(fields[usedKiBCol], 10, 64)
    79  		if err != nil {
    80  			return nil, fmt.Errorf("couldn't parse 'Used' column in %q: %w", swapCommand, err)
    81  		}
    82  
    83  		swapDevices = append(swapDevices, &SwapDevice{
    84  			Name:      fields[nameCol],
    85  			UsedBytes: usedKiB * 1024,
    86  			FreeBytes: (totalKiB - usedKiB) * 1024,
    87  		})
    88  	}
    89  
    90  	return swapDevices, nil
    91  }
    92  

View as plain text