github.com/rigado/snapd@v2.42.5-go-mod+incompatible/sandbox/cgroup/cgroup.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package cgroup
    21  
    22  import (
    23  	"fmt"
    24  	"path/filepath"
    25  	"syscall"
    26  
    27  	"github.com/snapcore/snapd/dirs"
    28  )
    29  
    30  const (
    31  	// From golang.org/x/sys/unix
    32  	cgroup2SuperMagic = 0x63677270
    33  
    34  	// The only cgroup path we expect, for v2 this is where the unified
    35  	// hierarchy is mounted, for v1 this is usually a tmpfs mount, under
    36  	// which the controller-hierarchies are mounted
    37  	expectedMountPoint = "/sys/fs/cgroup"
    38  )
    39  
    40  const (
    41  	// Separate block, because iota is fun
    42  	Unknown = iota
    43  	V1
    44  	V2
    45  )
    46  
    47  var (
    48  	probeVersion       = Unknown
    49  	probeErr     error = nil
    50  )
    51  
    52  func init() {
    53  	probeVersion, probeErr = probeCgroupVersion()
    54  }
    55  
    56  var fsTypeForPath = fsTypeForPathImpl
    57  
    58  func fsTypeForPathImpl(path string) (int64, error) {
    59  	var statfs syscall.Statfs_t
    60  	if err := syscall.Statfs(path, &statfs); err != nil {
    61  		return 0, fmt.Errorf("cannot statfs path: %v", err)
    62  	}
    63  	// Typs is int32 on 386, use explicit conversion to keep the code
    64  	// working for both
    65  	return int64(statfs.Type), nil
    66  }
    67  
    68  // ProcPidPath returns the path to the cgroup file under /proc for the given
    69  // process id.
    70  func ProcPidPath(pid int) string {
    71  	return filepath.Join(dirs.GlobalRootDir, fmt.Sprintf("proc/%v/cgroup", pid))
    72  }
    73  
    74  // ControllerPathV1 returns the path to given controller assuming cgroup v1
    75  // hierarchy
    76  func ControllerPathV1(controller string) string {
    77  	return filepath.Join(dirs.GlobalRootDir, expectedMountPoint, controller)
    78  }
    79  
    80  func probeCgroupVersion() (version int, err error) {
    81  	cgroupMount := filepath.Join(dirs.GlobalRootDir, expectedMountPoint)
    82  	typ, err := fsTypeForPath(cgroupMount)
    83  	if err != nil {
    84  		return Unknown, fmt.Errorf("cannot determine filesystem type: %v", err)
    85  	}
    86  	if typ == cgroup2SuperMagic {
    87  		return V2, nil
    88  	}
    89  	return V1, nil
    90  }
    91  
    92  // IsUnified returns true when a unified cgroup hierarchy is in use
    93  func IsUnified() bool {
    94  	version, _ := Version()
    95  	return version == V2
    96  }
    97  
    98  // Version returns the detected cgroup version
    99  func Version() (int, error) {
   100  	return probeVersion, probeErr
   101  }
   102  
   103  // MockVersion sets the reported version of cgroup support. For use in testing only
   104  func MockVersion(mockVersion int, mockErr error) (restore func()) {
   105  	oldVersion, oldErr := probeVersion, probeErr
   106  	probeVersion, probeErr = mockVersion, mockErr
   107  	return func() {
   108  		probeVersion, probeErr = oldVersion, oldErr
   109  	}
   110  }