mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add bare bones unsupported syscall logging
This change introduces a new flags to create/run called --user-log. Logs to this files are visible to users and are meant to help debugging problems with their images and containers. For now only unsupported syscalls are sent to this log, and only minimum support was added. We can build more infrastructure around it as needed. PiperOrigin-RevId: 216735977 Change-Id: I54427ca194604991c407d49943ab3680470de2d0
This commit is contained in:
committed by
Shentubot
parent
0bfa03d61c
commit
f413e4b117
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+9
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user