github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/hostmm/cgroup.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     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  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package hostmm
    16  
    17  import (
    18  	"bufio"
    19  	"fmt"
    20  	"os"
    21  	"path"
    22  	"strings"
    23  )
    24  
    25  // currentCgroupDirectory returns the directory for the cgroup for the given
    26  // controller in which the calling process resides.
    27  func currentCgroupDirectory(ctrl string) (string, error) {
    28  	root, err := cgroupRootDirectory(ctrl)
    29  	if err != nil {
    30  		return "", err
    31  	}
    32  	cg, err := currentCgroup(ctrl)
    33  	if err != nil {
    34  		return "", err
    35  	}
    36  	return path.Join(root, cg), nil
    37  }
    38  
    39  // cgroupRootDirectory returns the root directory for the cgroup hierarchy in
    40  // which the given cgroup controller is mounted in the calling process' mount
    41  // namespace.
    42  func cgroupRootDirectory(ctrl string) (string, error) {
    43  	const path = "/proc/self/mounts"
    44  	file, err := os.Open(path)
    45  	if err != nil {
    46  		return "", err
    47  	}
    48  	defer file.Close()
    49  
    50  	// Per proc(5) -> fstab(5):
    51  	// Each line of /proc/self/mounts describes a mount.
    52  	scanner := bufio.NewScanner(file)
    53  	for scanner.Scan() {
    54  		// Each line consists of 6 space-separated fields. Find the line for
    55  		// which the third field (fs_vfstype) is cgroup, and the fourth field
    56  		// (fs_mntops, a comma-separated list of mount options) contains
    57  		// ctrl.
    58  		var spec, file, vfstype, mntopts, freq, passno string
    59  		const nrfields = 6
    60  		line := scanner.Text()
    61  		n, err := fmt.Sscan(line, &spec, &file, &vfstype, &mntopts, &freq, &passno)
    62  		if err != nil {
    63  			return "", fmt.Errorf("failed to parse %s: %v", path, err)
    64  		}
    65  		if n != nrfields {
    66  			return "", fmt.Errorf("failed to parse %s: line %q: got %d fields, wanted %d", path, line, n, nrfields)
    67  		}
    68  		if vfstype != "cgroup" {
    69  			continue
    70  		}
    71  		for _, mntopt := range strings.Split(mntopts, ",") {
    72  			if mntopt == ctrl {
    73  				return file, nil
    74  			}
    75  		}
    76  	}
    77  	return "", fmt.Errorf("no cgroup hierarchy mounted for controller %s", ctrl)
    78  }
    79  
    80  // currentCgroup returns the cgroup for the given controller in which the
    81  // calling process resides. The returned string is a path that should be
    82  // interpreted as relative to cgroupRootDirectory(ctrl).
    83  func currentCgroup(ctrl string) (string, error) {
    84  	const path = "/proc/self/cgroup"
    85  	file, err := os.Open(path)
    86  	if err != nil {
    87  		return "", err
    88  	}
    89  	defer file.Close()
    90  
    91  	// Per proc(5) -> cgroups(7):
    92  	// Each line of /proc/self/cgroups describes a cgroup hierarchy.
    93  	scanner := bufio.NewScanner(file)
    94  	for scanner.Scan() {
    95  		// Each line consists of 3 colon-separated fields. Find the line for
    96  		// which the second field (controller-list, a comma-separated list of
    97  		// cgroup controllers) contains ctrl.
    98  		line := scanner.Text()
    99  		const nrfields = 3
   100  		fields := strings.Split(line, ":")
   101  		if len(fields) != nrfields {
   102  			return "", fmt.Errorf("failed to parse %s: line %q: got %d fields, wanted %d", path, line, len(fields), nrfields)
   103  		}
   104  		for _, controller := range strings.Split(fields[1], ",") {
   105  			if controller == ctrl {
   106  				return fields[2], nil
   107  			}
   108  		}
   109  	}
   110  	return "", fmt.Errorf("not a member of a cgroup hierarchy for controller %s", ctrl)
   111  }