diff --git a/capability/capability.go b/capability/capability.go index e6e418a..1b36f5f 100644 --- a/capability/capability.go +++ b/capability/capability.go @@ -137,6 +137,8 @@ func NewFile2(path string) (Capabilities, error) { // LastCap returns highest valid capability of the running kernel, // or an error if it can not be obtained. +// +// See also: [ListSupported]. func LastCap() (Cap, error) { return lastCap() } diff --git a/capability/capability_test.go b/capability/capability_test.go index ec03f18..bb405e6 100644 --- a/capability/capability_test.go +++ b/capability/capability_test.go @@ -9,6 +9,18 @@ import ( "testing" ) +// Based on the fact Go 1.18+ supports Linux >= 2.6.32, and +// - CAP_MAC_ADMIN (33) was added in 2.6.25; +// - CAP_SYSLOG (34) was added in 2.6.38; +// - CAP_CHECKPOINT_RESTORE (40) was added in 5.9, and it is +// the last added capability as of today (July 2024); +// +// LastCap return value should be between minLastCap and maxLastCap. +const ( + minLastCap = CAP_MAC_ADMIN + maxLastCap = CAP_CHECKPOINT_RESTORE +) + func TestLastCap(t *testing.T) { last, err := LastCap() switch runtime.GOOS { @@ -24,21 +36,35 @@ func TestLastCap(t *testing.T) { } // Sanity checks (Linux only). - // - // Based on the fact Go 1.18+ supports Linux >= 2.6.32, and - // - CAP_MAC_ADMIN (33) was added in 2.6.25; - // - CAP_SYSLOG (34) was added in 2.6.38; - // - CAP_CHECKPOINT_RESTORE (40) was added in 5.9, and it is - // the last added capability as of today (July 2024); - // LastCap return value should be between minCap and maxCap. - minCap := CAP_MAC_ADMIN - maxCap := CAP_CHECKPOINT_RESTORE - if last < minCap { + if last < minLastCap { t.Fatalf("LastCap returned %d (%s), expected >= %d (%s)", - last, last, minCap, minCap) + last, last, minLastCap, minLastCap) } - if last > maxCap { + if last > maxLastCap { t.Fatalf("LastCap returned %d, expected <= %d (%s). Package needs to be updated.", - last, maxCap, maxCap) + last, maxLastCap, maxLastCap) + } +} + +func TestListSupported(t *testing.T) { + list, err := ListSupported() + switch runtime.GOOS { + case "linux": + if err != nil { + t.Fatal(err) + } + default: + if err == nil { + t.Fatal(runtime.GOOS, ": want error, got nil") + } + } + if runtime.GOOS != "linux" { + return + } + // Sanity check (Linux only). + t.Logf("got +%v (len %d)", list, len(list)) + minLen := int(minLastCap) + 1 + if len(list) < minLen { + t.Fatalf("result is too short (got %d, want %d): +%v", len(list), minLen, list) } } diff --git a/capability/enum.go b/capability/enum.go index bbbc84d..f89f027 100644 --- a/capability/enum.go +++ b/capability/enum.go @@ -7,6 +7,8 @@ package capability +import "slices" + type CapType uint func (c CapType) String() string { @@ -301,3 +303,28 @@ const ( // Introduced in kernel 5.9 CAP_CHECKPOINT_RESTORE = Cap(40) ) + +// List returns the list of all capabilities known to the package. +// +// Deprecated: use [ListKnown] or [ListSupported] instead. +func List() []Cap { + return ListKnown() +} + +// ListKnown returns the list of all capabilities known to the package. +func ListKnown() []Cap { + return list() +} + +// ListSupported retuns the list of all capabilities known to the package, +// except those that are not supported by the currently running Linux kernel. +func ListSupported() ([]Cap, error) { + last, err := LastCap() + if err != nil { + return nil, err + } + return slices.DeleteFunc(list(), func(c Cap) bool { + // Remove caps not supported by the kernel. + return c > last + }), nil +} diff --git a/capability/enum_gen.go b/capability/enum_gen.go index 2ff9bf4..f72cd43 100644 --- a/capability/enum_gen.go +++ b/capability/enum_gen.go @@ -1,4 +1,4 @@ -// generated file; DO NOT EDIT - use go generate in directory with source +// Code generated by go generate; DO NOT EDIT. package capability @@ -90,8 +90,7 @@ func (c Cap) String() string { return "unknown" } -// List returns list of all supported capabilities -func List() []Cap { +func list() []Cap { return []Cap{ CAP_CHOWN, CAP_DAC_OVERRIDE, diff --git a/capability/enumgen/gen.go b/capability/enumgen/gen.go index 2813e03..88d40ca 100644 --- a/capability/enumgen/gen.go +++ b/capability/enumgen/gen.go @@ -23,7 +23,7 @@ type generator struct { } func (g *generator) writeHeader() { - g.buf.WriteString("// generated file; DO NOT EDIT - use go generate in directory with source\n") + g.buf.WriteString("// Code generated by go generate; DO NOT EDIT.\n") g.buf.WriteString("\n") g.buf.WriteString("package capability") } @@ -43,8 +43,7 @@ func (g *generator) writeStringFunc() { func (g *generator) writeListFunc() { g.buf.WriteString("\n") - g.buf.WriteString("// List returns list of all supported capabilities\n") - g.buf.WriteString("func List() []Cap {\n") + g.buf.WriteString("func list() []Cap {\n") g.buf.WriteString("return []Cap{\n") for _, cap := range g.caps { fmt.Fprintf(&g.buf, "%s,\n", cap)