summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
Diffstat (limited to 'runsc')
-rw-r--r--runsc/boot/BUILD6
-rw-r--r--runsc/boot/compat.go76
-rw-r--r--runsc/boot/loader.go6
-rw-r--r--runsc/cmd/boot.go5
-rw-r--r--runsc/cmd/capability_test.go2
-rw-r--r--runsc/cmd/checkpoint.go2
-rw-r--r--runsc/cmd/create.go10
-rw-r--r--runsc/cmd/run.go2
-rw-r--r--runsc/container/container.go8
-rw-r--r--runsc/container/container_test.go93
-rw-r--r--runsc/container/multi_container_test.go2
-rw-r--r--runsc/container/test_app.go36
-rw-r--r--runsc/sandbox/sandbox.go18
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)