systemd.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cgroups
  14. import (
  15. "fmt"
  16. "path/filepath"
  17. "strings"
  18. "sync"
  19. systemdDbus "github.com/coreos/go-systemd/v22/dbus"
  20. "github.com/godbus/dbus/v5"
  21. specs "github.com/opencontainers/runtime-spec/specs-go"
  22. )
  23. const (
  24. SystemdDbus Name = "systemd"
  25. defaultSlice = "system.slice"
  26. )
  27. var (
  28. canDelegate bool
  29. once sync.Once
  30. )
  31. func Systemd() ([]Subsystem, error) {
  32. root, err := v1MountPoint()
  33. if err != nil {
  34. return nil, err
  35. }
  36. defaultSubsystems, err := defaults(root)
  37. if err != nil {
  38. return nil, err
  39. }
  40. s, err := NewSystemd(root)
  41. if err != nil {
  42. return nil, err
  43. }
  44. // make sure the systemd controller is added first
  45. return append([]Subsystem{s}, defaultSubsystems...), nil
  46. }
  47. func Slice(slice, name string) Path {
  48. if slice == "" {
  49. slice = defaultSlice
  50. }
  51. return func(subsystem Name) (string, error) {
  52. return filepath.Join(slice, name), nil
  53. }
  54. }
  55. func NewSystemd(root string) (*SystemdController, error) {
  56. return &SystemdController{
  57. root: root,
  58. }, nil
  59. }
  60. type SystemdController struct {
  61. mu sync.Mutex
  62. root string
  63. }
  64. func (s *SystemdController) Name() Name {
  65. return SystemdDbus
  66. }
  67. func (s *SystemdController) Create(path string, resources *specs.LinuxResources) error {
  68. conn, err := systemdDbus.New()
  69. if err != nil {
  70. return err
  71. }
  72. defer conn.Close()
  73. slice, name := splitName(path)
  74. // We need to see if systemd can handle the delegate property
  75. // Systemd will return an error if it cannot handle delegate regardless
  76. // of its bool setting.
  77. checkDelegate := func() {
  78. canDelegate = true
  79. dlSlice := newProperty("Delegate", true)
  80. if _, err := conn.StartTransientUnit(slice, "testdelegate", []systemdDbus.Property{dlSlice}, nil); err != nil {
  81. if dbusError, ok := err.(dbus.Error); ok {
  82. // Starting with systemd v237, Delegate is not even a property of slices anymore,
  83. // so the D-Bus call fails with "InvalidArgs" error.
  84. if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") {
  85. canDelegate = false
  86. }
  87. }
  88. }
  89. conn.StopUnit(slice, "testDelegate", nil)
  90. }
  91. once.Do(checkDelegate)
  92. properties := []systemdDbus.Property{
  93. systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)),
  94. systemdDbus.PropWants(slice),
  95. newProperty("DefaultDependencies", false),
  96. newProperty("MemoryAccounting", true),
  97. newProperty("CPUAccounting", true),
  98. newProperty("BlockIOAccounting", true),
  99. }
  100. // If we can delegate, we add the property back in
  101. if canDelegate {
  102. properties = append(properties, newProperty("Delegate", true))
  103. }
  104. ch := make(chan string)
  105. _, err = conn.StartTransientUnit(name, "replace", properties, ch)
  106. if err != nil {
  107. return err
  108. }
  109. <-ch
  110. return nil
  111. }
  112. func (s *SystemdController) Delete(path string) error {
  113. conn, err := systemdDbus.New()
  114. if err != nil {
  115. return err
  116. }
  117. defer conn.Close()
  118. _, name := splitName(path)
  119. ch := make(chan string)
  120. _, err = conn.StopUnit(name, "replace", ch)
  121. if err != nil {
  122. return err
  123. }
  124. <-ch
  125. return nil
  126. }
  127. func newProperty(name string, units interface{}) systemdDbus.Property {
  128. return systemdDbus.Property{
  129. Name: name,
  130. Value: dbus.MakeVariant(units),
  131. }
  132. }
  133. func unitName(name string) string {
  134. return fmt.Sprintf("%s.slice", name)
  135. }
  136. func splitName(path string) (slice string, unit string) {
  137. slice, unit = filepath.Split(path)
  138. return strings.TrimSuffix(slice, "/"), unit
  139. }