1// Package capabilities is used for managing sets of linux capabilities. 2package capabilities 3 4import ( 5 "sort" 6 "strings" 7) 8 9type nothing struct{} 10 11var null = nothing{} 12 13// Set represents a group linux capabilities, implementing some useful set 14// operations, taking care of name normalization, and sentinel value expansions. 15// 16// Linux capabilities can be expressed in multiple ways when working with docker 17// and/or executor, along with Nomad configuration. 18// 19// Capability names may be upper or lower case, and may or may not be prefixed 20// with "CAP_" or "cap_". On top of that, Nomad interprets the special name "all" 21// and "ALL" to mean "all capabilities supported by the operating system". 22type Set struct { 23 data map[string]nothing 24} 25 26// New creates a new Set setting caps as the initial elements. 27func New(caps []string) *Set { 28 m := make(map[string]nothing, len(caps)) 29 for _, c := range caps { 30 insert(m, c) 31 } 32 return &Set{data: m} 33} 34 35// Add cap into s. 36func (s *Set) Add(cap string) { 37 insert(s.data, cap) 38} 39 40func insert(data map[string]nothing, cap string) { 41 switch name := normalize(cap); name { 42 case "": 43 case "all": 44 for k, v := range Supported().data { 45 data[k] = v 46 } 47 return 48 default: 49 data[name] = null 50 } 51} 52 53// Remove caps from s. 54func (s *Set) Remove(caps []string) { 55 for _, c := range caps { 56 name := normalize(c) 57 if name == "all" { 58 s.data = make(map[string]nothing) 59 return 60 } 61 delete(s.data, name) 62 } 63} 64 65// Union returns of Set of elements of both s and b. 66func (s *Set) Union(b *Set) *Set { 67 data := make(map[string]nothing) 68 for c := range s.data { 69 data[c] = null 70 } 71 for c := range b.data { 72 data[c] = null 73 } 74 return &Set{data: data} 75} 76 77// Difference returns the Set of elements of b not in s. 78func (s *Set) Difference(b *Set) *Set { 79 data := make(map[string]nothing) 80 for c := range b.data { 81 if _, exists := s.data[c]; !exists { 82 data[c] = null 83 } 84 } 85 return &Set{data: data} 86} 87 88// Intersect returns the Set of elements in both s and b. 89func (s *Set) Intersect(b *Set) *Set { 90 data := make(map[string]nothing) 91 for c := range s.data { 92 if _, exists := b.data[c]; exists { 93 data[c] = null 94 } 95 } 96 return &Set{data: data} 97} 98 99// Empty return true if no capabilities exist in s. 100func (s *Set) Empty() bool { 101 return len(s.data) == 0 102} 103 104// String returns the normalized and sorted string representation of s. 105func (s *Set) String() string { 106 return strings.Join(s.Slice(false), ", ") 107} 108 109// Slice returns a sorted slice of capabilities in s. 110// 111// upper - indicates whether to uppercase and prefix capabilities with CAP_ 112func (s *Set) Slice(upper bool) []string { 113 caps := make([]string, 0, len(s.data)) 114 for c := range s.data { 115 if upper { 116 c = "CAP_" + strings.ToUpper(c) 117 } 118 caps = append(caps, c) 119 } 120 sort.Strings(caps) 121 return caps 122} 123 124// linux capabilities are often named in 4 possible ways - upper or lower case, 125// and with or without a CAP_ prefix 126// 127// since we must do comparisons on cap names, always normalize the names before 128// letting them into the Set data-structure 129func normalize(name string) string { 130 spaces := strings.TrimSpace(name) 131 lower := strings.ToLower(spaces) 132 trim := strings.TrimPrefix(lower, "cap_") 133 return trim 134} 135