summaryrefslogtreecommitdiff
path: root/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go')
-rw-r--r--vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go127
1 files changed, 127 insertions, 0 deletions
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go
new file mode 100644
index 000000000..8917a6411
--- /dev/null
+++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go
@@ -0,0 +1,127 @@
+package fs2
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+ "time"
+
+ "golang.org/x/sys/unix"
+
+ "github.com/opencontainers/runc/libcontainer/cgroups"
+ "github.com/opencontainers/runc/libcontainer/configs"
+)
+
+func setFreezer(dirPath string, state configs.FreezerState) error {
+ var stateStr string
+ switch state {
+ case configs.Undefined:
+ return nil
+ case configs.Frozen:
+ stateStr = "1"
+ case configs.Thawed:
+ stateStr = "0"
+ default:
+ return fmt.Errorf("invalid freezer state %q requested", state)
+ }
+
+ fd, err := cgroups.OpenFile(dirPath, "cgroup.freeze", unix.O_RDWR)
+ if err != nil {
+ // We can ignore this request as long as the user didn't ask us to
+ // freeze the container (since without the freezer cgroup, that's a
+ // no-op).
+ if state != configs.Frozen {
+ return nil
+ }
+ return fmt.Errorf("freezer not supported: %w", err)
+ }
+ defer fd.Close()
+
+ if _, err := fd.WriteString(stateStr); err != nil {
+ return err
+ }
+ // Confirm that the cgroup did actually change states.
+ if actualState, err := readFreezer(dirPath, fd); err != nil {
+ return err
+ } else if actualState != state {
+ return fmt.Errorf(`expected "cgroup.freeze" to be in state %q but was in %q`, state, actualState)
+ }
+ return nil
+}
+
+func getFreezer(dirPath string) (configs.FreezerState, error) {
+ fd, err := cgroups.OpenFile(dirPath, "cgroup.freeze", unix.O_RDONLY)
+ if err != nil {
+ // If the kernel is too old, then we just treat the freezer as being in
+ // an "undefined" state.
+ if os.IsNotExist(err) || errors.Is(err, unix.ENODEV) {
+ err = nil
+ }
+ return configs.Undefined, err
+ }
+ defer fd.Close()
+
+ return readFreezer(dirPath, fd)
+}
+
+func readFreezer(dirPath string, fd *os.File) (configs.FreezerState, error) {
+ if _, err := fd.Seek(0, 0); err != nil {
+ return configs.Undefined, err
+ }
+ state := make([]byte, 2)
+ if _, err := fd.Read(state); err != nil {
+ return configs.Undefined, err
+ }
+ switch string(state) {
+ case "0\n":
+ return configs.Thawed, nil
+ case "1\n":
+ return waitFrozen(dirPath)
+ default:
+ return configs.Undefined, fmt.Errorf(`unknown "cgroup.freeze" state: %q`, state)
+ }
+}
+
+// waitFrozen polls cgroup.events until it sees "frozen 1" in it.
+func waitFrozen(dirPath string) (configs.FreezerState, error) {
+ fd, err := cgroups.OpenFile(dirPath, "cgroup.events", unix.O_RDONLY)
+ if err != nil {
+ return configs.Undefined, err
+ }
+ defer fd.Close()
+
+ // XXX: Simple wait/read/retry is used here. An implementation
+ // based on poll(2) or inotify(7) is possible, but it makes the code
+ // much more complicated. Maybe address this later.
+ const (
+ // Perform maxIter with waitTime in between iterations.
+ waitTime = 10 * time.Millisecond
+ maxIter = 1000
+ )
+ scanner := bufio.NewScanner(fd)
+ for i := 0; scanner.Scan(); {
+ if i == maxIter {
+ return configs.Undefined, fmt.Errorf("timeout of %s reached waiting for the cgroup to freeze", waitTime*maxIter)
+ }
+ line := scanner.Text()
+ val := strings.TrimPrefix(line, "frozen ")
+ if val != line { // got prefix
+ if val[0] == '1' {
+ return configs.Frozen, nil
+ }
+
+ i++
+ // wait, then re-read
+ time.Sleep(waitTime)
+ _, err := fd.Seek(0, 0)
+ if err != nil {
+ return configs.Undefined, err
+ }
+ }
+ }
+ // Should only reach here either on read error,
+ // or if the file does not contain "frozen " line.
+ return configs.Undefined, scanner.Err()
+}