mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add remaining /proc/* and /proc/sys/* files
Except for one under /proc/sys/net/ipv4/tcp_sack. /proc/pid/* is still incomplete. Updates #1195 PiperOrigin-RevId: 290120438
This commit is contained in:
committed by
gVisor bot
parent
fea1ce655d
commit
7b7c31820b
+21
-23
@@ -657,30 +657,28 @@ func (fs *FeatureSet) FlagsString(cpuinfoOnly bool) string {
|
||||
return strings.Join(s, " ")
|
||||
}
|
||||
|
||||
// CPUInfo is to generate a section of one cpu in /proc/cpuinfo. This is a
|
||||
// minimal /proc/cpuinfo, it is missing some fields like "microcode" that are
|
||||
// WriteCPUInfoTo is to generate a section of one cpu in /proc/cpuinfo. This is
|
||||
// a minimal /proc/cpuinfo, it is missing some fields like "microcode" that are
|
||||
// not always printed in Linux. The bogomips field is simply made up.
|
||||
func (fs FeatureSet) CPUInfo(cpu uint) string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, "processor\t: %d\n", cpu)
|
||||
fmt.Fprintf(&b, "vendor_id\t: %s\n", fs.VendorID)
|
||||
fmt.Fprintf(&b, "cpu family\t: %d\n", ((fs.ExtendedFamily<<4)&0xff)|fs.Family)
|
||||
fmt.Fprintf(&b, "model\t\t: %d\n", ((fs.ExtendedModel<<4)&0xff)|fs.Model)
|
||||
fmt.Fprintf(&b, "model name\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(&b, "stepping\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(&b, "cpu MHz\t\t: %.3f\n", cpuFreqMHz)
|
||||
fmt.Fprintln(&b, "fpu\t\t: yes")
|
||||
fmt.Fprintln(&b, "fpu_exception\t: yes")
|
||||
fmt.Fprintf(&b, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID.
|
||||
fmt.Fprintln(&b, "wp\t\t: yes")
|
||||
fmt.Fprintf(&b, "flags\t\t: %s\n", fs.FlagsString(true))
|
||||
fmt.Fprintf(&b, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
|
||||
fmt.Fprintf(&b, "clflush size\t: %d\n", fs.CacheLine)
|
||||
fmt.Fprintf(&b, "cache_alignment\t: %d\n", fs.CacheLine)
|
||||
fmt.Fprintf(&b, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48)
|
||||
fmt.Fprintln(&b, "power management:") // This is always here, but can be blank.
|
||||
fmt.Fprintln(&b, "") // The /proc/cpuinfo file ends with an extra newline.
|
||||
return b.String()
|
||||
func (fs FeatureSet) WriteCPUInfoTo(cpu uint, b *bytes.Buffer) {
|
||||
fmt.Fprintf(b, "processor\t: %d\n", cpu)
|
||||
fmt.Fprintf(b, "vendor_id\t: %s\n", fs.VendorID)
|
||||
fmt.Fprintf(b, "cpu family\t: %d\n", ((fs.ExtendedFamily<<4)&0xff)|fs.Family)
|
||||
fmt.Fprintf(b, "model\t\t: %d\n", ((fs.ExtendedModel<<4)&0xff)|fs.Model)
|
||||
fmt.Fprintf(b, "model name\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(b, "stepping\t: %s\n", "unknown") // Unknown for now.
|
||||
fmt.Fprintf(b, "cpu MHz\t\t: %.3f\n", cpuFreqMHz)
|
||||
fmt.Fprintln(b, "fpu\t\t: yes")
|
||||
fmt.Fprintln(b, "fpu_exception\t: yes")
|
||||
fmt.Fprintf(b, "cpuid level\t: %d\n", uint32(xSaveInfo)) // Same as ax in vendorID.
|
||||
fmt.Fprintln(b, "wp\t\t: yes")
|
||||
fmt.Fprintf(b, "flags\t\t: %s\n", fs.FlagsString(true))
|
||||
fmt.Fprintf(b, "bogomips\t: %.02f\n", cpuFreqMHz) // It's bogus anyway.
|
||||
fmt.Fprintf(b, "clflush size\t: %d\n", fs.CacheLine)
|
||||
fmt.Fprintf(b, "cache_alignment\t: %d\n", fs.CacheLine)
|
||||
fmt.Fprintf(b, "address sizes\t: %d bits physical, %d bits virtual\n", 46, 48)
|
||||
fmt.Fprintln(b, "power management:") // This is always here, but can be blank.
|
||||
fmt.Fprintln(b, "") // The /proc/cpuinfo file ends with an extra newline.
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
@@ -27,9 +29,9 @@ func newCPUInfo(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
|
||||
// Kernel is always initialized with a FeatureSet.
|
||||
panic("cpuinfo read with nil FeatureSet")
|
||||
}
|
||||
contents := make([]byte, 0, 1024)
|
||||
var buf bytes.Buffer
|
||||
for i, max := uint(0), k.ApplicationCores(); i < max; i++ {
|
||||
contents = append(contents, []byte(features.CPUInfo(i))...)
|
||||
features.WriteCPUInfoTo(i, &buf)
|
||||
}
|
||||
return newStaticProcInode(ctx, msrc, contents)
|
||||
return newStaticProcInode(ctx, msrc, buf.Bytes())
|
||||
}
|
||||
|
||||
@@ -510,3 +510,43 @@ type InodeSymlink struct {
|
||||
func (InodeSymlink) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, flags uint32) (*vfs.FileDescription, error) {
|
||||
return nil, syserror.ELOOP
|
||||
}
|
||||
|
||||
// StaticDirectory is a standard implementation of a directory with static
|
||||
// contents.
|
||||
//
|
||||
// +stateify savable
|
||||
type StaticDirectory struct {
|
||||
InodeNotSymlink
|
||||
InodeDirectoryNoNewChildren
|
||||
InodeAttrs
|
||||
InodeNoDynamicLookup
|
||||
OrderedChildren
|
||||
}
|
||||
|
||||
var _ Inode = (*StaticDirectory)(nil)
|
||||
|
||||
// NewStaticDir creates a new static directory and returns its dentry.
|
||||
func NewStaticDir(creds *auth.Credentials, ino uint64, perm linux.FileMode, children map[string]*Dentry) *Dentry {
|
||||
if perm&^linux.PermissionsMask != 0 {
|
||||
panic(fmt.Sprintf("Only permission mask must be set: %x", perm&linux.PermissionsMask))
|
||||
}
|
||||
|
||||
inode := &StaticDirectory{}
|
||||
inode.InodeAttrs.Init(creds, ino, linux.ModeDirectory|perm)
|
||||
|
||||
dentry := &Dentry{}
|
||||
dentry.Init(inode)
|
||||
|
||||
inode.OrderedChildren.Init(OrderedChildrenOptions{})
|
||||
links := inode.OrderedChildren.Populate(dentry, children)
|
||||
inode.IncLinks(links)
|
||||
|
||||
return dentry
|
||||
}
|
||||
|
||||
// Open implements kernfs.Inode.
|
||||
func (s *StaticDirectory) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, flags uint32) (*vfs.FileDescription, error) {
|
||||
fd := &GenericDirectoryFD{}
|
||||
fd.Init(rp.Mount(), vfsd, &s.OrderedChildren, flags)
|
||||
return fd.VFSFileDescription(), nil
|
||||
}
|
||||
|
||||
@@ -7,17 +7,13 @@ go_library(
|
||||
name = "proc",
|
||||
srcs = [
|
||||
"filesystem.go",
|
||||
"loadavg.go",
|
||||
"meminfo.go",
|
||||
"mounts.go",
|
||||
"net.go",
|
||||
"stat.go",
|
||||
"sys.go",
|
||||
"task.go",
|
||||
"task_files.go",
|
||||
"tasks.go",
|
||||
"tasks_files.go",
|
||||
"version.go",
|
||||
"tasks_net.go",
|
||||
"tasks_sys.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/sentry/fsimpl/proc",
|
||||
deps = [
|
||||
@@ -30,6 +26,7 @@ go_library(
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/limits",
|
||||
"//pkg/sentry/mm",
|
||||
"//pkg/sentry/socket",
|
||||
@@ -47,7 +44,7 @@ go_test(
|
||||
size = "small",
|
||||
srcs = [
|
||||
"boot_test.go",
|
||||
"net_test.go",
|
||||
"tasks_sys_test.go",
|
||||
"tasks_test.go",
|
||||
],
|
||||
embed = [":proc"],
|
||||
|
||||
@@ -67,3 +67,14 @@ func newDentry(creds *auth.Credentials, ino uint64, perm linux.FileMode, inode d
|
||||
d.Init(inode)
|
||||
return d
|
||||
}
|
||||
|
||||
type staticFile struct {
|
||||
kernfs.DynamicBytesFile
|
||||
vfs.StaticData
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*staticFile)(nil)
|
||||
|
||||
func newStaticFile(data string) *staticFile {
|
||||
return &staticFile{StaticData: vfs.StaticData{Data: data}}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2019 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
)
|
||||
|
||||
// loadavgData backs /proc/loadavg.
|
||||
//
|
||||
// +stateify savable
|
||||
type loadavgData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*loadavgData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *loadavgData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/62345059): Include real data in fields.
|
||||
// Column 1-3: CPU and IO utilization of the last 1, 5, and 10 minute periods.
|
||||
// Column 4-5: currently running processes and the total number of processes.
|
||||
// Column 6: the last process ID used.
|
||||
fmt.Fprintf(buf, "%.2f %.2f %.2f %d/%d %d\n", 0.00, 0.00, 0.00, 0, 0, 0)
|
||||
return nil
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2019 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usage"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
)
|
||||
|
||||
// meminfoData implements vfs.DynamicBytesSource for /proc/meminfo.
|
||||
//
|
||||
// +stateify savable
|
||||
type meminfoData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*meminfoData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *meminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
mf := d.k.MemoryFile()
|
||||
mf.UpdateUsage()
|
||||
snapshot, totalUsage := usage.MemoryAccounting.Copy()
|
||||
totalSize := usage.TotalMemory(mf.TotalSize(), totalUsage)
|
||||
anon := snapshot.Anonymous + snapshot.Tmpfs
|
||||
file := snapshot.PageCache + snapshot.Mapped
|
||||
// We don't actually have active/inactive LRUs, so just make up numbers.
|
||||
activeFile := (file / 2) &^ (usermem.PageSize - 1)
|
||||
inactiveFile := file - activeFile
|
||||
|
||||
fmt.Fprintf(buf, "MemTotal: %8d kB\n", totalSize/1024)
|
||||
memFree := (totalSize - totalUsage) / 1024
|
||||
// We use MemFree as MemAvailable because we don't swap.
|
||||
// TODO(rahat): When reclaim is implemented the value of MemAvailable
|
||||
// should change.
|
||||
fmt.Fprintf(buf, "MemFree: %8d kB\n", memFree)
|
||||
fmt.Fprintf(buf, "MemAvailable: %8d kB\n", memFree)
|
||||
fmt.Fprintf(buf, "Buffers: 0 kB\n") // memory usage by block devices
|
||||
fmt.Fprintf(buf, "Cached: %8d kB\n", (file+snapshot.Tmpfs)/1024)
|
||||
// Emulate a system with no swap, which disables inactivation of anon pages.
|
||||
fmt.Fprintf(buf, "SwapCache: 0 kB\n")
|
||||
fmt.Fprintf(buf, "Active: %8d kB\n", (anon+activeFile)/1024)
|
||||
fmt.Fprintf(buf, "Inactive: %8d kB\n", inactiveFile/1024)
|
||||
fmt.Fprintf(buf, "Active(anon): %8d kB\n", anon/1024)
|
||||
fmt.Fprintf(buf, "Inactive(anon): 0 kB\n")
|
||||
fmt.Fprintf(buf, "Active(file): %8d kB\n", activeFile/1024)
|
||||
fmt.Fprintf(buf, "Inactive(file): %8d kB\n", inactiveFile/1024)
|
||||
fmt.Fprintf(buf, "Unevictable: 0 kB\n") // TODO(b/31823263)
|
||||
fmt.Fprintf(buf, "Mlocked: 0 kB\n") // TODO(b/31823263)
|
||||
fmt.Fprintf(buf, "SwapTotal: 0 kB\n")
|
||||
fmt.Fprintf(buf, "SwapFree: 0 kB\n")
|
||||
fmt.Fprintf(buf, "Dirty: 0 kB\n")
|
||||
fmt.Fprintf(buf, "Writeback: 0 kB\n")
|
||||
fmt.Fprintf(buf, "AnonPages: %8d kB\n", anon/1024)
|
||||
fmt.Fprintf(buf, "Mapped: %8d kB\n", file/1024) // doesn't count mapped tmpfs, which we don't know
|
||||
fmt.Fprintf(buf, "Shmem: %8d kB\n", snapshot.Tmpfs/1024)
|
||||
return nil
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright 2019 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
)
|
||||
|
||||
// cpuStats contains the breakdown of CPU time for /proc/stat.
|
||||
type cpuStats struct {
|
||||
// user is time spent in userspace tasks with non-positive niceness.
|
||||
user uint64
|
||||
|
||||
// nice is time spent in userspace tasks with positive niceness.
|
||||
nice uint64
|
||||
|
||||
// system is time spent in non-interrupt kernel context.
|
||||
system uint64
|
||||
|
||||
// idle is time spent idle.
|
||||
idle uint64
|
||||
|
||||
// ioWait is time spent waiting for IO.
|
||||
ioWait uint64
|
||||
|
||||
// irq is time spent in interrupt context.
|
||||
irq uint64
|
||||
|
||||
// softirq is time spent in software interrupt context.
|
||||
softirq uint64
|
||||
|
||||
// steal is involuntary wait time.
|
||||
steal uint64
|
||||
|
||||
// guest is time spent in guests with non-positive niceness.
|
||||
guest uint64
|
||||
|
||||
// guestNice is time spent in guests with positive niceness.
|
||||
guestNice uint64
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (c cpuStats) String() string {
|
||||
return fmt.Sprintf("%d %d %d %d %d %d %d %d %d %d", c.user, c.nice, c.system, c.idle, c.ioWait, c.irq, c.softirq, c.steal, c.guest, c.guestNice)
|
||||
}
|
||||
|
||||
// statData implements vfs.DynamicBytesSource for /proc/stat.
|
||||
//
|
||||
// +stateify savable
|
||||
type statData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*statData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (s *statData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/37226836): We currently export only zero CPU stats. We could
|
||||
// at least provide some aggregate stats.
|
||||
var cpu cpuStats
|
||||
fmt.Fprintf(buf, "cpu %s\n", cpu)
|
||||
|
||||
for c, max := uint(0), s.k.ApplicationCores(); c < max; c++ {
|
||||
fmt.Fprintf(buf, "cpu%d %s\n", c, cpu)
|
||||
}
|
||||
|
||||
// The total number of interrupts is dependent on the CPUs and PCI
|
||||
// devices on the system. See arch_probe_nr_irqs.
|
||||
//
|
||||
// Since we don't report real interrupt stats, just choose an arbitrary
|
||||
// value from a representative VM.
|
||||
const numInterrupts = 256
|
||||
|
||||
// The Kernel doesn't handle real interrupts, so report all zeroes.
|
||||
// TODO(b/37226836): We could count page faults as #PF.
|
||||
fmt.Fprintf(buf, "intr 0") // total
|
||||
for i := 0; i < numInterrupts; i++ {
|
||||
fmt.Fprintf(buf, " 0")
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
|
||||
// Total number of context switches.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "ctxt 0\n")
|
||||
|
||||
// CLOCK_REALTIME timestamp from boot, in seconds.
|
||||
fmt.Fprintf(buf, "btime %d\n", s.k.Timekeeper().BootTime().Seconds())
|
||||
|
||||
// Total number of clones.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "processes 0\n")
|
||||
|
||||
// Number of runnable tasks.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "procs_running 0\n")
|
||||
|
||||
// Number of tasks waiting on IO.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "procs_blocked 0\n")
|
||||
|
||||
// Number of each softirq handled.
|
||||
fmt.Fprintf(buf, "softirq 0") // total
|
||||
for i := 0; i < linux.NumSoftIRQ; i++ {
|
||||
fmt.Fprintf(buf, " 0")
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
return nil
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2019 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// mmapMinAddrData implements vfs.DynamicBytesSource for
|
||||
// /proc/sys/vm/mmap_min_addr.
|
||||
//
|
||||
// +stateify savable
|
||||
type mmapMinAddrData struct {
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ vfs.DynamicBytesSource = (*mmapMinAddrData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *mmapMinAddrData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
fmt.Fprintf(buf, "%d\n", d.k.Platform.MinUserAddress())
|
||||
return nil
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type overcommitMemory struct{}
|
||||
|
||||
var _ vfs.DynamicBytesSource = (*overcommitMemory)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *overcommitMemory) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
fmt.Fprintf(buf, "0\n")
|
||||
return nil
|
||||
}
|
||||
@@ -50,15 +50,15 @@ func newTaskInode(inoGen InoGenerator, task *kernel.Task, pidns *kernel.PIDNames
|
||||
//"fd": newFdDir(t, msrc),
|
||||
//"fdinfo": newFdInfoDir(t, msrc),
|
||||
//"gid_map": newGIDMap(t, msrc),
|
||||
"io": newTaskOwnedFile(task, inoGen.NextIno(), defaultPermission, newIO(task, isThreadGroup)),
|
||||
"maps": newTaskOwnedFile(task, inoGen.NextIno(), defaultPermission, &mapsData{task: task}),
|
||||
"io": newTaskOwnedFile(task, inoGen.NextIno(), 0400, newIO(task, isThreadGroup)),
|
||||
"maps": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &mapsData{task: task}),
|
||||
//"mountinfo": seqfile.NewSeqFileInode(t, &mountInfoFile{t: t}, msrc),
|
||||
//"mounts": seqfile.NewSeqFileInode(t, &mountsFile{t: t}, msrc),
|
||||
//"ns": newNamespaceDir(t, msrc),
|
||||
"smaps": newTaskOwnedFile(task, inoGen.NextIno(), defaultPermission, &smapsData{task: task}),
|
||||
"stat": newTaskOwnedFile(task, inoGen.NextIno(), defaultPermission, &taskStatData{t: task, pidns: pidns, tgstats: isThreadGroup}),
|
||||
"statm": newTaskOwnedFile(task, inoGen.NextIno(), defaultPermission, &statmData{t: task}),
|
||||
"status": newTaskOwnedFile(task, inoGen.NextIno(), defaultPermission, &statusData{t: task, pidns: pidns}),
|
||||
"smaps": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &smapsData{task: task}),
|
||||
"stat": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &taskStatData{t: task, pidns: pidns, tgstats: isThreadGroup}),
|
||||
"statm": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &statmData{t: task}),
|
||||
"status": newTaskOwnedFile(task, inoGen.NextIno(), 0444, &statusData{t: task, pidns: pidns}),
|
||||
//"uid_map": newUIDMap(t, msrc),
|
||||
}
|
||||
if isThreadGroup {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
@@ -28,9 +29,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPermission = 0444
|
||||
selfName = "self"
|
||||
threadSelfName = "thread-self"
|
||||
selfName = "self"
|
||||
threadSelfName = "thread-self"
|
||||
)
|
||||
|
||||
// InoGenerator generates unique inode numbers for a given filesystem.
|
||||
@@ -61,15 +61,15 @@ var _ kernfs.Inode = (*tasksInode)(nil)
|
||||
func newTasksInode(inoGen InoGenerator, k *kernel.Kernel, pidns *kernel.PIDNamespace) (*tasksInode, *kernfs.Dentry) {
|
||||
root := auth.NewRootCredentials(pidns.UserNamespace())
|
||||
contents := map[string]*kernfs.Dentry{
|
||||
//"cpuinfo": newCPUInfo(ctx, msrc),
|
||||
//"filesystems": seqfile.NewSeqFileInode(ctx, &filesystemsData{}, msrc),
|
||||
"loadavg": newDentry(root, inoGen.NextIno(), defaultPermission, &loadavgData{}),
|
||||
"meminfo": newDentry(root, inoGen.NextIno(), defaultPermission, &meminfoData{k: k}),
|
||||
"mounts": kernfs.NewStaticSymlink(root, inoGen.NextIno(), defaultPermission, "self/mounts"),
|
||||
"stat": newDentry(root, inoGen.NextIno(), defaultPermission, &statData{k: k}),
|
||||
//"uptime": newUptime(ctx, msrc),
|
||||
//"version": newVersionData(root, inoGen.NextIno(), k),
|
||||
"version": newDentry(root, inoGen.NextIno(), defaultPermission, &versionData{k: k}),
|
||||
"cpuinfo": newDentry(root, inoGen.NextIno(), 0444, newStaticFile(cpuInfoData(k))),
|
||||
//"filesystems": newDentry(root, inoGen.NextIno(), 0444, &filesystemsData{}),
|
||||
"loadavg": newDentry(root, inoGen.NextIno(), 0444, &loadavgData{}),
|
||||
"sys": newSysDir(root, inoGen),
|
||||
"meminfo": newDentry(root, inoGen.NextIno(), 0444, &meminfoData{}),
|
||||
"mounts": kernfs.NewStaticSymlink(root, inoGen.NextIno(), 0777, "self/mounts"),
|
||||
"stat": newDentry(root, inoGen.NextIno(), 0444, &statData{}),
|
||||
"uptime": newDentry(root, inoGen.NextIno(), 0444, &uptimeData{}),
|
||||
"version": newDentry(root, inoGen.NextIno(), 0444, &versionData{}),
|
||||
}
|
||||
|
||||
inode := &tasksInode{
|
||||
@@ -216,3 +216,20 @@ func (i *tasksInode) Stat(vsfs *vfs.Filesystem) linux.Statx {
|
||||
|
||||
return stat
|
||||
}
|
||||
|
||||
func cpuInfoData(k *kernel.Kernel) string {
|
||||
features := k.FeatureSet()
|
||||
if features == nil {
|
||||
// Kernel is always initialized with a FeatureSet.
|
||||
panic("cpuinfo read with nil FeatureSet")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for i, max := uint(0), k.ApplicationCores(); i < max; i++ {
|
||||
features.WriteCPUInfoTo(i, &buf)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func shmData(v uint64) dynamicInode {
|
||||
return newStaticFile(strconv.FormatUint(v, 10))
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
@@ -23,6 +24,9 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usage"
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
@@ -90,3 +94,244 @@ func (s *threadSelfSymlink) Readlink(ctx context.Context) (string, error) {
|
||||
}
|
||||
return fmt.Sprintf("%d/task/%d", tgid, tid), nil
|
||||
}
|
||||
|
||||
// cpuStats contains the breakdown of CPU time for /proc/stat.
|
||||
type cpuStats struct {
|
||||
// user is time spent in userspace tasks with non-positive niceness.
|
||||
user uint64
|
||||
|
||||
// nice is time spent in userspace tasks with positive niceness.
|
||||
nice uint64
|
||||
|
||||
// system is time spent in non-interrupt kernel context.
|
||||
system uint64
|
||||
|
||||
// idle is time spent idle.
|
||||
idle uint64
|
||||
|
||||
// ioWait is time spent waiting for IO.
|
||||
ioWait uint64
|
||||
|
||||
// irq is time spent in interrupt context.
|
||||
irq uint64
|
||||
|
||||
// softirq is time spent in software interrupt context.
|
||||
softirq uint64
|
||||
|
||||
// steal is involuntary wait time.
|
||||
steal uint64
|
||||
|
||||
// guest is time spent in guests with non-positive niceness.
|
||||
guest uint64
|
||||
|
||||
// guestNice is time spent in guests with positive niceness.
|
||||
guestNice uint64
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (c cpuStats) String() string {
|
||||
return fmt.Sprintf("%d %d %d %d %d %d %d %d %d %d", c.user, c.nice, c.system, c.idle, c.ioWait, c.irq, c.softirq, c.steal, c.guest, c.guestNice)
|
||||
}
|
||||
|
||||
// statData implements vfs.DynamicBytesSource for /proc/stat.
|
||||
//
|
||||
// +stateify savable
|
||||
type statData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*statData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (s *statData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/37226836): We currently export only zero CPU stats. We could
|
||||
// at least provide some aggregate stats.
|
||||
var cpu cpuStats
|
||||
fmt.Fprintf(buf, "cpu %s\n", cpu)
|
||||
|
||||
for c, max := uint(0), s.k.ApplicationCores(); c < max; c++ {
|
||||
fmt.Fprintf(buf, "cpu%d %s\n", c, cpu)
|
||||
}
|
||||
|
||||
// The total number of interrupts is dependent on the CPUs and PCI
|
||||
// devices on the system. See arch_probe_nr_irqs.
|
||||
//
|
||||
// Since we don't report real interrupt stats, just choose an arbitrary
|
||||
// value from a representative VM.
|
||||
const numInterrupts = 256
|
||||
|
||||
// The Kernel doesn't handle real interrupts, so report all zeroes.
|
||||
// TODO(b/37226836): We could count page faults as #PF.
|
||||
fmt.Fprintf(buf, "intr 0") // total
|
||||
for i := 0; i < numInterrupts; i++ {
|
||||
fmt.Fprintf(buf, " 0")
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
|
||||
// Total number of context switches.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "ctxt 0\n")
|
||||
|
||||
// CLOCK_REALTIME timestamp from boot, in seconds.
|
||||
fmt.Fprintf(buf, "btime %d\n", s.k.Timekeeper().BootTime().Seconds())
|
||||
|
||||
// Total number of clones.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "processes 0\n")
|
||||
|
||||
// Number of runnable tasks.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "procs_running 0\n")
|
||||
|
||||
// Number of tasks waiting on IO.
|
||||
// TODO(b/37226836): Count this.
|
||||
fmt.Fprintf(buf, "procs_blocked 0\n")
|
||||
|
||||
// Number of each softirq handled.
|
||||
fmt.Fprintf(buf, "softirq 0") // total
|
||||
for i := 0; i < linux.NumSoftIRQ; i++ {
|
||||
fmt.Fprintf(buf, " 0")
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadavgData backs /proc/loadavg.
|
||||
//
|
||||
// +stateify savable
|
||||
type loadavgData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*loadavgData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *loadavgData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
// TODO(b/62345059): Include real data in fields.
|
||||
// Column 1-3: CPU and IO utilization of the last 1, 5, and 10 minute periods.
|
||||
// Column 4-5: currently running processes and the total number of processes.
|
||||
// Column 6: the last process ID used.
|
||||
fmt.Fprintf(buf, "%.2f %.2f %.2f %d/%d %d\n", 0.00, 0.00, 0.00, 0, 0, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// meminfoData implements vfs.DynamicBytesSource for /proc/meminfo.
|
||||
//
|
||||
// +stateify savable
|
||||
type meminfoData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*meminfoData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *meminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
mf := d.k.MemoryFile()
|
||||
mf.UpdateUsage()
|
||||
snapshot, totalUsage := usage.MemoryAccounting.Copy()
|
||||
totalSize := usage.TotalMemory(mf.TotalSize(), totalUsage)
|
||||
anon := snapshot.Anonymous + snapshot.Tmpfs
|
||||
file := snapshot.PageCache + snapshot.Mapped
|
||||
// We don't actually have active/inactive LRUs, so just make up numbers.
|
||||
activeFile := (file / 2) &^ (usermem.PageSize - 1)
|
||||
inactiveFile := file - activeFile
|
||||
|
||||
fmt.Fprintf(buf, "MemTotal: %8d kB\n", totalSize/1024)
|
||||
memFree := (totalSize - totalUsage) / 1024
|
||||
// We use MemFree as MemAvailable because we don't swap.
|
||||
// TODO(rahat): When reclaim is implemented the value of MemAvailable
|
||||
// should change.
|
||||
fmt.Fprintf(buf, "MemFree: %8d kB\n", memFree)
|
||||
fmt.Fprintf(buf, "MemAvailable: %8d kB\n", memFree)
|
||||
fmt.Fprintf(buf, "Buffers: 0 kB\n") // memory usage by block devices
|
||||
fmt.Fprintf(buf, "Cached: %8d kB\n", (file+snapshot.Tmpfs)/1024)
|
||||
// Emulate a system with no swap, which disables inactivation of anon pages.
|
||||
fmt.Fprintf(buf, "SwapCache: 0 kB\n")
|
||||
fmt.Fprintf(buf, "Active: %8d kB\n", (anon+activeFile)/1024)
|
||||
fmt.Fprintf(buf, "Inactive: %8d kB\n", inactiveFile/1024)
|
||||
fmt.Fprintf(buf, "Active(anon): %8d kB\n", anon/1024)
|
||||
fmt.Fprintf(buf, "Inactive(anon): 0 kB\n")
|
||||
fmt.Fprintf(buf, "Active(file): %8d kB\n", activeFile/1024)
|
||||
fmt.Fprintf(buf, "Inactive(file): %8d kB\n", inactiveFile/1024)
|
||||
fmt.Fprintf(buf, "Unevictable: 0 kB\n") // TODO(b/31823263)
|
||||
fmt.Fprintf(buf, "Mlocked: 0 kB\n") // TODO(b/31823263)
|
||||
fmt.Fprintf(buf, "SwapTotal: 0 kB\n")
|
||||
fmt.Fprintf(buf, "SwapFree: 0 kB\n")
|
||||
fmt.Fprintf(buf, "Dirty: 0 kB\n")
|
||||
fmt.Fprintf(buf, "Writeback: 0 kB\n")
|
||||
fmt.Fprintf(buf, "AnonPages: %8d kB\n", anon/1024)
|
||||
fmt.Fprintf(buf, "Mapped: %8d kB\n", file/1024) // doesn't count mapped tmpfs, which we don't know
|
||||
fmt.Fprintf(buf, "Shmem: %8d kB\n", snapshot.Tmpfs/1024)
|
||||
return nil
|
||||
}
|
||||
|
||||
// uptimeData implements vfs.DynamicBytesSource for /proc/uptime.
|
||||
//
|
||||
// +stateify savable
|
||||
type uptimeData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*uptimeData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (*uptimeData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
now := time.NowFromContext(ctx)
|
||||
|
||||
// Pretend that we've spent zero time sleeping (second number).
|
||||
fmt.Fprintf(buf, "%.2f 0.00\n", now.Sub(k.Timekeeper().BootTime()).Seconds())
|
||||
return nil
|
||||
}
|
||||
|
||||
// versionData implements vfs.DynamicBytesSource for /proc/version.
|
||||
//
|
||||
// +stateify savable
|
||||
type versionData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*versionData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (v *versionData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
init := v.k.GlobalInit()
|
||||
if init == nil {
|
||||
// Attempted to read before the init Task is created. This can
|
||||
// only occur during startup, which should never need to read
|
||||
// this file.
|
||||
panic("Attempted to read version before initial Task is available")
|
||||
}
|
||||
|
||||
// /proc/version takes the form:
|
||||
//
|
||||
// "SYSNAME version RELEASE (COMPILE_USER@COMPILE_HOST)
|
||||
// (COMPILER_VERSION) VERSION"
|
||||
//
|
||||
// where:
|
||||
// - SYSNAME, RELEASE, and VERSION are the same as returned by
|
||||
// sys_utsname
|
||||
// - COMPILE_USER is the user that build the kernel
|
||||
// - COMPILE_HOST is the hostname of the machine on which the kernel
|
||||
// was built
|
||||
// - COMPILER_VERSION is the version reported by the building compiler
|
||||
//
|
||||
// Since we don't really want to expose build information to
|
||||
// applications, those fields are omitted.
|
||||
//
|
||||
// FIXME(mpratt): Using Version from the init task SyscallTable
|
||||
// disregards the different version a task may have (e.g., in a uts
|
||||
// namespace).
|
||||
ver := init.Leader().SyscallTable().Version
|
||||
fmt.Fprintf(buf, "%s version %s %s\n", ver.Sysname, ver.Release, ver.Version)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -46,8 +46,7 @@ func (n *ifinet6) contents() []string {
|
||||
for id, naddrs := range n.s.InterfaceAddrs() {
|
||||
nic, ok := nics[id]
|
||||
if !ok {
|
||||
// NIC was added after NICNames was called. We'll just
|
||||
// ignore it.
|
||||
// NIC was added after NICNames was called. We'll just ignore it.
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2019 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
)
|
||||
|
||||
// newSysDir returns the dentry corresponding to /proc/sys directory.
|
||||
func newSysDir(root *auth.Credentials, inoGen InoGenerator) *kernfs.Dentry {
|
||||
return kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
"kernel": kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
"hostname": newDentry(root, inoGen.NextIno(), 0444, &hostnameData{}),
|
||||
"shmall": newDentry(root, inoGen.NextIno(), 0444, shmData(linux.SHMALL)),
|
||||
"shmmax": newDentry(root, inoGen.NextIno(), 0444, shmData(linux.SHMMAX)),
|
||||
"shmmni": newDentry(root, inoGen.NextIno(), 0444, shmData(linux.SHMMNI)),
|
||||
}),
|
||||
"vm": kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
"mmap_min_addr": newDentry(root, inoGen.NextIno(), 0444, &mmapMinAddrData{}),
|
||||
"overcommit_memory": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0\n")),
|
||||
}),
|
||||
"net": newSysNetDir(root, inoGen),
|
||||
})
|
||||
}
|
||||
|
||||
// newSysNetDir returns the dentry corresponding to /proc/sys/net directory.
|
||||
func newSysNetDir(root *auth.Credentials, inoGen InoGenerator) *kernfs.Dentry {
|
||||
return kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
"net": kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
"ipv4": kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
// Add tcp_sack.
|
||||
// TODO(gvisor.dev/issue/1195): tcp_sack allows write(2)
|
||||
// "tcp_sack": newTCPSackInode(ctx, msrc, s),
|
||||
|
||||
// The following files are simple stubs until they are implemented in
|
||||
// netstack, most of these files are configuration related. We use the
|
||||
// value closest to the actual netstack behavior or any empty file, all
|
||||
// of these files will have mode 0444 (read-only for all users).
|
||||
"ip_local_port_range": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("16000 65535")),
|
||||
"ip_local_reserved_ports": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("")),
|
||||
"ipfrag_time": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("30")),
|
||||
"ip_nonlocal_bind": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"ip_no_pmtu_disc": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("1")),
|
||||
|
||||
// tcp_allowed_congestion_control tell the user what they are able to
|
||||
// do as an unprivledged process so we leave it empty.
|
||||
"tcp_allowed_congestion_control": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("")),
|
||||
"tcp_available_congestion_control": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("reno")),
|
||||
"tcp_congestion_control": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("reno")),
|
||||
|
||||
// Many of the following stub files are features netstack doesn't
|
||||
// support. The unsupported features return "0" to indicate they are
|
||||
// disabled.
|
||||
"tcp_base_mss": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("1280")),
|
||||
"tcp_dsack": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_early_retrans": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_fack": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_fastopen": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_fastopen_key": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("")),
|
||||
"tcp_invalid_ratelimit": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_keepalive_intvl": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_keepalive_probes": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_keepalive_time": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("7200")),
|
||||
"tcp_mtu_probing": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_no_metrics_save": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("1")),
|
||||
"tcp_probe_interval": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_probe_threshold": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"tcp_retries1": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("3")),
|
||||
"tcp_retries2": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("15")),
|
||||
"tcp_rfc1337": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("1")),
|
||||
"tcp_slow_start_after_idle": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("1")),
|
||||
"tcp_synack_retries": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("5")),
|
||||
"tcp_syn_retries": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("3")),
|
||||
"tcp_timestamps": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("1")),
|
||||
}),
|
||||
"core": kernfs.NewStaticDir(root, inoGen.NextIno(), 0555, map[string]*kernfs.Dentry{
|
||||
"default_qdisc": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("pfifo_fast")),
|
||||
"message_burst": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("10")),
|
||||
"message_cost": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("5")),
|
||||
"optmem_max": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("0")),
|
||||
"rmem_default": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("212992")),
|
||||
"rmem_max": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("212992")),
|
||||
"somaxconn": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("128")),
|
||||
"wmem_default": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("212992")),
|
||||
"wmem_max": newDentry(root, inoGen.NextIno(), 0444, newStaticFile("212992")),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
// mmapMinAddrData implements vfs.DynamicBytesSource for
|
||||
// /proc/sys/vm/mmap_min_addr.
|
||||
//
|
||||
// +stateify savable
|
||||
type mmapMinAddrData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*mmapMinAddrData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *mmapMinAddrData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
fmt.Fprintf(buf, "%d\n", d.k.Platform.MinUserAddress())
|
||||
return nil
|
||||
}
|
||||
|
||||
// hostnameData implements vfs.DynamicBytesSource for /proc/sys/kernel/hostname.
|
||||
//
|
||||
// +stateify savable
|
||||
type hostnameData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*hostnameData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (*hostnameData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
utsns := kernel.UTSNamespaceFromContext(ctx)
|
||||
buf.WriteString(utsns.HostName())
|
||||
buf.WriteString("\n")
|
||||
return nil
|
||||
}
|
||||
@@ -69,12 +69,15 @@ func checkDots(dirs []vfs.Dirent) ([]vfs.Dirent, error) {
|
||||
|
||||
func checkTasksStaticFiles(gots []vfs.Dirent) ([]vfs.Dirent, error) {
|
||||
wants := map[string]vfs.Dirent{
|
||||
"cpuinfo": {Type: linux.DT_REG},
|
||||
"loadavg": {Type: linux.DT_REG},
|
||||
"meminfo": {Type: linux.DT_REG},
|
||||
"mounts": {Type: linux.DT_LNK},
|
||||
"self": selfLink,
|
||||
"stat": {Type: linux.DT_REG},
|
||||
"sys": {Type: linux.DT_DIR},
|
||||
"thread-self": threadSelfLink,
|
||||
"uptime": {Type: linux.DT_REG},
|
||||
"version": {Type: linux.DT_REG},
|
||||
}
|
||||
return checkFiles(gots, wants)
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2019 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 proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
)
|
||||
|
||||
// versionData implements vfs.DynamicBytesSource for /proc/version.
|
||||
//
|
||||
// +stateify savable
|
||||
type versionData struct {
|
||||
kernfs.DynamicBytesFile
|
||||
|
||||
// k is the owning Kernel.
|
||||
k *kernel.Kernel
|
||||
}
|
||||
|
||||
var _ dynamicInode = (*versionData)(nil)
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (v *versionData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
init := v.k.GlobalInit()
|
||||
if init == nil {
|
||||
// Attempted to read before the init Task is created. This can
|
||||
// only occur during startup, which should never need to read
|
||||
// this file.
|
||||
panic("Attempted to read version before initial Task is available")
|
||||
}
|
||||
|
||||
// /proc/version takes the form:
|
||||
//
|
||||
// "SYSNAME version RELEASE (COMPILE_USER@COMPILE_HOST)
|
||||
// (COMPILER_VERSION) VERSION"
|
||||
//
|
||||
// where:
|
||||
// - SYSNAME, RELEASE, and VERSION are the same as returned by
|
||||
// sys_utsname
|
||||
// - COMPILE_USER is the user that build the kernel
|
||||
// - COMPILE_HOST is the hostname of the machine on which the kernel
|
||||
// was built
|
||||
// - COMPILER_VERSION is the version reported by the building compiler
|
||||
//
|
||||
// Since we don't really want to expose build information to
|
||||
// applications, those fields are omitted.
|
||||
//
|
||||
// FIXME(mpratt): Using Version from the init task SyscallTable
|
||||
// disregards the different version a task may have (e.g., in a uts
|
||||
// namespace).
|
||||
ver := init.Leader().SyscallTable().Version
|
||||
fmt.Fprintf(buf, "%s version %s %s\n", ver.Sysname, ver.Release, ver.Version)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user