1
16
17 package cgroups
18
19 import (
20 "context"
21 "path/filepath"
22 "strings"
23 "sync"
24
25 systemdDbus "github.com/coreos/go-systemd/v22/dbus"
26 "github.com/godbus/dbus/v5"
27 specs "github.com/opencontainers/runtime-spec/specs-go"
28 )
29
30 const (
31 SystemdDbus Name = "systemd"
32 defaultSlice = "system.slice"
33 )
34
35 var (
36 canDelegate bool
37 once sync.Once
38 )
39
40 func Systemd() ([]Subsystem, error) {
41 root, err := v1MountPoint()
42 if err != nil {
43 return nil, err
44 }
45 defaultSubsystems, err := defaults(root)
46 if err != nil {
47 return nil, err
48 }
49 s, err := NewSystemd(root)
50 if err != nil {
51 return nil, err
52 }
53
54 return append([]Subsystem{s}, defaultSubsystems...), nil
55 }
56
57 func Slice(slice, name string) Path {
58 if slice == "" {
59 slice = defaultSlice
60 }
61 return func(subsystem Name) (string, error) {
62 return filepath.Join(slice, name), nil
63 }
64 }
65
66 func NewSystemd(root string) (*SystemdController, error) {
67 return &SystemdController{
68 root: root,
69 }, nil
70 }
71
72 type SystemdController struct {
73 mu sync.Mutex
74 root string
75 }
76
77 func (s *SystemdController) Name() Name {
78 return SystemdDbus
79 }
80
81 func (s *SystemdController) Create(path string, _ *specs.LinuxResources) error {
82 ctx := context.TODO()
83 conn, err := systemdDbus.NewWithContext(ctx)
84 if err != nil {
85 return err
86 }
87 defer conn.Close()
88 slice, name := splitName(path)
89
90
91
92 checkDelegate := func() {
93 canDelegate = true
94 dlSlice := newProperty("Delegate", true)
95 if _, err := conn.StartTransientUnitContext(ctx, slice, "testdelegate", []systemdDbus.Property{dlSlice}, nil); err != nil {
96 if dbusError, ok := err.(dbus.Error); ok {
97
98
99 if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") {
100 canDelegate = false
101 }
102 }
103 }
104
105 _, _ = conn.StopUnitContext(ctx, slice, "testDelegate", nil)
106 }
107 once.Do(checkDelegate)
108 properties := []systemdDbus.Property{
109 systemdDbus.PropDescription("cgroup " + name),
110 systemdDbus.PropWants(slice),
111 newProperty("DefaultDependencies", false),
112 newProperty("MemoryAccounting", true),
113 newProperty("CPUAccounting", true),
114 newProperty("BlockIOAccounting", true),
115 }
116
117
118 if canDelegate {
119 properties = append(properties, newProperty("Delegate", true))
120 }
121
122 ch := make(chan string)
123 _, err = conn.StartTransientUnitContext(ctx, name, "replace", properties, ch)
124 if err != nil {
125 return err
126 }
127 <-ch
128 return nil
129 }
130
131 func (s *SystemdController) Delete(path string) error {
132 ctx := context.TODO()
133 conn, err := systemdDbus.NewWithContext(ctx)
134 if err != nil {
135 return err
136 }
137 defer conn.Close()
138 _, name := splitName(path)
139 ch := make(chan string)
140 _, err = conn.StopUnitContext(ctx, name, "replace", ch)
141 if err != nil {
142 return err
143 }
144 <-ch
145 return nil
146 }
147
148 func newProperty(name string, units interface{}) systemdDbus.Property {
149 return systemdDbus.Property{
150 Name: name,
151 Value: dbus.MakeVariant(units),
152 }
153 }
154
155 func splitName(path string) (slice string, unit string) {
156 slice, unit = filepath.Split(path)
157 return strings.TrimSuffix(slice, "/"), unit
158 }
159
View as plain text