diff options
-rw-r--r-- | runsc/boot/BUILD | 6 | ||||
-rw-r--r-- | runsc/boot/compat.go | 76 | ||||
-rw-r--r-- | runsc/boot/loader.go | 6 | ||||
-rw-r--r-- | runsc/cmd/boot.go | 5 | ||||
-rw-r--r-- | runsc/cmd/capability_test.go | 2 | ||||
-rw-r--r-- | runsc/cmd/checkpoint.go | 2 | ||||
-rw-r--r-- | runsc/cmd/create.go | 10 | ||||
-rw-r--r-- | runsc/cmd/run.go | 2 | ||||
-rw-r--r-- | runsc/container/container.go | 8 | ||||
-rw-r--r-- | runsc/container/container_test.go | 93 | ||||
-rw-r--r-- | runsc/container/multi_container_test.go | 2 | ||||
-rw-r--r-- | runsc/container/test_app.go | 36 | ||||
-rw-r--r-- | runsc/sandbox/sandbox.go | 18 |
13 files changed, 223 insertions, 43 deletions
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index c1e035d3b..f8f848ebf 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "boot", srcs = [ + "compat.go", "config.go", "controller.go", "debug.go", @@ -21,12 +22,15 @@ go_library( "//runsc:__subpackages__", ], deps = [ + "//pkg/abi", "//pkg/abi/linux", "//pkg/control/server", "//pkg/cpuid", + "//pkg/eventchannel", "//pkg/log", "//pkg/rand", "//pkg/sentry/arch", + "//pkg/sentry/arch:registers_go_proto", "//pkg/sentry/context", "//pkg/sentry/control", "//pkg/sentry/fs", @@ -55,6 +59,7 @@ go_library( "//pkg/sentry/socket/unix", "//pkg/sentry/state", "//pkg/sentry/strace", + "//pkg/sentry/syscalls:unimplemented_syscall_go_proto", "//pkg/sentry/syscalls/linux", "//pkg/sentry/time", "//pkg/sentry/usage", @@ -74,6 +79,7 @@ go_library( "//pkg/urpc", "//runsc/boot/filter", "//runsc/specutils", + "@com_github_golang_protobuf//proto:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", ], ) diff --git a/runsc/boot/compat.go b/runsc/boot/compat.go new file mode 100644 index 000000000..3250cdcdc --- /dev/null +++ b/runsc/boot/compat.go @@ -0,0 +1,76 @@ +// 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 boot + +import ( + "fmt" + "os" + + "github.com/golang/protobuf/proto" + "gvisor.googlesource.com/gvisor/pkg/abi" + "gvisor.googlesource.com/gvisor/pkg/eventchannel" + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + rpb "gvisor.googlesource.com/gvisor/pkg/sentry/arch/registers_go_proto" + "gvisor.googlesource.com/gvisor/pkg/sentry/strace" + spb "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/unimplemented_syscall_go_proto" +) + +func initCompatLogs(fd int) error { + ce, err := newCompatEmitter(fd) + if err != nil { + return err + } + eventchannel.AddEmitter(ce) + return nil +} + +type compatEmitter struct { + sink *log.BasicLogger + nameMap strace.SyscallMap +} + +func newCompatEmitter(logFD int) (*compatEmitter, error) { + // Always logs to default logger. + nameMap, ok := strace.Lookup(abi.Linux, arch.AMD64) + if !ok { + return nil, fmt.Errorf("amd64 Linux syscall table not found") + } + c := &compatEmitter{sink: log.Log(), nameMap: nameMap} + + if logFD > 0 { + f := os.NewFile(uintptr(logFD), "user log file") + target := log.MultiEmitter{c.sink, log.GoogleEmitter{&log.Writer{Next: f}}} + c.sink = &log.BasicLogger{Level: log.Info, Emitter: target} + } + return c, nil +} + +// Emit implements eventchannel.Emitter. +func (c *compatEmitter) Emit(msg proto.Message) (hangup bool, err error) { + // Only interested in UnimplementedSyscall, skip the rest. + if us, ok := msg.(*spb.UnimplementedSyscall); ok { + regs := us.Registers.GetArch().(*rpb.Registers_Amd64).Amd64 + sysnr := regs.OrigRax + c.sink.Infof("Unsupported syscall: %s, regs: %+v", c.nameMap.Name(uintptr(sysnr)), regs) + } + return false, nil +} + +// Close implements eventchannel.Emitter. +func (c *compatEmitter) Close() error { + c.sink = nil + return nil +} diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 60b278295..0a3f67774 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -163,6 +163,8 @@ type Args struct { // TotalMem is the initial amount of total memory to report back to the // container. TotalMem uint64 + // UserLogFD is the file descriptor to write user logs to. + UserLogFD int } // New initializes a new kernel loader configured by spec. @@ -313,6 +315,10 @@ func New(args Args) (*Loader, error) { return nil, fmt.Errorf("failed to create root process: %v", err) } + if err := initCompatLogs(args.UserLogFD); err != nil { + return nil, fmt.Errorf("init compat logs: %v", err) + } + l := &Loader{ k: k, ctrl: ctrl, diff --git a/runsc/cmd/boot.go b/runsc/cmd/boot.go index d26e92bcd..023b63dc0 100644 --- a/runsc/cmd/boot.go +++ b/runsc/cmd/boot.go @@ -66,6 +66,9 @@ type Boot struct { // totalMem sets the initial amount of total memory to report back to the // container. totalMem uint64 + + // userLogFD is the file descriptor to write user logs to. + userLogFD int } // Name implements subcommands.Command.Name. @@ -95,6 +98,7 @@ func (b *Boot) SetFlags(f *flag.FlagSet) { f.BoolVar(&b.applyCaps, "apply-caps", false, "if true, apply capabilities defined in the spec to the process") f.IntVar(&b.cpuNum, "cpu-num", 0, "number of CPUs to create inside the sandbox") f.Uint64Var(&b.totalMem, "total-memory", 0, "sets the initial amount of total memory to report back to the container") + f.IntVar(&b.userLogFD, "user-log-fd", 0, "file descriptor to write user logs to. 0 means no logging.") } // Execute implements subcommands.Command.Execute. It starts a sandbox in a @@ -163,6 +167,7 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) Console: b.console, NumCPU: b.cpuNum, TotalMem: b.totalMem, + UserLogFD: b.userLogFD, } l, err := boot.New(bootArgs) if err != nil { diff --git a/runsc/cmd/capability_test.go b/runsc/cmd/capability_test.go index be9ef2e7b..3329b308d 100644 --- a/runsc/cmd/capability_test.go +++ b/runsc/cmd/capability_test.go @@ -97,7 +97,7 @@ func TestCapabilities(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + c, err := container.Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } diff --git a/runsc/cmd/checkpoint.go b/runsc/cmd/checkpoint.go index d074b8617..023ab2455 100644 --- a/runsc/cmd/checkpoint.go +++ b/runsc/cmd/checkpoint.go @@ -133,7 +133,7 @@ func (c *Checkpoint) Execute(_ context.Context, f *flag.FlagSet, args ...interfa Fatalf("error destroying container: %v", err) } - cont, err = container.Create(id, spec, conf, bundleDir, "", "") + cont, err = container.Create(id, spec, conf, bundleDir, "", "", "") if err != nil { Fatalf("error restoring container: %v", err) } diff --git a/runsc/cmd/create.go b/runsc/cmd/create.go index 38ae03e7a..ecd76ee93 100644 --- a/runsc/cmd/create.go +++ b/runsc/cmd/create.go @@ -41,6 +41,13 @@ type Create struct { // pseudoterminal. This is ignored unless spec.Process.Terminal is // true. consoleSocket string + + // userLog is the path to send user-visible logs to. This log is different + // from debug logs. The former is meant to be consumed by the users and should + // contain only information that is relevant to the person running the + // container, e.g. unsuported syscalls, while the later is more verbose and + // consumed by developers. + userLog string } // Name implements subcommands.Command.Name. @@ -64,6 +71,7 @@ func (c *Create) SetFlags(f *flag.FlagSet) { f.StringVar(&c.bundleDir, "bundle", "", "path to the root of the bundle directory, defaults to the current directory") f.StringVar(&c.consoleSocket, "console-socket", "", "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal") f.StringVar(&c.pidFile, "pid-file", "", "filename that the container pid will be written to") + f.StringVar(&c.userLog, "user-log", "", "filename to send user-visible logs to. Empty means no logging.") } // Execute implements subcommands.Command.Execute. @@ -90,7 +98,7 @@ func (c *Create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{} // Create the container. A new sandbox will be created for the // container unless the metadata specifies that it should be run in an // existing container. - if _, err := container.Create(id, spec, conf, bundleDir, c.consoleSocket, c.pidFile); err != nil { + if _, err := container.Create(id, spec, conf, bundleDir, c.consoleSocket, c.pidFile, c.userLog); err != nil { Fatalf("error creating container: %v", err) } return subcommands.ExitSuccess diff --git a/runsc/cmd/run.go b/runsc/cmd/run.go index 92aa6bc40..826e6e875 100644 --- a/runsc/cmd/run.go +++ b/runsc/cmd/run.go @@ -75,7 +75,7 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) s specutils.LogSpec(spec) conf.SpecFile = filepath.Join(bundleDir, "config.json") - ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile) + ws, err := container.Run(id, spec, conf, bundleDir, r.consoleSocket, r.pidFile, r.userLog) if err != nil { Fatalf("error running container: %v", err) } diff --git a/runsc/container/container.go b/runsc/container/container.go index eaa62daf1..10108db5a 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -230,7 +230,7 @@ func List(rootDir string) ([]string, error) { // Create creates the container in a new Sandbox process, unless the metadata // indicates that an existing Sandbox should be used. The caller must call // Destroy() on the container. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string) (*Container, error) { +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile, userLog string) (*Container, error) { log.Debugf("Create container %q in root dir: %s", id, conf.RootDir) if err := validateID(id); err != nil { return nil, err @@ -278,7 +278,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo // Start a new sandbox for this container. Any errors after this point // must destroy the container. - c.Sandbox, err = sandbox.Create(id, spec, conf, bundleDir, consoleSocket, ioFiles) + c.Sandbox, err = sandbox.Create(id, spec, conf, bundleDir, consoleSocket, userLog, ioFiles) if err != nil { return nil, err } @@ -396,9 +396,9 @@ func (c *Container) Restore(spec *specs.Spec, conf *boot.Config, restoreFile str } // Run is a helper that calls Create + Start + Wait. -func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile string) (syscall.WaitStatus, error) { +func Run(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, pidFile, userLog string) (syscall.WaitStatus, error) { log.Debugf("Run container %q in root dir: %s", id, conf.RootDir) - c, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile) + c, err := Create(id, spec, conf, bundleDir, consoleSocket, pidFile, userLog) if err != nil { return 0, fmt.Errorf("error creating container: %v", err) } diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 84b59ffd8..7ea99d06b 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -210,18 +210,9 @@ func run(spec *specs.Spec, conf *boot.Config) error { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { - return fmt.Errorf("error creating container: %v", err) - } - defer c.Destroy() - if err := c.Start(conf); err != nil { - return fmt.Errorf("error starting container: %v", err) - } - - ws, err := c.Wait() - if err != nil { - return fmt.Errorf("error waiting on container: %v", err) + return fmt.Errorf("running container: %v", err) } if !ws.Exited() || ws.ExitStatus() != 0 { return fmt.Errorf("container failed, waitStatus: %v", ws) @@ -299,7 +290,7 @@ func TestLifecycle(t *testing.T) { } // Create the container. id := testutil.UniqueContainerID() - c, err := Create(id, spec, conf, bundleDir, "", "") + c, err := Create(id, spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -420,7 +411,7 @@ func TestExePath(t *testing.T) { t.Fatalf("exec: %s, error setting up container: %v", test.path, err) } - ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") os.RemoveAll(rootDir) os.RemoveAll(bundleDir) @@ -453,7 +444,7 @@ func TestAppExitStatus(t *testing.T) { defer os.RemoveAll(rootDir) defer os.RemoveAll(bundleDir) - ws, err := Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir, "", "") + ws, err := Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error running container: %v", err) } @@ -472,7 +463,7 @@ func TestAppExitStatus(t *testing.T) { defer os.RemoveAll(rootDir2) defer os.RemoveAll(bundleDir2) - ws, err = Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir2, "", "") + ws, err = Run(testutil.UniqueContainerID(), succSpec, conf, bundleDir2, "", "", "") if err != nil { t.Fatalf("error running container: %v", err) } @@ -497,7 +488,7 @@ func TestExec(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -603,7 +594,7 @@ func TestCheckpointRestore(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -649,7 +640,7 @@ func TestCheckpointRestore(t *testing.T) { defer outputFile2.Close() // Restore into a new container. - cont2, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont2, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -688,7 +679,7 @@ func TestCheckpointRestore(t *testing.T) { defer outputFile3.Close() // Restore into a new container. - cont3, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont3, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -767,7 +758,7 @@ func TestUnixDomainSockets(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -814,7 +805,7 @@ func TestUnixDomainSockets(t *testing.T) { defer outputFile2.Close() // Restore into a new container. - contRestore, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + contRestore, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -868,7 +859,7 @@ func TestPauseResume(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -973,7 +964,7 @@ func TestPauseResumeStatus(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1037,7 +1028,7 @@ func TestCapabilities(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + cont, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1138,7 +1129,7 @@ func TestConsoleSocket(t *testing.T) { // Create the container and pass the socket name. id := testutil.UniqueContainerID() - c, err := Create(id, spec, conf, bundleDir, socketRelPath, "") + c, err := Create(id, spec, conf, bundleDir, socketRelPath, "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1262,7 +1253,7 @@ func TestReadonlyRoot(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1306,7 +1297,7 @@ func TestReadonlyMount(t *testing.T) { defer os.RemoveAll(bundleDir) // Create, start and wait for the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1349,7 +1340,7 @@ func TestAbbreviatedIDs(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - cont, err := Create(cid, spec, conf, bundleDir, "", "") + cont, err := Create(cid, spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1409,7 +1400,7 @@ func TestContainerVolumeContentsShared(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1531,7 +1522,7 @@ func TestGoferExits(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1591,7 +1582,7 @@ func TestJobControlSignalExec(t *testing.T) { defer os.RemoveAll(bundleDir) // Create and start the container. - c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "") + c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "") if err != nil { t.Fatalf("error creating container: %v", err) } @@ -1694,6 +1685,46 @@ func TestJobControlSignalExec(t *testing.T) { } } +func TestUserLog(t *testing.T) { + app, err := testutil.FindFile("runsc/container/test_app") + if err != nil { + t.Fatal("error finding test_app:", err) + } + + // sched_rr_get_interval = 148 - not implemented in gvisor. + spec := testutil.NewSpecWithArgs(app, "syscall", "--syscall=148") + conf := testutil.TestConfig() + rootDir, bundleDir, err := testutil.SetupContainer(spec, conf) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + dir, err := ioutil.TempDir(testutil.TmpDir(), "user_log_test") + if err != nil { + t.Fatalf("error creating tmp dir: %v", err) + } + userLog := filepath.Join(dir, "user.log") + + // Create, start and wait for the container. + ws, err := Run(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", userLog) + if err != nil { + t.Fatalf("error running container: %v", err) + } + if !ws.Exited() || ws.ExitStatus() != 0 { + t.Fatalf("container failed, waitStatus: %v", ws) + } + + out, err := ioutil.ReadFile(userLog) + if err != nil { + t.Fatalf("error opening user log file %q: %v", userLog, err) + } + if want := "Unsupported syscall: sched_rr_get_interval"; !strings.Contains(string(out), want) { + t.Errorf("user log file doesn't contain %q, out: %s", want, string(out)) + } +} + // executeSync synchronously executes a new process. func (cont *Container) executeSync(args *control.ExecArgs) (syscall.WaitStatus, error) { pid, err := cont.Execute(args) diff --git a/runsc/container/multi_container_test.go b/runsc/container/multi_container_test.go index d23d36c37..77f8da8b0 100644 --- a/runsc/container/multi_container_test.go +++ b/runsc/container/multi_container_test.go @@ -83,7 +83,7 @@ func startContainers(conf *boot.Config, specs []*specs.Spec, ids []string) ([]*C } bundles = append(bundles, bundleDir) - cont, err := Create(ids[i], spec, conf, bundleDir, "", "") + cont, err := Create(ids[i], spec, conf, bundleDir, "", "", "") if err != nil { cleanup() return nil, nil, fmt.Errorf("error creating container: %v", err) diff --git a/runsc/container/test_app.go b/runsc/container/test_app.go index f69cfdf83..9e4b5326d 100644 --- a/runsc/container/test_app.go +++ b/runsc/container/test_app.go @@ -24,6 +24,7 @@ import ( "os" "os/exec" "strconv" + sys "syscall" "time" "flag" @@ -38,6 +39,7 @@ func main() { subcommands.Register(new(taskTree), "") subcommands.Register(new(forkBomb), "") subcommands.Register(new(reaper), "") + subcommands.Register(new(syscall), "") flag.Parse() @@ -241,3 +243,37 @@ func (c *reaper) Execute(ctx context.Context, f *flag.FlagSet, args ...interface defer stop() select {} } + +type syscall struct { + sysno uint64 +} + +// Name implements subcommands.Command. +func (*syscall) Name() string { + return "syscall" +} + +// Synopsis implements subcommands.Command. +func (*syscall) Synopsis() string { + return "syscall makes a syscall" +} + +// Usage implements subcommands.Command. +func (*syscall) Usage() string { + return "syscall <flags>" +} + +// SetFlags implements subcommands.Command. +func (s *syscall) SetFlags(f *flag.FlagSet) { + f.Uint64Var(&s.sysno, "syscall", 0, "syscall to call") +} + +// Execute implements subcommands.Command. +func (s *syscall) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + if _, _, errno := sys.Syscall(uintptr(s.sysno), 0, 0, 0); errno != 0 { + fmt.Printf("syscall(%d, 0, 0...) failed: %v\n", s.sysno, errno) + } else { + fmt.Printf("syscall(%d, 0, 0...) success\n", s.sysno) + } + return subcommands.ExitSuccess +} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index 7f1afc34b..37a3efd09 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -66,7 +66,7 @@ type Sandbox struct { // Create creates the sandbox process. The caller must call Destroy() on the // sandbox. -func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string, ioFiles []*os.File) (*Sandbox, error) { +func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File) (*Sandbox, error) { s := &Sandbox{ID: id} c := specutils.MakeCleanup(func() { s.destroy() }) defer c.Clean() @@ -81,7 +81,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo } // Create the sandbox process. - if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, ioFiles); err != nil { + if err := s.createSandboxProcess(spec, conf, bundleDir, consoleSocket, userLog, ioFiles); err != nil { return nil, err } @@ -266,7 +266,7 @@ func (s *Sandbox) connError(err error) error { // createSandboxProcess starts the sandbox as a subprocess by running the "boot" // command, passing in the bundle dir. -func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket string, ioFiles []*os.File) error { +func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bundleDir, consoleSocket, userLog string, ioFiles []*os.File) error { // nextFD is used to get unused FDs that we can pass to the sandbox. It // starts at 3 because 0, 1, and 2 are taken by stdin/out/err. nextFD := 3 @@ -525,6 +525,18 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } } + if userLog != "" { + f, err := os.OpenFile(userLog, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) + if err != nil { + return fmt.Errorf("opening compat log file: %v", err) + } + defer f.Close() + + cmd.ExtraFiles = append(cmd.ExtraFiles, f) + cmd.Args = append(cmd.Args, "--user-log-fd", strconv.Itoa(nextFD)) + nextFD++ + } + // Add container as the last argument. cmd.Args = append(cmd.Args, s.ID) |