github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/main.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  
    13  	//nolint:revive // Enable cgroup manager to manage devices
    14  	_ "github.com/opencontainers/runc/libcontainer/cgroups/devices"
    15  	"github.com/opencontainers/runc/libcontainer/seccomp"
    16  	"github.com/opencontainers/runtime-spec/specs-go"
    17  
    18  	"github.com/sirupsen/logrus"
    19  	"github.com/urfave/cli"
    20  )
    21  
    22  // version must be set from the contents of VERSION file by go build's
    23  // -X main.version= option in the Makefile.
    24  var version = "unknown"
    25  
    26  // gitCommit will be the hash that the binary was built from
    27  // and will be populated by the Makefile
    28  var gitCommit = ""
    29  
    30  const (
    31  	specConfig = "config.json"
    32  	usage      = `Open Container Initiative runtime
    33  
    34  runc is a command line client for running applications packaged according to
    35  the Open Container Initiative (OCI) format and is a compliant implementation of the
    36  Open Container Initiative specification.
    37  
    38  runc integrates well with existing process supervisors to provide a production
    39  container runtime environment for applications. It can be used with your
    40  existing process monitoring tools and the container will be spawned as a
    41  direct child of the process supervisor.
    42  
    43  Containers are configured using bundles. A bundle for a container is a directory
    44  that includes a specification file named "` + specConfig + `" and a root filesystem.
    45  The root filesystem contains the contents of the container.
    46  
    47  To start a new instance of a container:
    48  
    49      # runc run [ -b bundle ] <container-id>
    50  
    51  Where "<container-id>" is your name for the instance of the container that you
    52  are starting. The name you provide for the container instance must be unique on
    53  your host. Providing the bundle directory using "-b" is optional. The default
    54  value for "bundle" is the current directory.`
    55  )
    56  
    57  func main() {
    58  	app := cli.NewApp()
    59  	app.Name = "runc"
    60  	app.Usage = usage
    61  
    62  	v := []string{version}
    63  
    64  	if gitCommit != "" {
    65  		v = append(v, "commit: "+gitCommit)
    66  	}
    67  	v = append(v, "spec: "+specs.Version)
    68  	v = append(v, "go: "+runtime.Version())
    69  
    70  	major, minor, micro := seccomp.Version()
    71  	if major+minor+micro > 0 {
    72  		v = append(v, fmt.Sprintf("libseccomp: %d.%d.%d", major, minor, micro))
    73  	}
    74  	app.Version = strings.Join(v, "\n")
    75  
    76  	root := "/run/runc"
    77  	xdgDirUsed := false
    78  	xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
    79  	if xdgRuntimeDir != "" && shouldHonorXDGRuntimeDir() {
    80  		root = xdgRuntimeDir + "/runc"
    81  		xdgDirUsed = true
    82  	}
    83  
    84  	app.Flags = []cli.Flag{
    85  		cli.BoolFlag{
    86  			Name:  "debug",
    87  			Usage: "enable debug logging",
    88  		},
    89  		cli.StringFlag{
    90  			Name:  "log",
    91  			Value: "",
    92  			Usage: "set the log file to write runc logs to (default is '/dev/stderr')",
    93  		},
    94  		cli.StringFlag{
    95  			Name:  "log-format",
    96  			Value: "text",
    97  			Usage: "set the log format ('text' (default), or 'json')",
    98  		},
    99  		cli.StringFlag{
   100  			Name:  "root",
   101  			Value: root,
   102  			Usage: "root directory for storage of container state (this should be located in tmpfs)",
   103  		},
   104  		cli.StringFlag{
   105  			Name:   "criu",
   106  			Usage:  "(obsoleted; do not use)",
   107  			Hidden: true,
   108  		},
   109  		cli.BoolFlag{
   110  			Name:  "systemd-cgroup",
   111  			Usage: "enable systemd cgroup support, expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"",
   112  		},
   113  		cli.StringFlag{
   114  			Name:  "rootless",
   115  			Value: "auto",
   116  			Usage: "ignore cgroup permission errors ('true', 'false', or 'auto')",
   117  		},
   118  	}
   119  	app.Commands = []cli.Command{
   120  		checkpointCommand,
   121  		createCommand,
   122  		deleteCommand,
   123  		eventsCommand,
   124  		execCommand,
   125  		killCommand,
   126  		listCommand,
   127  		pauseCommand,
   128  		psCommand,
   129  		restoreCommand,
   130  		resumeCommand,
   131  		runCommand,
   132  		specCommand,
   133  		startCommand,
   134  		stateCommand,
   135  		updateCommand,
   136  		featuresCommand,
   137  	}
   138  	app.Before = func(context *cli.Context) error {
   139  		if !context.IsSet("root") && xdgDirUsed {
   140  			// According to the XDG specification, we need to set anything in
   141  			// XDG_RUNTIME_DIR to have a sticky bit if we don't want it to get
   142  			// auto-pruned.
   143  			if err := os.MkdirAll(root, 0o700); err != nil {
   144  				fmt.Fprintln(os.Stderr, "the path in $XDG_RUNTIME_DIR must be writable by the user")
   145  				fatal(err)
   146  			}
   147  			if err := os.Chmod(root, os.FileMode(0o700)|os.ModeSticky); err != nil {
   148  				fmt.Fprintln(os.Stderr, "you should check permission of the path in $XDG_RUNTIME_DIR")
   149  				fatal(err)
   150  			}
   151  		}
   152  		if err := reviseRootDir(context); err != nil {
   153  			return err
   154  		}
   155  		// TODO: remove this in runc 1.3.0.
   156  		if context.IsSet("criu") {
   157  			fmt.Fprintln(os.Stderr, "WARNING: --criu ignored (criu binary from $PATH is used); do not use")
   158  		}
   159  
   160  		return configLogrus(context)
   161  	}
   162  
   163  	// If the command returns an error, cli takes upon itself to print
   164  	// the error on cli.ErrWriter and exit.
   165  	// Use our own writer here to ensure the log gets sent to the right location.
   166  	cli.ErrWriter = &FatalWriter{cli.ErrWriter}
   167  	if err := app.Run(os.Args); err != nil {
   168  		fatal(err)
   169  	}
   170  }
   171  
   172  type FatalWriter struct {
   173  	cliErrWriter io.Writer
   174  }
   175  
   176  func (f *FatalWriter) Write(p []byte) (n int, err error) {
   177  	logrus.Error(string(p))
   178  	if !logrusToStderr() {
   179  		return f.cliErrWriter.Write(p)
   180  	}
   181  	return len(p), nil
   182  }
   183  
   184  func configLogrus(context *cli.Context) error {
   185  	if context.GlobalBool("debug") {
   186  		logrus.SetLevel(logrus.DebugLevel)
   187  		logrus.SetReportCaller(true)
   188  		// Shorten function and file names reported by the logger, by
   189  		// trimming common "github.com/opencontainers/runc" prefix.
   190  		// This is only done for text formatter.
   191  		_, file, _, _ := runtime.Caller(0)
   192  		prefix := filepath.Dir(file) + "/"
   193  		logrus.SetFormatter(&logrus.TextFormatter{
   194  			CallerPrettyfier: func(f *runtime.Frame) (string, string) {
   195  				function := strings.TrimPrefix(f.Function, prefix) + "()"
   196  				fileLine := strings.TrimPrefix(f.File, prefix) + ":" + strconv.Itoa(f.Line)
   197  				return function, fileLine
   198  			},
   199  		})
   200  	}
   201  
   202  	switch f := context.GlobalString("log-format"); f {
   203  	case "":
   204  		// do nothing
   205  	case "text":
   206  		// do nothing
   207  	case "json":
   208  		logrus.SetFormatter(new(logrus.JSONFormatter))
   209  	default:
   210  		return errors.New("invalid log-format: " + f)
   211  	}
   212  
   213  	if file := context.GlobalString("log"); file != "" {
   214  		f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0o644)
   215  		if err != nil {
   216  			return err
   217  		}
   218  		logrus.SetOutput(f)
   219  	}
   220  
   221  	return nil
   222  }