Files
runc/utils.go
T
Kir Kolyshkin e4b70657ec Add --cgroup-manager, deprecate --systemd-cgroup
Add a new --cgroup-manager global option, accepting either "cgroupfs"
(the default) or "systemd". It is compatible with the option of the
same name crun has (except crun also has "disabled").

This helps tools which run either runc or crun, for example, crun's test
suite, which can now be run against runc.

Deprecate --systemd-cgroup in favor of --cgroup-manager=systemd. It is
still supported (and there are no plans to remove it), but conflicts with
--cgroup-manager set to anything other than systemd.

Switch the integration tests to use the new option, and add a few tests
for the new and the deprecated option.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2026-10-27 23:00:22 -07:00

150 lines
3.5 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v3"
)
const (
exactArgs = iota
minArgs
maxArgs
)
func checkArgs(cmd *cli.Command, expected, checkType int) error {
var err error
cmdName := cmd.Name
switch checkType {
case exactArgs:
if cmd.NArg() != expected {
err = fmt.Errorf("%s: %q requires exactly %d argument(s)", os.Args[0], cmdName, expected)
}
case minArgs:
if cmd.NArg() < expected {
err = fmt.Errorf("%s: %q requires a minimum of %d argument(s)", os.Args[0], cmdName, expected)
}
case maxArgs:
if cmd.NArg() > expected {
err = fmt.Errorf("%s: %q requires a maximum of %d argument(s)", os.Args[0], cmdName, expected)
}
}
if err != nil {
fmt.Printf("Incorrect Usage.\n\n")
_ = cli.ShowCommandHelp(context.Background(), cmd, cmdName)
return err
}
return nil
}
func logrusToStderr() bool {
l, ok := logrus.StandardLogger().Out.(*os.File)
return ok && l.Fd() == os.Stderr.Fd()
}
// fatal prints the error's details if it is a libcontainer specific error type
// then exits the program with an exit status of 1.
func fatal(err error) {
fatalWithCode(err, 1)
}
func fatalWithCode(err error, ret int) {
// Make sure the error is written to the logger.
logrus.Error(err)
if !logrusToStderr() {
fmt.Fprintln(os.Stderr, err)
}
os.Exit(ret)
}
// setupSpec performs initial setup based on the cli.Context for the container
func setupSpec(cmd *cli.Command) (*specs.Spec, error) {
bundle := cmd.String("bundle")
if bundle != "" {
if err := os.Chdir(bundle); err != nil {
return nil, err
}
}
spec, err := loadSpec(specConfig)
if err != nil {
return nil, err
}
return spec, nil
}
func revisePidFile(cmd *cli.Command) error {
pidFile := cmd.String("pid-file")
if pidFile == "" {
return nil
}
// convert pid-file to an absolute path so we can write to the right
// file after chdir to bundle
pidFile, err := filepath.Abs(pidFile)
if err != nil {
return err
}
return cmd.Set("pid-file", pidFile)
}
// reviseRootDir ensures that the --root option argument,
// if specified, is converted to an absolute and cleaned path,
// and that this path is sane.
func reviseRootDir(cmd *cli.Command) error {
if !cmd.IsSet("root") {
return nil
}
root, err := filepath.Abs(cmd.String("root"))
if err != nil {
return err
}
if root == "/" {
// This can happen if --root argument is
// - "" (i.e. empty);
// - "." (and the CWD is /);
// - "../../.." (enough to get to /);
// - "/" (the actual /).
return errors.New("Option --root argument should not be set to /")
}
return cmd.Set("root", root)
}
// reviseCgroupManager checks the --cgroup-manager option argument, and
// converts the deprecated --systemd-cgroup option to --cgroup-manager=systemd.
func reviseCgroupManager(cmd *cli.Command) error {
manager := cmd.String("cgroup-manager")
switch manager {
case "cgroupfs", "systemd":
default:
return fmt.Errorf("invalid --cgroup-manager value %q (must be 'cgroupfs' or 'systemd')", manager)
}
if !cmd.Bool("systemd-cgroup") {
return nil
}
if cmd.IsSet("cgroup-manager") && manager != "systemd" {
return fmt.Errorf("--systemd-cgroup conflicts with --cgroup-manager=%s", manager)
}
return cmd.Set("cgroup-manager", "systemd")
}
// parseBoolOrAuto returns (nil, nil) if s is empty or "auto"
func parseBoolOrAuto(s string) (*bool, error) {
if s == "" || strings.ToLower(s) == "auto" {
return nil, nil
}
b, err := strconv.ParseBool(s)
return &b, err
}