diff options
Diffstat (limited to 'runsc/cmd/boot.go')
-rw-r--r-- | runsc/cmd/boot.go | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go new file mode 100644 index 000000000..0dad6da79 --- /dev/null +++ b/runsc/cmd/boot.go @@ -0,0 +1,161 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "os" + "runtime" + "runtime/debug" + "strings" + "syscall" + + "context" + "flag" + "github.com/google/subcommands" + specs "github.com/opencontainers/runtime-spec/specs-go" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "gvisor.googlesource.com/gvisor/runsc/specutils" +) + +// Boot implements subcommands.Command for the "boot" command which starts a +// new sandbox. It should not be called directly. +type Boot struct { + // bundleDir is the path to the bundle directory. + bundleDir string + + // controllerFD is the file descriptor of a stream socket for the + // control server that is donated to this process. + controllerFD int + + // ioFDs is the list of FDs used to connect to FS gofers. + ioFDs intFlags + + // console is set to true if the sandbox should allow terminal ioctl(2) + // syscalls. + console bool + + // applyCaps determines if capabilities defined in the spec should be applied + // to the process. + applyCaps bool +} + +// Name implements subcommands.Command.Name. +func (*Boot) Name() string { + return "boot" +} + +// Synopsis implements subcommands.Command.Synopsis. +func (*Boot) Synopsis() string { + return "launch a sandbox process (internal use only)" +} + +// Usage implements subcommands.Command.Usage. +func (*Boot) Usage() string { + return `boot [flags]` +} + +// SetFlags implements subcommands.Command.SetFlags. +func (b *Boot) SetFlags(f *flag.FlagSet) { + f.StringVar(&b.bundleDir, "bundle", "", "required path to the root of the bundle directory") + f.IntVar(&b.controllerFD, "controller-fd", -1, "required FD of a stream socket for the control server that must be donated to this process") + f.Var(&b.ioFDs, "io-fds", "list of FDs to connect 9P clients. They must follow this order: root first, then mounts as defined in the spec") + f.BoolVar(&b.console, "console", false, "set to true if the sandbox should allow terminal ioctl(2) syscalls") + f.BoolVar(&b.applyCaps, "apply-caps", false, "if true, apply capabilities defined in the spec to the process") +} + +// Execute implements subcommands.Command.Execute. It starts a sandbox in a +// waiting state. +func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + if b.bundleDir == "" || b.controllerFD == -1 || f.NArg() != 0 { + f.Usage() + return subcommands.ExitUsageError + } + + // Ensure that if there is a panic, all goroutine stacks are printed. + debug.SetTraceback("all") + + // Get the spec from the bundleDir. + spec, err := specutils.ReadSpec(b.bundleDir) + if err != nil { + Fatalf("error reading spec: %v", err) + } + specutils.LogSpec(spec) + + // Turn any relative paths in the spec to absolute by prepending the bundleDir. + spec.Root.Path = absPath(b.bundleDir, spec.Root.Path) + for _, m := range spec.Mounts { + if m.Source != "" { + m.Source = absPath(b.bundleDir, m.Source) + } + } + + conf := args[0].(*boot.Config) + waitStatus := args[1].(*syscall.WaitStatus) + + if b.applyCaps { + setCapsAndCallSelf(conf, spec) + Fatalf("setCapsAndCallSelf must never return") + } + + // Create the loader. + s, err := boot.New(spec, conf, b.controllerFD, b.ioFDs.GetArray(), b.console) + if err != nil { + Fatalf("error creating loader: %v", err) + } + defer s.Destroy() + + // Wait for the start signal from runsc. + s.WaitForStartSignal() + + // Run the application and wait for it to finish. + if err := s.Run(); err != nil { + Fatalf("error running sandbox: %v", err) + } + + ws := s.WaitExit() + log.Infof("application exiting with %+v", ws) + *waitStatus = syscall.WaitStatus(ws.Status()) + return subcommands.ExitSuccess +} + +// setCapsAndCallSelf sets capabilities to the current thread and then execve's +// itself again with the same arguments except '--apply-caps' to restart the +// whole process with the desired capabilities. +func setCapsAndCallSelf(conf *boot.Config, spec *specs.Spec) { + // Keep thread locked while capabilities are changed. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if err := boot.ApplyCaps(conf, spec.Process.Capabilities); err != nil { + Fatalf("ApplyCaps, err: %v", err) + } + binPath, err := specutils.BinPath() + if err != nil { + Fatalf("%v", err) + } + + // Remove --apply-caps arg to call myself. + var args []string + for _, arg := range os.Args { + if !strings.Contains(arg, "apply-caps") { + args = append(args, arg) + } + } + + log.Infof("Execve 'boot' again, bye!") + log.Infof("%s %v", binPath, args) + syscall.Exec(binPath, args, []string{}) +} |