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 }