mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add runsc trace commands
The trace commands allows a user to manipulate trace sessions. `runsc trace create <name> --config <file>` => creates a new trace session `runsc trace delete <name>` => deletes an existing trace session `runsc trace list` => lists all running trace sessions `runsc trace metadata` => lists all point with their respective optional and context fields This allows trace sessions to be created/deleted on a running sandbox. Note that the system currently only allows a single trace session to exist, named 'Default'. Attempts to manipulate other sessions will error out. Updates #4805 PiperOrigin-RevId: 447815153
This commit is contained in:
committed by
gVisor bot
parent
944b941f9d
commit
f34e34b3c3
@@ -5,7 +5,6 @@ package(licenses = ["notice"])
|
||||
go_library(
|
||||
name = "remote",
|
||||
srcs = ["remote.go"],
|
||||
marshal = True,
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/cleanup",
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck/checkers/remote/header"
|
||||
|
||||
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
|
||||
)
|
||||
|
||||
@@ -101,6 +100,15 @@ func New(_ map[string]interface{}, endpoint *fd.FD) (seccheck.Checker, error) {
|
||||
return &Remote{endpoint: endpoint}, nil
|
||||
}
|
||||
|
||||
// Stop implements seccheck.Checker.
|
||||
func (r *Remote) Stop() {
|
||||
if r.endpoint != nil {
|
||||
// It's possible to race with Point firing, but in the worst case they will
|
||||
// simply fail to be delivered.
|
||||
r.endpoint.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Remote) write(msg proto.Message, msgType pb.MessageType) {
|
||||
out, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
@@ -116,7 +124,7 @@ func (r *Remote) write(msg proto.Message, msgType pb.MessageType) {
|
||||
|
||||
// TODO(gvisor.dev/issue/4805): Change to non-blocking write. Count as dropped
|
||||
// if write fails.
|
||||
if _, err = unix.Writev(r.endpoint.FD(), [][]byte{hdrOut[:], out}); err != nil {
|
||||
if _, err := unix.Writev(r.endpoint.FD(), [][]byte{hdrOut[:], out}); err != nil {
|
||||
log.Debugf("write(%+v, %v): %v", msg, msgType, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -169,6 +169,16 @@ func (s *Server) Count() int {
|
||||
return len(s.points)
|
||||
}
|
||||
|
||||
// Reset throws aways all points received so far and returns the number of
|
||||
// points discarded.
|
||||
func (s *Server) Reset() int {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
count := len(s.points)
|
||||
s.points = nil
|
||||
return count
|
||||
}
|
||||
|
||||
// GetPoints returns all points that it has received.
|
||||
func (s *Server) GetPoints() []Message {
|
||||
s.mu.Lock()
|
||||
|
||||
@@ -17,11 +17,21 @@ package seccheck
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
// DefaultSessionName is the name of the only session that can exist in the
|
||||
// system for now. When multiple sessions are supported, this can be removed.
|
||||
const DefaultSessionName = "Default"
|
||||
|
||||
var (
|
||||
sessionsMu = sync.Mutex{}
|
||||
sessions = make(map[string]*State)
|
||||
)
|
||||
|
||||
// SessionConfig describes a new session configuration. A session consists of a
|
||||
// set of points to be enabled and sinks where the points are sent to.
|
||||
type SessionConfig struct {
|
||||
@@ -57,13 +67,18 @@ type SinkConfig struct {
|
||||
FD *fd.FD `json:"-"`
|
||||
}
|
||||
|
||||
// Configure reads the session configuration and applies it to the system.
|
||||
func Configure(conf *SessionConfig) error {
|
||||
log.Debugf("Configuring seccheck: %+v", conf)
|
||||
state, err := findSession(conf.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
// Create reads the session configuration and applies it to the system.
|
||||
func Create(conf *SessionConfig) error {
|
||||
log.Debugf("Creating seccheck: %+v", conf)
|
||||
sessionsMu.Lock()
|
||||
defer sessionsMu.Unlock()
|
||||
if _, ok := sessions[conf.Name]; ok {
|
||||
return fmt.Errorf("session %q already exists", conf.Name)
|
||||
}
|
||||
if conf.Name != DefaultSessionName {
|
||||
return fmt.Errorf(`only a single "Default" session is supported`)
|
||||
}
|
||||
state := &Global
|
||||
|
||||
var reqs []PointReq
|
||||
for _, ptConfig := range conf.Points {
|
||||
@@ -100,11 +115,31 @@ func Configure(conf *SessionConfig) error {
|
||||
state.AppendChecker(checker, reqs)
|
||||
}
|
||||
|
||||
sessions[conf.Name] = state
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupSink runs the setup step for a given sink.
|
||||
func SetupSink(config SinkConfig) (*os.File, error) {
|
||||
// SetupSinks runs the setup step of all sinks in the configuration.
|
||||
func SetupSinks(sinks []SinkConfig) ([]*os.File, error) {
|
||||
var files []*os.File
|
||||
for _, sink := range sinks {
|
||||
sinkFile, err := setupSink(sink)
|
||||
if err != nil {
|
||||
if !sink.IgnoreSetupError {
|
||||
return nil, err
|
||||
}
|
||||
log.Warningf("Ignoring sink setup failure: %v", err)
|
||||
// Set sinkFile is nil and append it to the list to ensure the file
|
||||
// order is preserved.
|
||||
sinkFile = nil
|
||||
}
|
||||
files = append(files, sinkFile)
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// setupSink runs the setup step for a given sink.
|
||||
func setupSink(config SinkConfig) (*os.File, error) {
|
||||
sink, err := findSinkDesc(config.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -115,11 +150,30 @@ func SetupSink(config SinkConfig) (*os.File, error) {
|
||||
return sink.Setup(config.Config)
|
||||
}
|
||||
|
||||
func findSession(name string) (*State, error) {
|
||||
if name != "Default" {
|
||||
return nil, fmt.Errorf(`only a single "Default" session is supported`)
|
||||
// Delete deletes an existing session.
|
||||
func Delete(name string) error {
|
||||
sessionsMu.Lock()
|
||||
defer sessionsMu.Unlock()
|
||||
|
||||
session := sessions[name]
|
||||
if session == nil {
|
||||
return fmt.Errorf("session %q not found", name)
|
||||
}
|
||||
|
||||
session.clearCheckers()
|
||||
delete(sessions, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// List lists all existing sessions.
|
||||
func List(out *[]SessionConfig) {
|
||||
sessionsMu.Lock()
|
||||
defer sessionsMu.Unlock()
|
||||
|
||||
for name := range sessions {
|
||||
// Only report session name. Consider adding rest of the fields as needed.
|
||||
*out = append(*out, SessionConfig{Name: name})
|
||||
}
|
||||
return &Global, nil
|
||||
}
|
||||
|
||||
func findPointDesc(name string) (PointDesc, error) {
|
||||
|
||||
@@ -96,6 +96,9 @@ func (fm *FieldMask) Empty() bool {
|
||||
// may be missing requested fields in some cases (e.g. if the Checker is
|
||||
// registered concurrently with invocations of checkpoints).
|
||||
type Checker interface {
|
||||
// Stop requests the checker to stop.
|
||||
Stop()
|
||||
|
||||
Clone(ctx context.Context, fields FieldSet, info *pb.CloneInfo) error
|
||||
Execve(ctx context.Context, fields FieldSet, info *pb.ExecveInfo) error
|
||||
ExitNotifyParent(ctx context.Context, fields FieldSet, info *pb.ExitNotifyParentInfo) error
|
||||
@@ -113,6 +116,9 @@ type CheckerDefaults struct{}
|
||||
|
||||
var _ Checker = (*CheckerDefaults)(nil)
|
||||
|
||||
// Stop implements Checker.Stop.
|
||||
func (CheckerDefaults) Stop() {}
|
||||
|
||||
// Clone implements Checker.Clone.
|
||||
func (CheckerDefaults) Clone(context.Context, FieldSet, *pb.CloneInfo) error {
|
||||
return nil
|
||||
@@ -201,6 +207,24 @@ func (s *State) AppendChecker(c Checker, reqs []PointReq) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) clearCheckers() {
|
||||
s.registrationMu.Lock()
|
||||
defer s.registrationMu.Unlock()
|
||||
|
||||
for i := range s.enabledPoints {
|
||||
s.enabledPoints[i].Store(0)
|
||||
}
|
||||
s.pointFields = nil
|
||||
|
||||
oldCheckers := s.getCheckers()
|
||||
s.registrationSeq.BeginWrite()
|
||||
s.checkers = nil
|
||||
s.registrationSeq.EndWrite()
|
||||
for _, checker := range oldCheckers {
|
||||
checker.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled returns true if any Checker is registered for the given checkpoint.
|
||||
func (s *State) Enabled(p Point) bool {
|
||||
word, bit := p/32, p%32
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
controlpb "gvisor.dev/gvisor/pkg/sentry/control/control_go_proto"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/netstack"
|
||||
"gvisor.dev/gvisor/pkg/sentry/state"
|
||||
"gvisor.dev/gvisor/pkg/sentry/time"
|
||||
@@ -80,6 +81,15 @@ const (
|
||||
|
||||
// ContMgrRootContainerStart starts a new sandbox with a root container.
|
||||
ContMgrRootContainerStart = "containerManager.StartRoot"
|
||||
|
||||
// ContMgrCreateTraceSession starts a trace session.
|
||||
ContMgrCreateTraceSession = "containerManager.CreateTraceSession"
|
||||
|
||||
// ContMgrDeleteTraceSession deletes a trace session.
|
||||
ContMgrDeleteTraceSession = "containerManager.DeleteTraceSession"
|
||||
|
||||
// ContMgrListTraceSessions lists a trace session.
|
||||
ContMgrListTraceSessions = "containerManager.ListTraceSessions"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -590,3 +600,37 @@ func (cm *containerManager) Signal(args *SignalArgs, _ *struct{}) error {
|
||||
log.Debugf("containerManager.Signal: cid: %s, PID: %d, signal: %d, mode: %v", args.CID, args.PID, args.Signo, args.Mode)
|
||||
return cm.l.signal(args.CID, args.PID, args.Signo, args.Mode)
|
||||
}
|
||||
|
||||
// CreateTraceSessionArgs are arguments to the CreateTraceSession method.
|
||||
type CreateTraceSessionArgs struct {
|
||||
Config seccheck.SessionConfig
|
||||
urpc.FilePayload
|
||||
}
|
||||
|
||||
// CreateTraceSession creates a new trace session.
|
||||
func (cm *containerManager) CreateTraceSession(args *CreateTraceSessionArgs, _ *struct{}) error {
|
||||
log.Debugf("containerManager.CreateTraceSession: config: %+v", args.Config)
|
||||
for i, sinkFile := range args.Files {
|
||||
if sinkFile != nil {
|
||||
fd, err := fd.NewFromFile(sinkFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.Config.Sinks[i].FD = fd
|
||||
}
|
||||
}
|
||||
return seccheck.Create(&args.Config)
|
||||
}
|
||||
|
||||
// DeleteTraceSession deletes an existing trace session.
|
||||
func (cm *containerManager) DeleteTraceSession(name *string, _ *struct{}) error {
|
||||
log.Debugf("containerManager.DeleteTraceSession: name: %q", *name)
|
||||
return seccheck.Delete(*name)
|
||||
}
|
||||
|
||||
// ListTraceSessions lists trace sessions.
|
||||
func (cm *containerManager) ListTraceSessions(_ *struct{}, out *[]seccheck.SessionConfig) error {
|
||||
log.Debugf("containerManager.ListTraceSessions")
|
||||
seccheck.List(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
+4
-20
@@ -20,7 +20,6 @@ import (
|
||||
"os"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
|
||||
// Register supported of checkers.
|
||||
@@ -41,7 +40,7 @@ func setupSeccheck(configFD int, sinkFDs []int) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return initConf.configure(sinkFDs)
|
||||
return initConf.create(sinkFDs)
|
||||
}
|
||||
|
||||
// LoadInitConfig loads an InitConfig struct from a json formatted file.
|
||||
@@ -66,29 +65,14 @@ func loadInitConfig(reader io.Reader) (*InitConfig, error) {
|
||||
// Setup performs the actions defined in the InitConfig, e.g. setup seccheck
|
||||
// session.
|
||||
func (c *InitConfig) Setup() ([]*os.File, error) {
|
||||
var files []*os.File
|
||||
for _, sink := range c.TraceSession.Sinks {
|
||||
sinkFile, err := seccheck.SetupSink(sink)
|
||||
if err != nil {
|
||||
if !sink.IgnoreSetupError {
|
||||
return nil, err
|
||||
}
|
||||
log.Warningf("Ignoring sink setup failure: %v", err)
|
||||
// Ensure sinkFile is nil and append it to the list to ensure the file
|
||||
// order is preserved.
|
||||
sinkFile = nil
|
||||
}
|
||||
files = append(files, sinkFile)
|
||||
}
|
||||
return files, nil
|
||||
return seccheck.SetupSinks(c.TraceSession.Sinks)
|
||||
}
|
||||
|
||||
func (c *InitConfig) configure(sinkFDs []int) error {
|
||||
func (c *InitConfig) create(sinkFDs []int) error {
|
||||
for i, sinkFD := range sinkFDs {
|
||||
if sinkFD >= 0 {
|
||||
c.TraceSession.Sinks[i].FD = fd.New(sinkFD)
|
||||
}
|
||||
}
|
||||
return seccheck.Configure(&c.TraceSession)
|
||||
|
||||
return seccheck.Create(&c.TraceSession)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ go_library(
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/sentry/platform",
|
||||
"//runsc/cmd",
|
||||
"//runsc/cmd/trace",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/config",
|
||||
"//runsc/flag",
|
||||
|
||||
+3
-1
@@ -33,6 +33,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/refsvfs2"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
"gvisor.dev/gvisor/runsc/cmd"
|
||||
"gvisor.dev/gvisor/runsc/cmd/trace"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/flag"
|
||||
@@ -83,11 +84,12 @@ func Main(version string) {
|
||||
subcommands.Register(new(cmd.VerityPrepare), "")
|
||||
subcommands.Register(new(cmd.Wait), "")
|
||||
|
||||
// Installation helpers.
|
||||
// Helpers.
|
||||
const helperGroup = "helpers"
|
||||
subcommands.Register(new(cmd.Install), helperGroup)
|
||||
subcommands.Register(new(cmd.Mitigate), helperGroup)
|
||||
subcommands.Register(new(cmd.Uninstall), helperGroup)
|
||||
subcommands.Register(new(trace.Trace), helperGroup)
|
||||
|
||||
const debugGroup = "debug"
|
||||
subcommands.Register(new(cmd.Debug), debugGroup)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "trace",
|
||||
srcs = [
|
||||
"create.go",
|
||||
"delete.go",
|
||||
"list.go",
|
||||
"metadata.go",
|
||||
"trace.go",
|
||||
],
|
||||
visibility = [
|
||||
"//runsc:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/sentry/seccheck",
|
||||
"//runsc/cmd/util",
|
||||
"//runsc/config",
|
||||
"//runsc/container",
|
||||
"//runsc/flag",
|
||||
"@com_github_google_subcommands//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/container"
|
||||
"gvisor.dev/gvisor/runsc/flag"
|
||||
)
|
||||
|
||||
// create implements subcommands.Command for the "create" command.
|
||||
type create struct {
|
||||
config string
|
||||
}
|
||||
|
||||
// Name implements subcommands.Command.
|
||||
func (*create) Name() string {
|
||||
return "create"
|
||||
}
|
||||
|
||||
// Synopsis implements subcommands.Command.
|
||||
func (*create) Synopsis() string {
|
||||
return "create a trace session"
|
||||
}
|
||||
|
||||
// Usage implements subcommands.Command.
|
||||
func (*create) Usage() string {
|
||||
return `create [flags] <sandbox id> - create a trace session
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags implements subcommands.Command.
|
||||
func (l *create) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&l.config, "config", "", "path to the JSON file that describes the session being created")
|
||||
}
|
||||
|
||||
// Execute implements subcommands.Command.
|
||||
func (l *create) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||
if f.NArg() != 1 {
|
||||
f.Usage()
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if len(l.config) == 0 {
|
||||
f.Usage()
|
||||
return util.Errorf("missing path to configuration file, please set --config=[path]")
|
||||
}
|
||||
|
||||
file, err := os.Open(l.config)
|
||||
if err != nil {
|
||||
return util.Errorf(err.Error())
|
||||
}
|
||||
defer file.Close()
|
||||
decoder := json.NewDecoder(file)
|
||||
sessionConfig := &seccheck.SessionConfig{}
|
||||
if err := decoder.Decode(sessionConfig); err != nil {
|
||||
return util.Errorf("invalid configuration file: %v", err)
|
||||
}
|
||||
|
||||
id := f.Arg(0)
|
||||
conf := args[0].(*config.Config)
|
||||
|
||||
opts := container.LoadOpts{
|
||||
SkipCheck: true,
|
||||
RootContainer: true,
|
||||
}
|
||||
c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, opts)
|
||||
if err != nil {
|
||||
util.Fatalf("loading sandbox: %v", err)
|
||||
}
|
||||
|
||||
if err := c.Sandbox.CreateTraceSession(sessionConfig); err != nil {
|
||||
util.Fatalf("creating session: %v", err)
|
||||
}
|
||||
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/container"
|
||||
"gvisor.dev/gvisor/runsc/flag"
|
||||
)
|
||||
|
||||
// delete implements subcommands.Command for the "delete" command.
|
||||
type delete struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// Name implements subcommands.Command.
|
||||
func (*delete) Name() string {
|
||||
return "delete"
|
||||
}
|
||||
|
||||
// Synopsis implements subcommands.Command.
|
||||
func (*delete) Synopsis() string {
|
||||
return "delete a trace session"
|
||||
}
|
||||
|
||||
// Usage implements subcommands.Command.
|
||||
func (*delete) Usage() string {
|
||||
return `delete [flags] <sandbox id> - delete a trace session
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags implements subcommands.Command.
|
||||
func (l *delete) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&l.name, "name", "", "name of session to be deleted")
|
||||
}
|
||||
|
||||
// Execute implements subcommands.Command.
|
||||
func (l *delete) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||
if f.NArg() != 1 {
|
||||
f.Usage()
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if len(l.name) == 0 {
|
||||
f.Usage()
|
||||
return util.Errorf("missing session name, please set --name")
|
||||
}
|
||||
|
||||
id := f.Arg(0)
|
||||
conf := args[0].(*config.Config)
|
||||
|
||||
opts := container.LoadOpts{
|
||||
SkipCheck: true,
|
||||
RootContainer: true,
|
||||
}
|
||||
c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, opts)
|
||||
if err != nil {
|
||||
util.Fatalf("loading sandbox: %v", err)
|
||||
}
|
||||
|
||||
if err := c.Sandbox.DeleteTraceSession(l.name); err != nil {
|
||||
util.Fatalf("deleting session: %v", err)
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"gvisor.dev/gvisor/runsc/cmd/util"
|
||||
"gvisor.dev/gvisor/runsc/config"
|
||||
"gvisor.dev/gvisor/runsc/container"
|
||||
"gvisor.dev/gvisor/runsc/flag"
|
||||
)
|
||||
|
||||
// list implements subcommands.Command for the "list" command.
|
||||
type list struct{}
|
||||
|
||||
// Name implements subcommands.Command.
|
||||
func (*list) Name() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
// Synopsis implements subcommands.Command.
|
||||
func (*list) Synopsis() string {
|
||||
return "list all trace sessions"
|
||||
}
|
||||
|
||||
// Usage implements subcommands.Command.
|
||||
func (*list) Usage() string {
|
||||
return `list - list all trace sessions
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags implements subcommands.Command.
|
||||
func (*list) SetFlags(*flag.FlagSet) {}
|
||||
|
||||
// Execute implements subcommands.Command.
|
||||
func (l *list) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||
if f.NArg() != 1 {
|
||||
f.Usage()
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
id := f.Arg(0)
|
||||
conf := args[0].(*config.Config)
|
||||
|
||||
opts := container.LoadOpts{
|
||||
SkipCheck: true,
|
||||
RootContainer: true,
|
||||
}
|
||||
c, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, opts)
|
||||
if err != nil {
|
||||
util.Fatalf("loading sandbox: %v", err)
|
||||
}
|
||||
|
||||
sessions, err := c.Sandbox.ListTraceSessions()
|
||||
if err != nil {
|
||||
util.Fatalf("listing sessions: %v", err)
|
||||
}
|
||||
fmt.Printf("SESSIONS (%d)\n", len(sessions))
|
||||
for _, session := range sessions {
|
||||
fmt.Printf("%q\n", session.Name)
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
"gvisor.dev/gvisor/runsc/flag"
|
||||
)
|
||||
|
||||
// metadata implements subcommands.Command for the "metadata" command.
|
||||
type metadata struct{}
|
||||
|
||||
// Name implements subcommands.Command.
|
||||
func (*metadata) Name() string {
|
||||
return "metadata"
|
||||
}
|
||||
|
||||
// Synopsis implements subcommands.Command.
|
||||
func (*metadata) Synopsis() string {
|
||||
return "list all trace points configuration information"
|
||||
}
|
||||
|
||||
// Usage implements subcommands.Command.
|
||||
func (*metadata) Usage() string {
|
||||
return `metadata - list all trace points configuration information
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags implements subcommands.Command.
|
||||
func (*metadata) SetFlags(*flag.FlagSet) {}
|
||||
|
||||
// Execute implements subcommands.Command.
|
||||
func (l *metadata) Execute(context.Context, *flag.FlagSet, ...interface{}) subcommands.ExitStatus {
|
||||
// Sort to keep related points together.
|
||||
points := make([]seccheck.PointDesc, 0, len(seccheck.Points))
|
||||
for _, pt := range seccheck.Points {
|
||||
points = append(points, pt)
|
||||
}
|
||||
sort.Slice(points, func(i int, j int) bool {
|
||||
return points[i].Name < points[j].Name
|
||||
})
|
||||
|
||||
fmt.Printf("POINTS (%d)\n", len(seccheck.Points))
|
||||
for _, pt := range points {
|
||||
optFields := fieldNames(pt.OptionalFields)
|
||||
ctxFields := fieldNames(pt.ContextFields)
|
||||
fmt.Printf("Name: %s, optional fields: [%s], context fields: [%s]\n", pt.Name, strings.Join(optFields, "|"), strings.Join(ctxFields, "|"))
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
func fieldNames(fields []seccheck.FieldDesc) []string {
|
||||
names := make([]string, 0, len(fields))
|
||||
for _, f := range fields {
|
||||
names = append(names, f.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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 trace provides subcommands for the trace command.
|
||||
package trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"gvisor.dev/gvisor/runsc/flag"
|
||||
)
|
||||
|
||||
// Trace implements subcommands.Command for the "trace" command.
|
||||
type Trace struct{}
|
||||
|
||||
// Name implements subcommands.Command.
|
||||
func (*Trace) Name() string {
|
||||
return "trace"
|
||||
}
|
||||
|
||||
// Synopsis implements subcommands.Command.
|
||||
func (*Trace) Synopsis() string {
|
||||
return "manages trace sessions for a given sandbox"
|
||||
}
|
||||
|
||||
// Usage implements subcommands.Command.
|
||||
func (*Trace) Usage() string {
|
||||
buf := bytes.Buffer{}
|
||||
buf.WriteString("Usage: trace <flags> <subcommand> <subcommand args>\n\n")
|
||||
|
||||
cdr := createCommander(&flag.FlagSet{})
|
||||
cdr.VisitGroups(func(grp *subcommands.CommandGroup) {
|
||||
cdr.ExplainGroup(&buf, grp)
|
||||
})
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// SetFlags implements subcommands.Command.
|
||||
func (*Trace) SetFlags(f *flag.FlagSet) {}
|
||||
|
||||
// Execute implements subcommands.Command.
|
||||
func (*Trace) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
||||
return createCommander(f).Execute(ctx, args...)
|
||||
}
|
||||
|
||||
func createCommander(f *flag.FlagSet) *subcommands.Commander {
|
||||
cdr := subcommands.NewCommander(f, "trace")
|
||||
cdr.Register(cdr.HelpCommand(), "")
|
||||
cdr.Register(cdr.FlagsCommand(), "")
|
||||
cdr.Register(new(create), "")
|
||||
cdr.Register(new(delete), "")
|
||||
cdr.Register(new(list), "")
|
||||
cdr.Register(new(metadata), "")
|
||||
return cdr
|
||||
}
|
||||
@@ -43,6 +43,7 @@ go_test(
|
||||
"container_test.go",
|
||||
"multi_container_test.go",
|
||||
"shared_volume_test.go",
|
||||
"trace_test.go",
|
||||
],
|
||||
# Only run the default platform for the tsan test, which should
|
||||
# be compatible. For non-tsan builds, run all platforms.
|
||||
@@ -70,6 +71,9 @@ go_test(
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/seccheck",
|
||||
"//pkg/sentry/seccheck/checkers/remote/test",
|
||||
"//pkg/sentry/seccheck/points:points_go_proto",
|
||||
"//pkg/sync",
|
||||
"//pkg/test/testutil",
|
||||
"//pkg/unet",
|
||||
@@ -81,6 +85,7 @@ go_test(
|
||||
"@com_github_cenkalti_backoff//:go_default_library",
|
||||
"@com_github_kr_pty//:go_default_library",
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -38,6 +38,11 @@ type LoadOpts struct {
|
||||
|
||||
// SkipCheck tells Load() to skip checking if container is runnning.
|
||||
SkipCheck bool
|
||||
|
||||
// RootContainer when true matches the search only with the root container of
|
||||
// a sandbox. This is used when looking for a sandbox given that root
|
||||
// container and sandbox share the same ID.
|
||||
RootContainer bool
|
||||
}
|
||||
|
||||
// Load loads a container with the given id from a metadata file. "id" may
|
||||
@@ -77,6 +82,10 @@ func Load(rootDir string, id FullID, opts LoadOpts) (*Container, error) {
|
||||
return nil, fmt.Errorf("reading container metadata file %q: %v", state.statePath(), err)
|
||||
}
|
||||
|
||||
if opts.RootContainer && c.ID != c.Sandbox.ID {
|
||||
return nil, fmt.Errorf("ID %q doesn't belong to a sandbox", id)
|
||||
}
|
||||
|
||||
if !opts.SkipCheck {
|
||||
// If the status is "Running" or "Created", check that the sandbox/container
|
||||
// is still running, setting it to Stopped if not.
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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 container
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck/checkers/remote/test"
|
||||
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
|
||||
"gvisor.dev/gvisor/pkg/test/testutil"
|
||||
"gvisor.dev/gvisor/runsc/boot"
|
||||
)
|
||||
|
||||
// Test that setting up a trace session configuration in PodInitConfig creates
|
||||
// a session before container creation.
|
||||
func TestTraceStartup(t *testing.T) {
|
||||
// Test on all configurations to ensure that point can be sent to an outside
|
||||
// process in all cases. Rest of the tests don't require all configs.
|
||||
for name, conf := range configs(t, false /* noOverlay */) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
server, err := test.NewServer()
|
||||
if err != nil {
|
||||
t.Fatalf("newServer(): %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
podInitConfig, err := ioutil.TempFile(testutil.TmpDir(), "config")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmp file: %v", err)
|
||||
}
|
||||
defer podInitConfig.Close()
|
||||
|
||||
initConfig := boot.InitConfig{
|
||||
TraceSession: seccheck.SessionConfig{
|
||||
Name: seccheck.DefaultSessionName,
|
||||
Points: []seccheck.PointConfig{
|
||||
{
|
||||
Name: "container/start",
|
||||
ContextFields: []string{"container_id"},
|
||||
},
|
||||
},
|
||||
Sinks: []seccheck.SinkConfig{
|
||||
{
|
||||
Name: "remote",
|
||||
Config: map[string]interface{}{
|
||||
"endpoint": server.Path,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
encoder := json.NewEncoder(podInitConfig)
|
||||
if err := encoder.Encode(&initConfig); err != nil {
|
||||
t.Fatalf("JSON encode: %v", err)
|
||||
}
|
||||
conf.PodInitConfig = podInitConfig.Name()
|
||||
|
||||
spec := testutil.NewSpecWithArgs("/bin/true")
|
||||
if err := run(spec, conf); err != nil {
|
||||
t.Fatalf("Error running container: %v", err)
|
||||
}
|
||||
|
||||
// Wait for the point to be received and then check that fields match.
|
||||
if err := server.WaitForCount(1); err != nil {
|
||||
t.Fatalf("WaitForCount(1): %v", err)
|
||||
}
|
||||
pt := server.GetPoints()[0]
|
||||
if want := pb.MessageType_MESSAGE_CONTAINER_START; pt.MsgType != want {
|
||||
t.Errorf("wrong message type, want: %v, got: %v", want, pt.MsgType)
|
||||
}
|
||||
got := &pb.Start{}
|
||||
if err := proto.Unmarshal(pt.Msg, got); err != nil {
|
||||
t.Errorf("proto.Unmarshal(Start): %v", err)
|
||||
}
|
||||
if want := "/bin/true"; len(got.Args) != 1 || want != got.Args[0] {
|
||||
t.Errorf("container.Start.Args, want: %q, got: %q", want, got.Args)
|
||||
}
|
||||
if want, got := got.Id, got.ContextData.ContainerId; want != got {
|
||||
t.Errorf("Mismatched container ID, want: %v, got: %v", want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceLifecycle(t *testing.T) {
|
||||
spec, conf := sleepSpecConf(t)
|
||||
_, bundleDir, cleanup, err := testutil.SetupContainer(spec, conf)
|
||||
if err != nil {
|
||||
t.Fatalf("error setting up container: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Create and start the container.
|
||||
args := Args{
|
||||
ID: testutil.RandomContainerID(),
|
||||
Spec: spec,
|
||||
BundleDir: bundleDir,
|
||||
}
|
||||
cont, err := New(conf, args)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating container: %v", err)
|
||||
}
|
||||
defer cont.Destroy()
|
||||
if err := cont.Start(conf); err != nil {
|
||||
t.Fatalf("error starting container: %v", err)
|
||||
}
|
||||
|
||||
// Check that no session are created.
|
||||
if sessions, err := cont.Sandbox.ListTraceSessions(); err != nil {
|
||||
t.Fatalf("ListTraceSessions(): %v", err)
|
||||
} else if len(sessions) != 0 {
|
||||
t.Fatalf("no session should exist, got: %+v", sessions)
|
||||
}
|
||||
|
||||
// Create a new trace session on the fly.
|
||||
server, err := test.NewServer()
|
||||
if err != nil {
|
||||
t.Fatalf("newServer(): %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
session := seccheck.SessionConfig{
|
||||
Name: "Default",
|
||||
Points: []seccheck.PointConfig{
|
||||
{
|
||||
Name: "sentry/task_exit",
|
||||
ContextFields: []string{"container_id"},
|
||||
},
|
||||
},
|
||||
Sinks: []seccheck.SinkConfig{
|
||||
{
|
||||
Name: "remote",
|
||||
Config: map[string]interface{}{
|
||||
"endpoint": server.Path,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := cont.Sandbox.CreateTraceSession(&session); err != nil {
|
||||
t.Fatalf("CreateTraceSession(): %v", err)
|
||||
}
|
||||
|
||||
// Trigger the configured point and want to receive it in the server.
|
||||
if ws, err := execute(conf, cont, "/bin/true"); err != nil || ws != 0 {
|
||||
t.Fatalf("exec: true, ws: %v, err: %v", ws, err)
|
||||
}
|
||||
if err := server.WaitForCount(1); err != nil {
|
||||
t.Fatalf("WaitForCount(1): %v", err)
|
||||
}
|
||||
pt := server.GetPoints()[0]
|
||||
if want := pb.MessageType_MESSAGE_SENTRY_TASK_EXIT; pt.MsgType != want {
|
||||
t.Errorf("wrong message type, want: %v, got: %v", want, pt.MsgType)
|
||||
}
|
||||
got := &pb.TaskExit{}
|
||||
if err := proto.Unmarshal(pt.Msg, got); err != nil {
|
||||
t.Errorf("proto.Unmarshal(TaskExit): %v", err)
|
||||
}
|
||||
if got.ExitStatus != 0 {
|
||||
t.Errorf("Wrong TaskExit.ExitStatus, want: 0, got: %+v", got)
|
||||
}
|
||||
if want, got := cont.ID, got.ContextData.ContainerId; want != got {
|
||||
t.Errorf("Wrong TaskExit.ContextData.ContainerId, want: %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
// Check that no more points were received and reset the server for the
|
||||
// remaining tests.
|
||||
if want, got := 1, server.Reset(); want != got {
|
||||
t.Errorf("wrong number of points, want: %d, got: %d", want, got)
|
||||
}
|
||||
|
||||
// List and check that trace session is reported.
|
||||
sessions, err := cont.Sandbox.ListTraceSessions()
|
||||
if err != nil {
|
||||
t.Fatalf("ListTraceSessions(): %v", err)
|
||||
}
|
||||
if len(sessions) != 1 {
|
||||
t.Fatalf("expected a single session, got: %+v", sessions)
|
||||
}
|
||||
if got := sessions[0].Name; seccheck.DefaultSessionName != got {
|
||||
t.Errorf("wrong session, want: %v, got: %v", seccheck.DefaultSessionName, got)
|
||||
}
|
||||
|
||||
if err := cont.Sandbox.DeleteTraceSession("Default"); err != nil {
|
||||
t.Fatalf("DeleteTraceSession(): %v", err)
|
||||
}
|
||||
|
||||
// Check that session was indeed deleted.
|
||||
if sessions, err := cont.Sandbox.ListTraceSessions(); err != nil {
|
||||
t.Fatalf("ListTraceSessions(): %v", err)
|
||||
} else if len(sessions) != 0 {
|
||||
t.Fatalf("no session should exist, got: %+v", sessions)
|
||||
}
|
||||
|
||||
// Trigger the point again and check that it's not received.
|
||||
if ws, err := execute(conf, cont, "/bin/true"); err != nil || ws != 0 {
|
||||
t.Fatalf("exec: true, ws: %v, err: %v", ws, err)
|
||||
}
|
||||
time.Sleep(time.Second) // give some time to receive the point.
|
||||
if server.Count() > 0 {
|
||||
t.Errorf("point received after session was deleted: %+v", server.GetPoints())
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ go_library(
|
||||
"//pkg/log",
|
||||
"//pkg/sentry/control",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/seccheck",
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/stack",
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/unet"
|
||||
"gvisor.dev/gvisor/pkg/urpc"
|
||||
@@ -196,7 +197,7 @@ func New(conf *config.Config, args *Args) (*Sandbox, error) {
|
||||
if len(conf.PodInitConfig) > 0 {
|
||||
initConf, err := boot.LoadInitConfig(conf.PodInitConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("loading init config file: %w", err)
|
||||
}
|
||||
args.SinkFiles, err = initConf.Setup()
|
||||
if err != nil {
|
||||
@@ -388,6 +389,69 @@ func (s *Sandbox) Processes(cid string) ([]*control.Process, error) {
|
||||
return pl, nil
|
||||
}
|
||||
|
||||
// CreateTraceSession creates a new trace session.
|
||||
func (s *Sandbox) CreateTraceSession(config *seccheck.SessionConfig) error {
|
||||
log.Debugf("Creating trace session in sandbox %q", s.ID)
|
||||
|
||||
sinkFiles, err := seccheck.SetupSinks(config.Sinks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
for _, f := range sinkFiles {
|
||||
_ = f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := s.sandboxConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
arg := boot.CreateTraceSessionArgs{
|
||||
Config: *config,
|
||||
FilePayload: urpc.FilePayload{
|
||||
Files: sinkFiles,
|
||||
},
|
||||
}
|
||||
if err := conn.Call(boot.ContMgrCreateTraceSession, &arg, nil); err != nil {
|
||||
return fmt.Errorf("creating trace session: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTraceSession deletes an existing trace session.
|
||||
func (s *Sandbox) DeleteTraceSession(name string) error {
|
||||
log.Debugf("Deleting trace session %q in sandbox %q", name, s.ID)
|
||||
conn, err := s.sandboxConnect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err := conn.Call(boot.ContMgrDeleteTraceSession, name, nil); err != nil {
|
||||
return fmt.Errorf("deleting trace session: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListTraceSessions lists all trace sessions.
|
||||
func (s *Sandbox) ListTraceSessions() ([]seccheck.SessionConfig, error) {
|
||||
log.Debugf("Listing trace sessions in sandbox %q", s.ID)
|
||||
conn, err := s.sandboxConnect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var sessions []seccheck.SessionConfig
|
||||
if err := conn.Call(boot.ContMgrListTraceSessions, nil, &sessions); err != nil {
|
||||
return nil, fmt.Errorf("listing trace session: %w", err)
|
||||
}
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// NewCGroup returns the sandbox's Cgroup, or an error if it does not have one.
|
||||
func (s *Sandbox) NewCGroup() (cgroup.Cgroup, error) {
|
||||
return cgroup.NewFromPid(s.Pid.load(), false /* useSystemd */)
|
||||
|
||||
Reference in New Issue
Block a user