cnetstat/lsns.go

69 строки
1.7 KiB
Go

package main
import (
"bufio"
"context"
"fmt"
"os/exec"
"strings"
)
type NamespaceData struct {
Ns int
Pid int
}
var expectedHeaders = []string{
"NS", "PID",
}
// Parse output from 'lsns --output ns,pid'
func parseLsnsOutput(blob []byte) ([]NamespaceData, error) {
// Note: lsns has a --json option, so you would think that we
// would use that, pass the result to Go's builtin JSON
// decoder, and not have to write a custom parser. Except
// that, somewhere between util-linux 2.31 and 2.34, the JSON
// output format changed. The old version prints ns and pid as
// JSON strings containing ints, and the new one prints them
// as JSON numbers. The non-JSON output format is the same,
// which makes it easier to support both versions this way.
lines := bufio.NewScanner(strings.NewReader(string(blob)))
var result []NamespaceData
lines.Scan()
header := lines.Text()
if !stringSlicesEqual(strings.Fields(header), expectedHeaders) {
return nil, fmt.Errorf("Unexpected header of lsns output: %s", header)
}
for lines.Scan() {
var ns, pid int
_, err := fmt.Sscanf(lines.Text(), "%d %d", &ns, &pid)
if err != nil {
return nil, err
}
result = append(result, NamespaceData{
Ns: ns,
Pid: pid,
})
}
return result, nil
}
// Run lsns and parse the output.
// NOTE: if not run as root, lsns will succeed, but not necessarily
// return all namespaces
func listNetNamespaces() ([]NamespaceData, error) {
ctx, _ := context.WithTimeout(context.Background(), subprocessTimeout)
output, err := exec.CommandContext(ctx, "lsns", "--type", "net", "--output", "ns,pid").Output()
if err != nil {
return nil, err
}
return parseLsnsOutput(output)
}