88 "io/ioutil"
99 "os"
1010 "path/filepath"
11- "strconv"
1211 "strings"
1312 "sync"
1413 "time"
@@ -67,12 +66,14 @@ var subsystems = subsystemSet{
6766
6867const (
6968 testScopeWait = 4
69+ testSliceWait = 4
7070)
7171
7272var (
7373 connLock sync.Mutex
7474 theConn * systemdDbus.Conn
7575 hasStartTransientUnit bool
76+ hasStartTransientSliceUnit bool
7677 hasTransientDefaultDependencies bool
7778 hasDelegate bool
7879)
@@ -159,8 +160,36 @@ func UseSystemd() bool {
159160 }
160161 }
161162
163+ // Assume we have the ability to start a transient unit as a slice
164+ // This was broken until systemd v229, but has been back-ported on RHEL environments >= 219
165+ // For details, see: https://bugzilla.redhat.com/show_bug.cgi?id=1370299
166+ hasStartTransientSliceUnit = true
167+
168+ // To ensure simple clean-up, we create a slice off the root with no hierarchy
169+ slice := fmt .Sprintf ("libcontainer_%d_systemd_test_default.slice" , os .Getpid ())
170+ if _ , err := theConn .StartTransientUnit (slice , "replace" , nil , nil ); err != nil {
171+ if _ , ok := err .(dbus.Error ); ok {
172+ hasStartTransientSliceUnit = false
173+ }
174+ }
175+
176+ for i := 0 ; i <= testSliceWait ; i ++ {
177+ if _ , err := theConn .StopUnit (slice , "replace" , nil ); err != nil {
178+ if dbusError , ok := err .(dbus.Error ); ok {
179+ if strings .Contains (dbusError .Name , "org.freedesktop.systemd1.NoSuchUnit" ) {
180+ hasStartTransientSliceUnit = false
181+ break
182+ }
183+ }
184+ } else {
185+ break
186+ }
187+ time .Sleep (time .Millisecond )
188+ }
189+
162190 // Not critical because of the stop unit logic above.
163191 theConn .StopUnit (scope , "replace" , nil )
192+ theConn .StopUnit (slice , "replace" , nil )
164193 }
165194 return hasStartTransientUnit
166195}
@@ -194,11 +223,24 @@ func (m *Manager) Apply(pid int) error {
194223 slice = c .Parent
195224 }
196225
197- properties = append (properties ,
198- systemdDbus .PropSlice (slice ),
199- systemdDbus .PropDescription ("docker container " + c .Name ),
200- newProp ("PIDs" , []uint32 {uint32 (pid )}),
201- )
226+ properties = append (properties , systemdDbus .PropDescription ("libcontainer container " + c .Name ))
227+
228+ // if we create a slice, the parent is defined via a Wants=
229+ if strings .HasSuffix (unitName , ".slice" ) {
230+ // This was broken until systemd v229, but has been back-ported on RHEL environments >= 219
231+ if ! hasStartTransientSliceUnit {
232+ return fmt .Errorf ("systemd version does not support ability to start a slice as transient unit" )
233+ }
234+ properties = append (properties , systemdDbus .PropWants (slice ))
235+ } else {
236+ // otherwise, we use Slice=
237+ properties = append (properties , systemdDbus .PropSlice (slice ))
238+ }
239+
240+ // only add pid if its valid, -1 is used w/ general slice creation.
241+ if pid != - 1 {
242+ properties = append (properties , newProp ("PIDs" , []uint32 {uint32 (pid )}))
243+ }
202244
203245 if hasDelegate {
204246 // This is only supported on systemd versions 218 and above.
@@ -302,10 +344,9 @@ func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
302344 if err := os .MkdirAll (path , 0755 ); err != nil {
303345 return "" , err
304346 }
305- if err := writeFile (path , "cgroup.procs" , strconv . Itoa ( pid ) ); err != nil {
347+ if err := cgroups . WriteCgroupProc (path , pid ); err != nil {
306348 return "" , err
307349 }
308-
309350 return path , nil
310351}
311352
@@ -350,7 +391,7 @@ func joinCgroups(c *configs.Cgroup, pid int) error {
350391// systemd represents slice heirarchy using `-`, so we need to follow suit when
351392// generating the path of slice. Essentially, test-a-b.slice becomes
352393// test.slice/test-a.slice/test-a-b.slice.
353- func expandSlice (slice string ) (string , error ) {
394+ func ExpandSlice (slice string ) (string , error ) {
354395 suffix := ".slice"
355396 // Name has to end with ".slice", but can't be just ".slice".
356397 if len (slice ) < len (suffix ) || ! strings .HasSuffix (slice , suffix ) {
@@ -364,6 +405,10 @@ func expandSlice(slice string) (string, error) {
364405
365406 var path , prefix string
366407 sliceName := strings .TrimSuffix (slice , suffix )
408+ // if input was -.slice, we should just return root now
409+ if sliceName == "-" {
410+ return "/" , nil
411+ }
367412 for _ , component := range strings .Split (sliceName , "-" ) {
368413 // test--a.slice isn't permitted, nor is -test.slice.
369414 if component == "" {
@@ -396,7 +441,7 @@ func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
396441 slice = c .Parent
397442 }
398443
399- slice , err = expandSlice (slice )
444+ slice , err = ExpandSlice (slice )
400445 if err != nil {
401446 return "" , err
402447 }
@@ -483,7 +528,11 @@ func (m *Manager) Set(container *configs.Config) error {
483528}
484529
485530func getUnitName (c * configs.Cgroup ) string {
486- return fmt .Sprintf ("%s-%s.scope" , c .ScopePrefix , c .Name )
531+ // by default, we create a scope unless the user explicitly asks for a slice.
532+ if ! strings .HasSuffix (c .Name , ".slice" ) {
533+ return fmt .Sprintf ("%s-%s.scope" , c .ScopePrefix , c .Name )
534+ }
535+ return c .Name
487536}
488537
489538func setKernelMemory (c * configs.Cgroup ) error {
0 commit comments