summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/control
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/control')
-rw-r--r--pkg/sentry/control/BUILD8
-rw-r--r--pkg/sentry/control/events.go65
-rw-r--r--pkg/sentry/control/fs.go93
-rw-r--r--pkg/sentry/control/lifecycle.go36
-rw-r--r--pkg/sentry/control/usage.go183
5 files changed, 385 insertions, 0 deletions
diff --git a/pkg/sentry/control/BUILD b/pkg/sentry/control/BUILD
index deaf5fa23..a4934a565 100644
--- a/pkg/sentry/control/BUILD
+++ b/pkg/sentry/control/BUILD
@@ -6,16 +6,22 @@ go_library(
name = "control",
srcs = [
"control.go",
+ "events.go",
+ "fs.go",
+ "lifecycle.go",
"logging.go",
"pprof.go",
"proc.go",
"state.go",
+ "usage.go",
],
visibility = [
"//:sandbox",
],
deps = [
"//pkg/abi/linux",
+ "//pkg/context",
+ "//pkg/eventchannel",
"//pkg/fd",
"//pkg/log",
"//pkg/sentry/fdimport",
@@ -35,6 +41,8 @@ go_library(
"//pkg/sync",
"//pkg/tcpip/link/sniffer",
"//pkg/urpc",
+ "//pkg/usermem",
+ "@org_golang_x_sys//unix:go_default_library",
],
)
diff --git a/pkg/sentry/control/events.go b/pkg/sentry/control/events.go
new file mode 100644
index 000000000..92e437ae7
--- /dev/null
+++ b/pkg/sentry/control/events.go
@@ -0,0 +1,65 @@
+// Copyright 2021 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 control
+
+import (
+ "errors"
+ "fmt"
+
+ "gvisor.dev/gvisor/pkg/eventchannel"
+ "gvisor.dev/gvisor/pkg/urpc"
+)
+
+// EventsOpts are the arguments for eventchannel-related commands.
+type EventsOpts struct {
+ urpc.FilePayload
+}
+
+// Events is the control server state for eventchannel-related commands.
+type Events struct {
+ emitter eventchannel.Emitter
+}
+
+// AttachDebugEmitter receives a connected unix domain socket FD from the client
+// and establishes it as a new emitter for the sentry eventchannel. Any existing
+// emitters are replaced on a subsequent attach.
+func (e *Events) AttachDebugEmitter(o *EventsOpts, _ *struct{}) error {
+ if len(o.FilePayload.Files) < 1 {
+ return errors.New("no output writer provided")
+ }
+
+ sock, err := o.ReleaseFD(0)
+ if err != nil {
+ return err
+ }
+ sockFD := sock.Release()
+
+ // SocketEmitter takes ownership of sockFD.
+ emitter, err := eventchannel.SocketEmitter(sockFD)
+ if err != nil {
+ return fmt.Errorf("failed to create SocketEmitter for FD %d: %v", sockFD, err)
+ }
+
+ // If there is already a debug emitter, close the old one.
+ if e.emitter != nil {
+ e.emitter.Close()
+ }
+
+ e.emitter = eventchannel.DebugEmitterFrom(emitter)
+
+ // Register the new stream destination.
+ eventchannel.AddEmitter(e.emitter)
+ return nil
+}
diff --git a/pkg/sentry/control/fs.go b/pkg/sentry/control/fs.go
new file mode 100644
index 000000000..d19b21f2d
--- /dev/null
+++ b/pkg/sentry/control/fs.go
@@ -0,0 +1,93 @@
+// Copyright 2021 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 control
+
+import (
+ "fmt"
+ "io"
+ "os"
+
+ "gvisor.dev/gvisor/pkg/context"
+ "gvisor.dev/gvisor/pkg/sentry/fs"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
+ "gvisor.dev/gvisor/pkg/urpc"
+ "gvisor.dev/gvisor/pkg/usermem"
+)
+
+// CatOpts contains options for the Cat RPC call.
+type CatOpts struct {
+ // Files are the filesystem paths for the files to cat.
+ Files []string `json:"files"`
+
+ // FilePayload contains the destination for output.
+ urpc.FilePayload
+}
+
+// Fs includes fs-related functions.
+type Fs struct {
+ Kernel *kernel.Kernel
+}
+
+// Cat is a RPC stub which prints out and returns the content of the files.
+func (f *Fs) Cat(o *CatOpts, _ *struct{}) error {
+ // Create an output stream.
+ if len(o.FilePayload.Files) != 1 {
+ return ErrInvalidFiles
+ }
+
+ output := o.FilePayload.Files[0]
+ for _, file := range o.Files {
+ if err := cat(f.Kernel, file, output); err != nil {
+ return fmt.Errorf("cannot read from file %s: %v", file, err)
+ }
+ }
+
+ return nil
+}
+
+// fileReader encapsulates a fs.File and provides an io.Reader interface.
+type fileReader struct {
+ ctx context.Context
+ file *fs.File
+}
+
+// Read implements io.Reader.Read.
+func (f *fileReader) Read(p []byte) (int, error) {
+ n, err := f.file.Readv(f.ctx, usermem.BytesIOSequence(p))
+ return int(n), err
+}
+
+func cat(k *kernel.Kernel, path string, output *os.File) error {
+ ctx := k.SupervisorContext()
+ mns := k.GlobalInit().Leader().MountNamespace()
+ root := mns.Root()
+ defer root.DecRef(ctx)
+
+ remainingTraversals := uint(fs.DefaultTraversalLimit)
+ d, err := mns.FindInode(ctx, root, nil, path, &remainingTraversals)
+ if err != nil {
+ return fmt.Errorf("cannot find file %s: %v", path, err)
+ }
+ defer d.DecRef(ctx)
+
+ file, err := d.Inode.GetFile(ctx, d, fs.FileFlags{Read: true})
+ if err != nil {
+ return fmt.Errorf("cannot get file for path %s: %v", path, err)
+ }
+ defer file.DecRef(ctx)
+
+ _, err = io.Copy(output, &fileReader{ctx: ctx, file: file})
+ return err
+}
diff --git a/pkg/sentry/control/lifecycle.go b/pkg/sentry/control/lifecycle.go
new file mode 100644
index 000000000..67abf497d
--- /dev/null
+++ b/pkg/sentry/control/lifecycle.go
@@ -0,0 +1,36 @@
+// Copyright 2021 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 control
+
+import (
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
+)
+
+// Lifecycle provides functions related to starting and stopping tasks.
+type Lifecycle struct {
+ Kernel *kernel.Kernel
+}
+
+// Pause pauses all tasks, blocking until they are stopped.
+func (l *Lifecycle) Pause(_, _ *struct{}) error {
+ l.Kernel.Pause()
+ return nil
+}
+
+// Resume resumes all tasks.
+func (l *Lifecycle) Resume(_, _ *struct{}) error {
+ l.Kernel.Unpause()
+ return nil
+}
diff --git a/pkg/sentry/control/usage.go b/pkg/sentry/control/usage.go
new file mode 100644
index 000000000..cc78d3f45
--- /dev/null
+++ b/pkg/sentry/control/usage.go
@@ -0,0 +1,183 @@
+// Copyright 2021 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 control
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
+ "gvisor.dev/gvisor/pkg/sentry/usage"
+ "gvisor.dev/gvisor/pkg/urpc"
+)
+
+// Usage includes usage-related RPC stubs.
+type Usage struct {
+ Kernel *kernel.Kernel
+}
+
+// MemoryUsageOpts contains usage options.
+type MemoryUsageOpts struct {
+ // Full indicates that a full accounting should be done. If Full is not
+ // specified, then a partial accounting will be done, and Unknown will
+ // contain a majority of memory. See Collect for more information.
+ Full bool `json:"Full"`
+}
+
+// MemoryUsage is a memory usage structure.
+type MemoryUsage struct {
+ Unknown uint64 `json:"Unknown"`
+ System uint64 `json:"System"`
+ Anonymous uint64 `json:"Anonymous"`
+ PageCache uint64 `json:"PageCache"`
+ Mapped uint64 `json:"Mapped"`
+ Tmpfs uint64 `json:"Tmpfs"`
+ Ramdiskfs uint64 `json:"Ramdiskfs"`
+ Total uint64 `json:"Total"`
+}
+
+// MemoryUsageFileOpts contains usage file options.
+type MemoryUsageFileOpts struct {
+ // Version is used to ensure both sides agree on the format of the
+ // shared memory buffer.
+ Version uint64 `json:"Version"`
+}
+
+// MemoryUsageFile contains the file handle to the usage file.
+type MemoryUsageFile struct {
+ urpc.FilePayload
+}
+
+// UsageFD returns the file that tracks the memory usage of the application.
+func (u *Usage) UsageFD(opts *MemoryUsageFileOpts, out *MemoryUsageFile) error {
+ // Only support version 1 for now.
+ if opts.Version != 1 {
+ return fmt.Errorf("unsupported version requested: %d", opts.Version)
+ }
+
+ mf := u.Kernel.MemoryFile()
+ *out = MemoryUsageFile{
+ FilePayload: urpc.FilePayload{
+ Files: []*os.File{
+ usage.MemoryAccounting.File,
+ mf.File(),
+ },
+ },
+ }
+
+ return nil
+}
+
+// Collect returns memory used by the sandboxed application.
+func (u *Usage) Collect(opts *MemoryUsageOpts, out *MemoryUsage) error {
+ if opts.Full {
+ // Ensure everything is up to date.
+ if err := u.Kernel.MemoryFile().UpdateUsage(); err != nil {
+ return err
+ }
+
+ // Copy out a snapshot.
+ snapshot, total := usage.MemoryAccounting.Copy()
+ *out = MemoryUsage{
+ System: snapshot.System,
+ Anonymous: snapshot.Anonymous,
+ PageCache: snapshot.PageCache,
+ Mapped: snapshot.Mapped,
+ Tmpfs: snapshot.Tmpfs,
+ Ramdiskfs: snapshot.Ramdiskfs,
+ Total: total,
+ }
+ } else {
+ // Get total usage from the MemoryFile implementation.
+ total, err := u.Kernel.MemoryFile().TotalUsage()
+ if err != nil {
+ return err
+ }
+
+ // The memory accounting is guaranteed to be accurate only when
+ // UpdateUsage is called. If UpdateUsage is not called, then only Mapped
+ // will be up-to-date.
+ snapshot, _ := usage.MemoryAccounting.Copy()
+ *out = MemoryUsage{
+ Unknown: total,
+ Mapped: snapshot.Mapped,
+ Total: total + snapshot.Mapped,
+ }
+
+ }
+
+ return nil
+}
+
+// UsageReduceOpts contains options to Usage.Reduce().
+type UsageReduceOpts struct {
+ // If Wait is true, Reduce blocks until all activity initiated by
+ // Usage.Reduce() has completed.
+ Wait bool `json:"wait"`
+}
+
+// UsageReduceOutput contains output from Usage.Reduce().
+type UsageReduceOutput struct{}
+
+// Reduce requests that the sentry attempt to reduce its memory usage.
+func (u *Usage) Reduce(opts *UsageReduceOpts, out *UsageReduceOutput) error {
+ mf := u.Kernel.MemoryFile()
+ mf.StartEvictions()
+ if opts.Wait {
+ mf.WaitForEvictions()
+ }
+ return nil
+}
+
+// MemoryUsageRecord contains the mapping and platform memory file.
+type MemoryUsageRecord struct {
+ mmap uintptr
+ stats *usage.RTMemoryStats
+ mf os.File
+}
+
+// NewMemoryUsageRecord creates a new MemoryUsageRecord from usageFile and
+// platformFile.
+func NewMemoryUsageRecord(usageFile, platformFile os.File) (*MemoryUsageRecord, error) {
+ mmap, _, e := unix.RawSyscall6(unix.SYS_MMAP, 0, usage.RTMemoryStatsSize, unix.PROT_READ, unix.MAP_SHARED, usageFile.Fd(), 0)
+ if e != 0 {
+ return nil, fmt.Errorf("mmap returned %d, want 0", e)
+ }
+
+ m := MemoryUsageRecord{
+ mmap: mmap,
+ stats: usage.RTMemoryStatsPointer(mmap),
+ mf: platformFile,
+ }
+
+ runtime.SetFinalizer(&m, finalizer)
+ return &m, nil
+}
+
+func finalizer(m *MemoryUsageRecord) {
+ unix.RawSyscall(unix.SYS_MUNMAP, m.mmap, usage.RTMemoryStatsSize, 0)
+}
+
+// Fetch fetches the usage info from a MemoryUsageRecord.
+func (m *MemoryUsageRecord) Fetch() (mapped, unknown, total uint64, err error) {
+ var stat unix.Stat_t
+ if err := unix.Fstat(int(m.mf.Fd()), &stat); err != nil {
+ return 0, 0, 0, err
+ }
+ fmem := uint64(stat.Blocks) * 512
+ return m.stats.RTMapped, fmem, m.stats.RTMapped + fmem, nil
+}