systemd.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 cgroup1
  14. import (
  15. "context"
  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 Name = "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 = string(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. root string
  62. }
  63. func (s *SystemdController) Name() Name {
  64. return SystemdDbus
  65. }
  66. func (s *SystemdController) Create(path string, _ *specs.LinuxResources) error {
  67. ctx := context.TODO()
  68. conn, err := systemdDbus.NewWithContext(ctx)
  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.StartTransientUnitContext(ctx, 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.StopUnitContext(ctx, slice, "testDelegate", nil)
  90. }
  91. once.Do(checkDelegate)
  92. properties := []systemdDbus.Property{
  93. systemdDbus.PropDescription("cgroup " + 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.StartTransientUnitContext(ctx, 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. ctx := context.TODO()
  114. conn, err := systemdDbus.NewWithContext(ctx)
  115. if err != nil {
  116. return err
  117. }
  118. defer conn.Close()
  119. _, name := splitName(path)
  120. ch := make(chan string)
  121. _, err = conn.StopUnitContext(ctx, name, "replace", ch)
  122. if err != nil {
  123. return err
  124. }
  125. <-ch
  126. return nil
  127. }
  128. func newProperty(name string, units interface{}) systemdDbus.Property {
  129. return systemdDbus.Property{
  130. Name: name,
  131. Value: dbus.MakeVariant(units),
  132. }
  133. }
  134. func splitName(path string) (slice string, unit string) {
  135. slice, unit = filepath.Split(path)
  136. return strings.TrimSuffix(slice, "/"), unit
  137. }