From 74e397e39accbeb9fcb5382ce963df55014ae7ce Mon Sep 17 00:00:00 2001 From: Ian Lewis Date: Mon, 10 Jun 2019 23:37:32 -0700 Subject: [PATCH] Add introspection for Linux/AMD64 syscalls Adds simple introspection for syscall compatibility information to Linux/AMD64. Syscalls registered in the syscall table now have associated metadata like name, support level, notes, and URLs to relevant issues. Syscall information can be exported as a table, JSON, or CSV using the new 'runsc help syscalls' command. Users can use this info to debug and get info on the compatibility of the version of runsc they are running or to generate documentation. PiperOrigin-RevId: 252558304 --- WORKSPACE | 2 +- go.mod | 8 +- pkg/abi/linux/capability.go | 84 +++ pkg/sentry/kernel/syscalls.go | 60 ++- pkg/sentry/kernel/table_test.go | 8 +- pkg/sentry/syscalls/linux/linux64.go | 755 ++++++++++++--------------- pkg/sentry/syscalls/syscalls.go | 88 +++- runsc/cmd/BUILD | 3 + runsc/cmd/help.go | 126 +++++ runsc/cmd/syscalls.go | 347 ++++++++++++ runsc/main.go | 4 +- 11 files changed, 1032 insertions(+), 453 deletions(-) create mode 100644 runsc/cmd/help.go create mode 100644 runsc/cmd/syscalls.go diff --git a/WORKSPACE b/WORKSPACE index 421453a87..5155dc527 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -62,7 +62,7 @@ go_repository( go_repository( name = "com_github_google_subcommands", - commit = "ce3d4cfc062f", + commit = "636abe8753b8", importpath = "github.com/google/subcommands", ) diff --git a/go.mod b/go.mod index 9a6b27c6b..e58b84cfb 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,19 @@ module gvisor.googlesource.com/gvisor go 1.12 require ( - github.com/cenkalti/backoff v2.2.0 + github.com/cenkalti/backoff v0.0.0-20190506075156-2146c9339422 github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079 github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.1 github.com/google/btree v1.0.0 github.com/google/go-cmp v0.2.0 - github.com/google/subcommands v0.0.0-20170224175846-ce3d4cfc062f + github.com/google/subcommands v0.0.0-20190508160503-636abe8753b8 github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d github.com/kr/pty v1.1.1 github.com/opencontainers/runtime-spec v0.1.2-0.20171211145439-b2d941ef6a78 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/vishvananda/netlink v1.0.1-0.20190318003149-adb577d4a45e github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936 - golang.org/x/net v0.0.0-20180404174746-b3c676e531a6 - golang.org/x/sys v0.0.0-20171117071000-0dd5e194bbf5 + golang.org/x/net v0.0.0-20190311183353-d8887717615a + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a ) diff --git a/pkg/abi/linux/capability.go b/pkg/abi/linux/capability.go index c120cac64..65dd77e6e 100644 --- a/pkg/abi/linux/capability.go +++ b/pkg/abi/linux/capability.go @@ -69,6 +69,90 @@ func (cp Capability) Ok() bool { return cp >= 0 && cp <= MaxCapability } +// String returns the capability name. +func (cp Capability) String() string { + switch cp { + case CAP_CHOWN: + return "CAP_CHOWN" + case CAP_DAC_OVERRIDE: + return "CAP_DAC_OVERRIDE" + case CAP_DAC_READ_SEARCH: + return "CAP_DAC_READ_SEARCH" + case CAP_FOWNER: + return "CAP_FOWNER" + case CAP_FSETID: + return "CAP_FSETID" + case CAP_KILL: + return "CAP_KILL" + case CAP_SETGID: + return "CAP_SETGID" + case CAP_SETUID: + return "CAP_SETUID" + case CAP_SETPCAP: + return "CAP_SETPCAP" + case CAP_LINUX_IMMUTABLE: + return "CAP_LINUX_IMMUTABLE" + case CAP_NET_BIND_SERVICE: + return "CAP_NET_BIND_SERVICE" + case CAP_NET_BROADCAST: + return "CAP_NET_BROADCAST" + case CAP_NET_ADMIN: + return "CAP_NET_ADMIN" + case CAP_NET_RAW: + return "CAP_NET_RAW" + case CAP_IPC_LOCK: + return "CAP_IPC_LOCK" + case CAP_IPC_OWNER: + return "CAP_IPC_OWNER" + case CAP_SYS_MODULE: + return "CAP_SYS_MODULE" + case CAP_SYS_RAWIO: + return "CAP_SYS_RAWIO" + case CAP_SYS_CHROOT: + return "CAP_SYS_CHROOT" + case CAP_SYS_PTRACE: + return "CAP_SYS_PTRACE" + case CAP_SYS_PACCT: + return "CAP_SYS_PACCT" + case CAP_SYS_ADMIN: + return "CAP_SYS_ADMIN" + case CAP_SYS_BOOT: + return "CAP_SYS_BOOT" + case CAP_SYS_NICE: + return "CAP_SYS_NICE" + case CAP_SYS_RESOURCE: + return "CAP_SYS_RESOURCE" + case CAP_SYS_TIME: + return "CAP_SYS_TIME" + case CAP_SYS_TTY_CONFIG: + return "CAP_SYS_TTY_CONFIG" + case CAP_MKNOD: + return "CAP_MKNOD" + case CAP_LEASE: + return "CAP_LEASE" + case CAP_AUDIT_WRITE: + return "CAP_AUDIT_WRITE" + case CAP_AUDIT_CONTROL: + return "CAP_AUDIT_CONTROL" + case CAP_SETFCAP: + return "CAP_SETFCAP" + case CAP_MAC_OVERRIDE: + return "CAP_MAC_OVERRIDE" + case CAP_MAC_ADMIN: + return "CAP_MAC_ADMIN" + case CAP_SYSLOG: + return "CAP_SYSLOG" + case CAP_WAKE_ALARM: + return "CAP_WAKE_ALARM" + case CAP_BLOCK_SUSPEND: + return "CAP_BLOCK_SUSPEND" + case CAP_AUDIT_READ: + return "CAP_AUDIT_READ" + default: + return "UNKNOWN" + } +} + // Version numbers used by the capget/capset syscalls, defined in Linux's // include/uapi/linux/capability.h. const ( diff --git a/pkg/sentry/kernel/syscalls.go b/pkg/sentry/kernel/syscalls.go index 0572053db..27cd3728b 100644 --- a/pkg/sentry/kernel/syscalls.go +++ b/pkg/sentry/kernel/syscalls.go @@ -32,6 +32,51 @@ import ( // syscall. const maxSyscallNum = 2000 +// SyscallSupportLevel is a syscall support levels. +type SyscallSupportLevel int + +// String returns a human readable represetation of the support level. +func (l SyscallSupportLevel) String() string { + switch l { + case SupportUnimplemented: + return "Unimplemented" + case SupportPartial: + return "Partial Support" + case SupportFull: + return "Full Support" + default: + return "Undocumented" + } +} + +const ( + // SupportUndocumented indicates the syscall is not documented yet. + SupportUndocumented = iota + + // SupportUnimplemented indicates the syscall is unimplemented. + SupportUnimplemented + + // SupportPartial indicates the syscall is partially supported. + SupportPartial + + // SupportFull indicates the syscall is fully supported. + SupportFull +) + +// Syscall includes the syscall implementation and compatibility information. +type Syscall struct { + // Name is the syscall name. + Name string + // Fn is the implementation of the syscall. + Fn SyscallFn + // SupportLevel is the level of support implemented in gVisor. + SupportLevel SyscallSupportLevel + // Note describes the compatibility of the syscall. + Note string + // URLs is set of URLs to any relevant bugs or issues. + URLs []string +} + // SyscallFn is a syscall implementation. type SyscallFn func(t *Task, args arch.SyscallArguments) (uintptr, *SyscallControl, error) @@ -83,7 +128,7 @@ type SyscallFlagsTable struct { // Init initializes the struct, with all syscalls in table set to enable. // // max is the largest syscall number in table. -func (e *SyscallFlagsTable) init(table map[uintptr]SyscallFn, max uintptr) { +func (e *SyscallFlagsTable) init(table map[uintptr]Syscall, max uintptr) { e.enable = make([]uint32, max+1) for num := range table { e.enable[num] = syscallPresent @@ -194,7 +239,7 @@ type SyscallTable struct { AuditNumber uint32 `state:"manual"` // Table is the collection of functions. - Table map[uintptr]SyscallFn `state:"manual"` + Table map[uintptr]Syscall `state:"manual"` // lookup is a fixed-size array that holds the syscalls (indexed by // their numbers). It is used for fast look ups. @@ -247,7 +292,7 @@ func LookupSyscallTable(os abi.OS, a arch.Arch) (*SyscallTable, bool) { func RegisterSyscallTable(s *SyscallTable) { if s.Table == nil { // Ensure non-nil lookup table. - s.Table = make(map[uintptr]SyscallFn) + s.Table = make(map[uintptr]Syscall) } if s.Emulate == nil { // Ensure non-nil emulate table. @@ -268,8 +313,8 @@ func RegisterSyscallTable(s *SyscallTable) { s.lookup = make([]SyscallFn, max+1) // Initialize the fast-lookup table. - for num, fn := range s.Table { - s.lookup[num] = fn + for num, sc := range s.Table { + s.lookup[num] = sc.Fn } s.FeatureEnable.init(s.Table, max) @@ -303,5 +348,8 @@ func (s *SyscallTable) LookupEmulate(addr usermem.Addr) (uintptr, bool) { // mapLookup is similar to Lookup, except that it only uses the syscall table, // that is, it skips the fast look array. This is available for benchmarking. func (s *SyscallTable) mapLookup(sysno uintptr) SyscallFn { - return s.Table[sysno] + if sc, ok := s.Table[sysno]; ok { + return sc.Fn + } + return nil } diff --git a/pkg/sentry/kernel/table_test.go b/pkg/sentry/kernel/table_test.go index 8f7cdb9f3..3f2b042c8 100644 --- a/pkg/sentry/kernel/table_test.go +++ b/pkg/sentry/kernel/table_test.go @@ -26,11 +26,13 @@ const ( ) func createSyscallTable() *SyscallTable { - m := make(map[uintptr]SyscallFn) + m := make(map[uintptr]Syscall) for i := uintptr(0); i <= maxTestSyscall; i++ { j := i - m[i] = func(*Task, arch.SyscallArguments) (uintptr, *SyscallControl, error) { - return j, nil, nil + m[i] = Syscall{ + Fn: func(*Task, arch.SyscallArguments) (uintptr, *SyscallControl, error) { + return j, nil, nil + }, } } diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index ad88b1391..5251c2463 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -34,33 +34,6 @@ const _AUDIT_ARCH_X86_64 = 0xc000003e // AMD64 is a table of Linux amd64 syscall API with the corresponding syscall // numbers from Linux 4.4. The entries commented out are those syscalls we // don't currently support. -// -// Syscall support is documented as annotations in Go comments of the form: -// @Syscall(, , ...) -// -// Supported args and values are: -// -// - arg: A syscall option. This entry only applies to the syscall when given -// this option. -// - support: Indicates support level -// - UNIMPLEMENTED: Unimplemented (default, implies returns:ENOSYS) -// - PARTIAL: Partial support. Details should be provided in note. -// - FULL: Full support -// - returns: Indicates a known return value. Values are syscall errors. This -// is treated as a string so you can use something like -// "returns:EPERM or ENOSYS". -// - issue: A Github issue number. -// - note: A note -// -// Example: -// // @Syscall(mmap, arg:MAP_PRIVATE, support:FULL, note:Private memory fully supported) -// // @Syscall(mmap, arg:MAP_SHARED, issue:123, note:Shared memory not supported) -// // @Syscall(setxattr, returns:ENOTSUP, note:Requires file system support) -// -// Annotations should be placed as close to their implementation as possible -// (preferrably as part of a supporting function's Godoc) and should be -// updated as syscall support changes. Unimplemented syscalls are documented -// here due to their lack of a supporting function or method. var AMD64 = &kernel.SyscallTable{ OS: abi.Linux, Arch: arch.AMD64, @@ -74,404 +47,338 @@ var AMD64 = &kernel.SyscallTable{ Version: "#1 SMP Sun Jan 10 15:06:54 PST 2016", }, AuditNumber: _AUDIT_ARCH_X86_64, - Table: map[uintptr]kernel.SyscallFn{ - 0: Read, - 1: Write, - 2: Open, - 3: Close, - 4: Stat, - 5: Fstat, - 6: Lstat, - 7: Poll, - 8: Lseek, - 9: Mmap, - 10: Mprotect, - 11: Munmap, - 12: Brk, - 13: RtSigaction, - 14: RtSigprocmask, - 15: RtSigreturn, - 16: Ioctl, - 17: Pread64, - 18: Pwrite64, - 19: Readv, - 20: Writev, - 21: Access, - 22: Pipe, - 23: Select, - 24: SchedYield, - 25: Mremap, - 26: Msync, - 27: Mincore, - 28: Madvise, - 29: Shmget, - 30: Shmat, - 31: Shmctl, - 32: Dup, - 33: Dup2, - 34: Pause, - 35: Nanosleep, - 36: Getitimer, - 37: Alarm, - 38: Setitimer, - 39: Getpid, - 40: Sendfile, - 41: Socket, - 42: Connect, - 43: Accept, - 44: SendTo, - 45: RecvFrom, - 46: SendMsg, - 47: RecvMsg, - 48: Shutdown, - 49: Bind, - 50: Listen, - 51: GetSockName, - 52: GetPeerName, - 53: SocketPair, - 54: SetSockOpt, - 55: GetSockOpt, - 56: Clone, - 57: Fork, - 58: Vfork, - 59: Execve, - 60: Exit, - 61: Wait4, - 62: Kill, - 63: Uname, - 64: Semget, - 65: Semop, - 66: Semctl, - 67: Shmdt, - // 68: @Syscall(Msgget), TODO(b/29354921) - // 69: @Syscall(Msgsnd), TODO(b/29354921) - // 70: @Syscall(Msgrcv), TODO(b/29354921) - // 71: @Syscall(Msgctl), TODO(b/29354921) - 72: Fcntl, - 73: Flock, - 74: Fsync, - 75: Fdatasync, - 76: Truncate, - 77: Ftruncate, - 78: Getdents, - 79: Getcwd, - 80: Chdir, - 81: Fchdir, - 82: Rename, - 83: Mkdir, - 84: Rmdir, - 85: Creat, - 86: Link, - 87: Unlink, - 88: Symlink, - 89: Readlink, - 90: Chmod, - 91: Fchmod, - 92: Chown, - 93: Fchown, - 94: Lchown, - 95: Umask, - 96: Gettimeofday, - 97: Getrlimit, - 98: Getrusage, - 99: Sysinfo, - 100: Times, - 101: Ptrace, - 102: Getuid, - 103: Syslog, - 104: Getgid, - 105: Setuid, - 106: Setgid, - 107: Geteuid, - 108: Getegid, - 109: Setpgid, - 110: Getppid, - 111: Getpgrp, - 112: Setsid, - 113: Setreuid, - 114: Setregid, - 115: Getgroups, - 116: Setgroups, - 117: Setresuid, - 118: Getresuid, - 119: Setresgid, - 120: Getresgid, - 121: Getpgid, - // 122: @Syscall(Setfsuid), TODO(b/112851702) - // 123: @Syscall(Setfsgid), TODO(b/112851702) - 124: Getsid, - 125: Capget, - 126: Capset, - 127: RtSigpending, - 128: RtSigtimedwait, - 129: RtSigqueueinfo, - 130: RtSigsuspend, - 131: Sigaltstack, - 132: Utime, - 133: Mknod, - // @Syscall(Uselib, note:Obsolete) - 134: syscalls.Error(syscall.ENOSYS), - // @Syscall(SetPersonality, returns:EINVAL, note:Unable to change personality) - 135: syscalls.ErrorWithEvent(syscall.EINVAL), - // @Syscall(Ustat, note:Needs filesystem support) - 136: syscalls.ErrorWithEvent(syscall.ENOSYS), - 137: Statfs, - 138: Fstatfs, - // 139: @Syscall(Sysfs), TODO(gvisor.dev/issue/165) - 140: Getpriority, - 141: Setpriority, - // @Syscall(SchedSetparam, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise) - 142: syscalls.CapError(linux.CAP_SYS_NICE), // requires cap_sys_nice - 143: SchedGetparam, - 144: SchedSetscheduler, - 145: SchedGetscheduler, - 146: SchedGetPriorityMax, - 147: SchedGetPriorityMin, - // @Syscall(SchedRrGetInterval, returns:EPERM) - 148: syscalls.ErrorWithEvent(syscall.EPERM), - 149: Mlock, - 150: Munlock, - 151: Mlockall, - 152: Munlockall, - // @Syscall(Vhangup, returns:EPERM) - 153: syscalls.CapError(linux.CAP_SYS_TTY_CONFIG), - // @Syscall(ModifyLdt, returns:EPERM) - 154: syscalls.Error(syscall.EPERM), - // @Syscall(PivotRoot, returns:EPERM) - 155: syscalls.Error(syscall.EPERM), - // @Syscall(Sysctl, returns:EPERM) - 156: syscalls.Error(syscall.EPERM), // syscall is "worthless" - 157: Prctl, - 158: ArchPrctl, - // @Syscall(Adjtimex, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_time; ENOSYS otherwise) - 159: syscalls.CapError(linux.CAP_SYS_TIME), // requires cap_sys_time - 160: Setrlimit, - 161: Chroot, - 162: Sync, - // @Syscall(Acct, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_pacct; ENOSYS otherwise) - 163: syscalls.CapError(linux.CAP_SYS_PACCT), // requires cap_sys_pacct - // @Syscall(Settimeofday, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_time; ENOSYS otherwise) - 164: syscalls.CapError(linux.CAP_SYS_TIME), // requires cap_sys_time - 165: Mount, - 166: Umount2, - // @Syscall(Swapon, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_admin; ENOSYS otherwise) - 167: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_admin - // @Syscall(Swapoff, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_admin; ENOSYS otherwise) - 168: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_admin - // @Syscall(Reboot, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_boot; ENOSYS otherwise) - 169: syscalls.CapError(linux.CAP_SYS_BOOT), // requires cap_sys_boot - 170: Sethostname, - 171: Setdomainname, - // @Syscall(Iopl, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_rawio; ENOSYS otherwise) - 172: syscalls.CapError(linux.CAP_SYS_RAWIO), // requires cap_sys_rawio - // @Syscall(Ioperm, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_rawio; ENOSYS otherwise) - 173: syscalls.CapError(linux.CAP_SYS_RAWIO), // requires cap_sys_rawio - // @Syscall(CreateModule, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_module; ENOSYS otherwise) - 174: syscalls.CapError(linux.CAP_SYS_MODULE), // CreateModule, requires cap_sys_module - // @Syscall(InitModule, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_module; ENOSYS otherwise) - 175: syscalls.CapError(linux.CAP_SYS_MODULE), // requires cap_sys_module - // @Syscall(DeleteModule, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_module; ENOSYS otherwise) - 176: syscalls.CapError(linux.CAP_SYS_MODULE), // requires cap_sys_module - // @Syscall(GetKernelSyms, note:Not supported in > 2.6) - 177: syscalls.Error(syscall.ENOSYS), - // @Syscall(QueryModule, note:Not supported in > 2.6) - 178: syscalls.Error(syscall.ENOSYS), - // @Syscall(Quotactl, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_admin; ENOSYS otherwise) - 179: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_admin (most operations) - // @Syscall(Nfsservctl, note:Does not exist > 3.1) - 180: syscalls.Error(syscall.ENOSYS), - // @Syscall(Getpmsg, note:Not implemented in Linux) - 181: syscalls.Error(syscall.ENOSYS), - // @Syscall(Putpmsg, note:Not implemented in Linux) - 182: syscalls.Error(syscall.ENOSYS), - // @Syscall(AfsSyscall, note:Not implemented in Linux) - 183: syscalls.Error(syscall.ENOSYS), - // @Syscall(Tuxcall, note:Not implemented in Linux) - 184: syscalls.Error(syscall.ENOSYS), - // @Syscall(Security, note:Not implemented in Linux) - 185: syscalls.Error(syscall.ENOSYS), - 186: Gettid, - 187: nil, // @Syscall(Readahead), TODO(b/29351341) - // @Syscall(Setxattr, returns:ENOTSUP, note:Requires filesystem support) - 188: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Lsetxattr, returns:ENOTSUP, note:Requires filesystem support) - 189: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Fsetxattr, returns:ENOTSUP, note:Requires filesystem support) - 190: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Getxattr, returns:ENOTSUP, note:Requires filesystem support) - 191: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Lgetxattr, returns:ENOTSUP, note:Requires filesystem support) - 192: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Fgetxattr, returns:ENOTSUP, note:Requires filesystem support) - 193: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Listxattr, returns:ENOTSUP, note:Requires filesystem support) - 194: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Llistxattr, returns:ENOTSUP, note:Requires filesystem support) - 195: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Flistxattr, returns:ENOTSUP, note:Requires filesystem support) - 196: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Removexattr, returns:ENOTSUP, note:Requires filesystem support) - 197: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Lremovexattr, returns:ENOTSUP, note:Requires filesystem support) - 198: syscalls.ErrorWithEvent(syscall.ENOTSUP), - // @Syscall(Fremovexattr, returns:ENOTSUP, note:Requires filesystem support) - 199: syscalls.ErrorWithEvent(syscall.ENOTSUP), - 200: Tkill, - 201: Time, - 202: Futex, - 203: SchedSetaffinity, - 204: SchedGetaffinity, - // @Syscall(SetThreadArea, note:Expected to return ENOSYS on 64-bit) - 205: syscalls.Error(syscall.ENOSYS), - 206: IoSetup, - 207: IoDestroy, - 208: IoGetevents, - 209: IoSubmit, - 210: IoCancel, - // @Syscall(GetThreadArea, note:Expected to return ENOSYS on 64-bit) - 211: syscalls.Error(syscall.ENOSYS), - // @Syscall(LookupDcookie, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_admin; ENOSYS otherwise) - 212: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_admin - 213: EpollCreate, - // @Syscall(EpollCtlOld, note:Deprecated) - 214: syscalls.ErrorWithEvent(syscall.ENOSYS), // deprecated (afaik, unused) - // @Syscall(EpollWaitOld, note:Deprecated) - 215: syscalls.ErrorWithEvent(syscall.ENOSYS), // deprecated (afaik, unused) - // @Syscall(RemapFilePages, note:Deprecated) - 216: syscalls.ErrorWithEvent(syscall.ENOSYS), // deprecated since 3.16 - 217: Getdents64, - 218: SetTidAddress, - 219: RestartSyscall, - // 220: @Syscall(Semtimedop), TODO(b/29354920) - 221: Fadvise64, - 222: TimerCreate, - 223: TimerSettime, - 224: TimerGettime, - 225: TimerGetoverrun, - 226: TimerDelete, - 227: ClockSettime, - 228: ClockGettime, - 229: ClockGetres, - 230: ClockNanosleep, - 231: ExitGroup, - 232: EpollWait, - 233: EpollCtl, - 234: Tgkill, - 235: Utimes, - // @Syscall(Vserver, note:Not implemented by Linux) - 236: syscalls.Error(syscall.ENOSYS), // Vserver, not implemented by Linux - 237: Mbind, - 238: SetMempolicy, - 239: GetMempolicy, - // 240: @Syscall(MqOpen), TODO(b/29354921) - // 241: @Syscall(MqUnlink), TODO(b/29354921) - // 242: @Syscall(MqTimedsend), TODO(b/29354921) - // 243: @Syscall(MqTimedreceive), TODO(b/29354921) - // 244: @Syscall(MqNotify), TODO(b/29354921) - // 245: @Syscall(MqGetsetattr), TODO(b/29354921) - 246: syscalls.CapError(linux.CAP_SYS_BOOT), // kexec_load, requires cap_sys_boot - 247: Waitid, - // @Syscall(AddKey, returns:EACCES, note:Not available to user) - 248: syscalls.Error(syscall.EACCES), - // @Syscall(RequestKey, returns:EACCES, note:Not available to user) - 249: syscalls.Error(syscall.EACCES), - // @Syscall(Keyctl, returns:EACCES, note:Not available to user) - 250: syscalls.Error(syscall.EACCES), - // @Syscall(IoprioSet, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_admin; ENOSYS otherwise) - 251: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_nice or cap_sys_admin (depending) - // @Syscall(IoprioGet, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_admin; ENOSYS otherwise) - 252: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_nice or cap_sys_admin (depending) - 253: InotifyInit, - 254: InotifyAddWatch, - 255: InotifyRmWatch, - // @Syscall(MigratePages, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise) - 256: syscalls.CapError(linux.CAP_SYS_NICE), - 257: Openat, - 258: Mkdirat, - 259: Mknodat, - 260: Fchownat, - 261: Futimesat, - 262: Fstatat, - 263: Unlinkat, - 264: Renameat, - 265: Linkat, - 266: Symlinkat, - 267: Readlinkat, - 268: Fchmodat, - 269: Faccessat, - 270: Pselect, - 271: Ppoll, - 272: Unshare, - // @Syscall(SetRobustList, note:Obsolete) - 273: syscalls.Error(syscall.ENOSYS), - // @Syscall(GetRobustList, note:Obsolete) - 274: syscalls.Error(syscall.ENOSYS), - 275: Splice, - // 276: @Syscall(Tee), TODO(b/29354098) - 277: SyncFileRange, - // 278: @Syscall(Vmsplice), TODO(b/29354098) - // @Syscall(MovePages, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_nice; ENOSYS otherwise) - 279: syscalls.CapError(linux.CAP_SYS_NICE), // requires cap_sys_nice (mostly) - 280: Utimensat, - 281: EpollPwait, - // 282: @Syscall(Signalfd), TODO(b/19846426) - 283: TimerfdCreate, - 284: Eventfd, - 285: Fallocate, - 286: TimerfdSettime, - 287: TimerfdGettime, - 288: Accept4, - // 289: @Syscall(Signalfd4), TODO(b/19846426) - 290: Eventfd2, - 291: EpollCreate1, - 292: Dup3, - 293: Pipe2, - 294: InotifyInit1, - 295: Preadv, - 296: Pwritev, - 297: RtTgsigqueueinfo, - // @Syscall(PerfEventOpen, returns:ENODEV, note:No support for perf counters) - 298: syscalls.ErrorWithEvent(syscall.ENODEV), - 299: RecvMMsg, - // @Syscall(FanotifyInit, note:Needs CONFIG_FANOTIFY) - 300: syscalls.ErrorWithEvent(syscall.ENOSYS), - // @Syscall(FanotifyMark, note:Needs CONFIG_FANOTIFY) - 301: syscalls.ErrorWithEvent(syscall.ENOSYS), - 302: Prlimit64, - // @Syscall(NameToHandleAt, returns:EOPNOTSUPP, note:Needs filesystem support) - 303: syscalls.ErrorWithEvent(syscall.EOPNOTSUPP), - // @Syscall(OpenByHandleAt, returns:EOPNOTSUPP, note:Needs filesystem support) - 304: syscalls.ErrorWithEvent(syscall.EOPNOTSUPP), - // @Syscall(ClockAdjtime, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_module; ENOSYS otherwise) - 305: syscalls.CapError(linux.CAP_SYS_TIME), // requires cap_sys_time - 306: Syncfs, - 307: SendMMsg, - // 308: @Syscall(Setns), TODO(b/29354995) - 309: Getcpu, - // 310: @Syscall(ProcessVmReadv), TODO(gvisor.dev/issue/158) may require cap_sys_ptrace - // 311: @Syscall(ProcessVmWritev), TODO(gvisor.dev/issue/158) may require cap_sys_ptrace - // @Syscall(Kcmp, returns:EPERM or ENOSYS, note:Requires cap_sys_ptrace) - 312: syscalls.CapError(linux.CAP_SYS_PTRACE), - // @Syscall(FinitModule, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_module; ENOSYS otherwise) - 313: syscalls.CapError(linux.CAP_SYS_MODULE), - // 314: @Syscall(SchedSetattr), TODO(b/118902272), we have no scheduler - // 315: @Syscall(SchedGetattr), TODO(b/118902272), we have no scheduler - // 316: @Syscall(Renameat2), TODO(b/118902772) - 317: Seccomp, - 318: GetRandom, - 319: MemfdCreate, - // @Syscall(KexecFileLoad, EPERM or ENOSYS, note:Infeasible to support. Returns EPERM if the process does not have cap_sys_boot; ENOSYS otherwise) - 320: syscalls.CapError(linux.CAP_SYS_BOOT), - // @Syscall(Bpf, returns:EPERM or ENOSYS, note:Returns EPERM if the process does not have cap_sys_boot; ENOSYS otherwise) - 321: syscalls.CapError(linux.CAP_SYS_ADMIN), // requires cap_sys_admin for all commands - // 322: @Syscall(Execveat), TODO(b/118901836) - // 323: @Syscall(Userfaultfd), TODO(b/118906345) - // 324: @Syscall(Membarrier), TODO(b/118904897) - 325: Mlock2, + Table: map[uintptr]kernel.Syscall{ + 0: syscalls.Supported("read", Read), + 1: syscalls.Supported("write", Write), + 2: syscalls.Supported("open", Open), + 3: syscalls.Supported("close", Close), + 4: syscalls.Undocumented("stat", Stat), + 5: syscalls.Undocumented("fstat", Fstat), + 6: syscalls.Undocumented("lstat", Lstat), + 7: syscalls.Undocumented("poll", Poll), + 8: syscalls.Undocumented("lseek", Lseek), + 9: syscalls.Undocumented("mmap", Mmap), + 10: syscalls.Undocumented("mprotect", Mprotect), + 11: syscalls.Undocumented("munmap", Munmap), + 12: syscalls.Undocumented("brk", Brk), + 13: syscalls.Undocumented("rt_sigaction", RtSigaction), + 14: syscalls.Undocumented("rt_sigprocmask", RtSigprocmask), + 15: syscalls.Undocumented("rt_sigreturn", RtSigreturn), + 16: syscalls.Undocumented("ioctl", Ioctl), + 17: syscalls.Undocumented("pread64", Pread64), + 18: syscalls.Undocumented("pwrite64", Pwrite64), + 19: syscalls.Undocumented("readv", Readv), + 20: syscalls.Undocumented("writev", Writev), + 21: syscalls.Undocumented("access", Access), + 22: syscalls.Undocumented("pipe", Pipe), + 23: syscalls.Undocumented("select", Select), + 24: syscalls.Undocumented("sched_yield", SchedYield), + 25: syscalls.Undocumented("mremap", Mremap), + 26: syscalls.Undocumented("msync", Msync), + 27: syscalls.Undocumented("mincore", Mincore), + 28: syscalls.Undocumented("madvise", Madvise), + 29: syscalls.Undocumented("shmget", Shmget), + 30: syscalls.Undocumented("shmat", Shmat), + 31: syscalls.Undocumented("shmctl", Shmctl), + 32: syscalls.Undocumented("dup", Dup), + 33: syscalls.Undocumented("dup2", Dup2), + 34: syscalls.Undocumented("pause", Pause), + 35: syscalls.Undocumented("nanosleep", Nanosleep), + 36: syscalls.Undocumented("getitimer", Getitimer), + 37: syscalls.Undocumented("alarm", Alarm), + 38: syscalls.Undocumented("setitimer", Setitimer), + 39: syscalls.Undocumented("getpid", Getpid), + 40: syscalls.Undocumented("sendfile", Sendfile), + 41: syscalls.Undocumented("socket", Socket), + 42: syscalls.Undocumented("connect", Connect), + 43: syscalls.Undocumented("accept", Accept), + 44: syscalls.Undocumented("sendto", SendTo), + 45: syscalls.Undocumented("recvfrom", RecvFrom), + 46: syscalls.Undocumented("sendmsg", SendMsg), + 47: syscalls.Undocumented("recvmsg", RecvMsg), + 48: syscalls.Undocumented("shutdown", Shutdown), + 49: syscalls.Undocumented("bind", Bind), + 50: syscalls.Undocumented("listen", Listen), + 51: syscalls.Undocumented("getsockname", GetSockName), + 52: syscalls.Undocumented("getpeername", GetPeerName), + 53: syscalls.Undocumented("socketpair", SocketPair), + 54: syscalls.Undocumented("setsockopt", SetSockOpt), + 55: syscalls.Undocumented("getsockopt", GetSockOpt), + 56: syscalls.Undocumented("clone", Clone), + 57: syscalls.Undocumented("fork", Fork), + 58: syscalls.Undocumented("vfork", Vfork), + 59: syscalls.Undocumented("execve", Execve), + 60: syscalls.Undocumented("exit", Exit), + 61: syscalls.Undocumented("wait4", Wait4), + 62: syscalls.Undocumented("kill", Kill), + 63: syscalls.Undocumented("uname", Uname), + 64: syscalls.Undocumented("semget", Semget), + 65: syscalls.Undocumented("semop", Semop), + 66: syscalls.Undocumented("semctl", Semctl), + 67: syscalls.Undocumented("shmdt", Shmdt), + 68: syscalls.ErrorWithEvent("msgget", syscall.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 69: syscalls.ErrorWithEvent("msgsnd", syscall.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 70: syscalls.ErrorWithEvent("msgrcv", syscall.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 71: syscalls.ErrorWithEvent("msgctl", syscall.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921) + 72: syscalls.Undocumented("fcntl", Fcntl), + 73: syscalls.Undocumented("flock", Flock), + 74: syscalls.Undocumented("fsync", Fsync), + 75: syscalls.Undocumented("fdatasync", Fdatasync), + 76: syscalls.Undocumented("truncate", Truncate), + 77: syscalls.Undocumented("ftruncate", Ftruncate), + 78: syscalls.Undocumented("getdents", Getdents), + 79: syscalls.Undocumented("getcwd", Getcwd), + 80: syscalls.Undocumented("chdir", Chdir), + 81: syscalls.Undocumented("fchdir", Fchdir), + 82: syscalls.Undocumented("rename", Rename), + 83: syscalls.Undocumented("mkdir", Mkdir), + 84: syscalls.Undocumented("rmdir", Rmdir), + 85: syscalls.Undocumented("creat", Creat), + 86: syscalls.Undocumented("link", Link), + 87: syscalls.Undocumented("link", Unlink), + 88: syscalls.Undocumented("symlink", Symlink), + 89: syscalls.Undocumented("readlink", Readlink), + 90: syscalls.Undocumented("chmod", Chmod), + 91: syscalls.Undocumented("fchmod", Fchmod), + 92: syscalls.Undocumented("chown", Chown), + 93: syscalls.Undocumented("fchown", Fchown), + 94: syscalls.Undocumented("lchown", Lchown), + 95: syscalls.Undocumented("umask", Umask), + 96: syscalls.Undocumented("gettimeofday", Gettimeofday), + 97: syscalls.Undocumented("getrlimit", Getrlimit), + 98: syscalls.Undocumented("getrusage", Getrusage), + 99: syscalls.Undocumented("sysinfo", Sysinfo), + 100: syscalls.Undocumented("times", Times), + 101: syscalls.Undocumented("ptrace", Ptrace), + 102: syscalls.Undocumented("getuid", Getuid), + 103: syscalls.Undocumented("syslog", Syslog), + 104: syscalls.Undocumented("getgid", Getgid), + 105: syscalls.Undocumented("setuid", Setuid), + 106: syscalls.Undocumented("setgid", Setgid), + 107: syscalls.Undocumented("geteuid", Geteuid), + 108: syscalls.Undocumented("getegid", Getegid), + 109: syscalls.Undocumented("setpgid", Setpgid), + 110: syscalls.Undocumented("getppid", Getppid), + 111: syscalls.Undocumented("getpgrp", Getpgrp), + 112: syscalls.Undocumented("setsid", Setsid), + 113: syscalls.Undocumented("setreuid", Setreuid), + 114: syscalls.Undocumented("setregid", Setregid), + 115: syscalls.Undocumented("getgroups", Getgroups), + 116: syscalls.Undocumented("setgroups", Setgroups), + 117: syscalls.Undocumented("setresuid", Setresuid), + 118: syscalls.Undocumented("getresuid", Getresuid), + 119: syscalls.Undocumented("setresgid", Setresgid), + 120: syscalls.Undocumented("setresgid", Getresgid), + 121: syscalls.Undocumented("getpgid", Getpgid), + 122: syscalls.ErrorWithEvent("setfsuid", syscall.ENOSYS, "", []string{"gvisor.dev/issue/260"}), // TODO(b/112851702) + 123: syscalls.ErrorWithEvent("setfsgid", syscall.ENOSYS, "", []string{"gvisor.dev/issue/260"}), // TODO(b/112851702) + 124: syscalls.Undocumented("getsid", Getsid), + 125: syscalls.Undocumented("capget", Capget), + 126: syscalls.Undocumented("capset", Capset), + 127: syscalls.Undocumented("rt_sigpending", RtSigpending), + 128: syscalls.Undocumented("rt_sigtimedwait", RtSigtimedwait), + 129: syscalls.Undocumented("rt_sigqueueinfo", RtSigqueueinfo), + 130: syscalls.Undocumented("rt_sigsuspend", RtSigsuspend), + 131: syscalls.Undocumented("sigaltstack", Sigaltstack), + 132: syscalls.Undocumented("utime", Utime), + 133: syscalls.Undocumented("mknod", Mknod), + 134: syscalls.Error("uselib", syscall.ENOSYS, "Obsolete", nil), + 135: syscalls.ErrorWithEvent("personality", syscall.EINVAL, "Unable to change personality.", nil), + 136: syscalls.ErrorWithEvent("ustat", syscall.ENOSYS, "Needs filesystem support.", nil), + 137: syscalls.Undocumented("statfs", Statfs), + 138: syscalls.Undocumented("fstatfs", Fstatfs), + 139: syscalls.ErrorWithEvent("sysfs", syscall.ENOSYS, "", []string{"gvisor.dev/issue/165"}), + 140: syscalls.Undocumented("getpriority", Getpriority), + 141: syscalls.Undocumented("setpriority", Setpriority), + 142: syscalls.CapError("sched_setparam", linux.CAP_SYS_NICE, "", nil), + 143: syscalls.Undocumented("sched_getparam", SchedGetparam), + 144: syscalls.Undocumented("sched_setscheduler", SchedSetscheduler), + 145: syscalls.Undocumented("sched_getscheduler", SchedGetscheduler), + 146: syscalls.Undocumented("sched_get_priority_max", SchedGetPriorityMax), + 147: syscalls.Undocumented("sched_get_priority_min", SchedGetPriorityMin), + 148: syscalls.ErrorWithEvent("sched_rr_get_interval", syscall.EPERM, "", nil), + 149: syscalls.Undocumented("mlock", Mlock), + 150: syscalls.Undocumented("munlock", Munlock), + 151: syscalls.Undocumented("mlockall", Mlockall), + 152: syscalls.Undocumented("munlockall", Munlockall), + 153: syscalls.CapError("vhangup", linux.CAP_SYS_TTY_CONFIG, "", nil), + 154: syscalls.Error("modify_ldt", syscall.EPERM, "", nil), + 155: syscalls.Error("pivot_root", syscall.EPERM, "", nil), + 156: syscalls.Error("sysctl", syscall.EPERM, `syscall is "worthless"`, nil), + 157: syscalls.Undocumented("prctl", Prctl), + 158: syscalls.Undocumented("arch_prctl", ArchPrctl), + 159: syscalls.CapError("adjtimex", linux.CAP_SYS_TIME, "", nil), + 160: syscalls.Undocumented("setrlimit", Setrlimit), + 161: syscalls.Undocumented("chroot", Chroot), + 162: syscalls.Undocumented("sync", Sync), + 163: syscalls.CapError("acct", linux.CAP_SYS_PACCT, "", nil), + 164: syscalls.CapError("settimeofday", linux.CAP_SYS_TIME, "", nil), + 165: syscalls.Undocumented("mount", Mount), + 166: syscalls.Undocumented("umount2", Umount2), + 167: syscalls.CapError("swapon", linux.CAP_SYS_ADMIN, "", nil), + 168: syscalls.CapError("swapoff", linux.CAP_SYS_ADMIN, "", nil), + 169: syscalls.CapError("reboot", linux.CAP_SYS_BOOT, "", nil), + 170: syscalls.Undocumented("sethostname", Sethostname), + 171: syscalls.Undocumented("setdomainname", Setdomainname), + 172: syscalls.CapError("iopl", linux.CAP_SYS_RAWIO, "", nil), + 173: syscalls.CapError("ioperm", linux.CAP_SYS_RAWIO, "", nil), + 174: syscalls.CapError("create_module", linux.CAP_SYS_MODULE, "", nil), + 175: syscalls.CapError("init_module", linux.CAP_SYS_MODULE, "", nil), + 176: syscalls.CapError("delete_module", linux.CAP_SYS_MODULE, "", nil), + 177: syscalls.Error("get_kernel_syms", syscall.ENOSYS, "Not supported in > 2.6", nil), + 178: syscalls.Error("query_module", syscall.ENOSYS, "Not supported in > 2.6", nil), + 179: syscalls.CapError("quotactl", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_admin for most operations + 180: syscalls.Error("nfsservctl", syscall.ENOSYS, "Does not exist > 3.1", nil), + 181: syscalls.Error("getpmsg", syscall.ENOSYS, "Not implemented in Linux", nil), + 182: syscalls.Error("putpmsg", syscall.ENOSYS, "Not implemented in Linux", nil), + 183: syscalls.Error("afs_syscall", syscall.ENOSYS, "Not implemented in Linux", nil), + 184: syscalls.Error("tuxcall", syscall.ENOSYS, "Not implemented in Linux", nil), + 185: syscalls.Error("security", syscall.ENOSYS, "Not implemented in Linux", nil), + 186: syscalls.Undocumented("gettid", Gettid), + 187: syscalls.ErrorWithEvent("readahead", syscall.ENOSYS, "", []string{"gvisor.dev/issue/261"}), // TODO(b/29351341) + 188: syscalls.ErrorWithEvent("setxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 189: syscalls.ErrorWithEvent("lsetxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 190: syscalls.ErrorWithEvent("fsetxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 191: syscalls.ErrorWithEvent("getxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 192: syscalls.ErrorWithEvent("lgetxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 193: syscalls.ErrorWithEvent("fgetxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 194: syscalls.ErrorWithEvent("listxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 195: syscalls.ErrorWithEvent("llistxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 196: syscalls.ErrorWithEvent("flistxattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 197: syscalls.ErrorWithEvent("removexattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 198: syscalls.ErrorWithEvent("lremovexattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 199: syscalls.ErrorWithEvent("fremovexattr", syscall.ENOTSUP, "Requires filesystem support", nil), + 200: syscalls.Undocumented("tkill", Tkill), + 201: syscalls.Undocumented("time", Time), + 202: syscalls.Undocumented("futex", Futex), + 203: syscalls.Undocumented("sched_setaffinity", SchedSetaffinity), + 204: syscalls.Undocumented("sched_getaffinity", SchedGetaffinity), + 205: syscalls.Error("set_thread_area", syscall.ENOSYS, "Expected to return ENOSYS on 64-bit", nil), + 206: syscalls.Undocumented("io_setup", IoSetup), + 207: syscalls.Undocumented("io_destroy", IoDestroy), + 208: syscalls.Undocumented("io_getevents", IoGetevents), + 209: syscalls.Undocumented("io_submit", IoSubmit), + 210: syscalls.Undocumented("io_cancel", IoCancel), + 211: syscalls.Error("get_thread_area", syscall.ENOSYS, "Expected to return ENOSYS on 64-bit", nil), + 212: syscalls.CapError("lookup_dcookie", linux.CAP_SYS_ADMIN, "", nil), + 213: syscalls.Undocumented("epoll_create", EpollCreate), + 214: syscalls.ErrorWithEvent("epoll_ctl_old", syscall.ENOSYS, "Deprecated", nil), + 215: syscalls.ErrorWithEvent("epoll_wait_old", syscall.ENOSYS, "Deprecated", nil), + 216: syscalls.ErrorWithEvent("remap_file_pages", syscall.ENOSYS, "Deprecated since 3.16", nil), + 217: syscalls.Undocumented("getdents64", Getdents64), + 218: syscalls.Undocumented("set_tid_address", SetTidAddress), + 219: syscalls.Undocumented("restart_syscall", RestartSyscall), + 220: syscalls.ErrorWithEvent("semtimedop", syscall.ENOSYS, "", []string{"gvisor.dev/issue/137"}), // TODO(b/29354920) + 221: syscalls.Undocumented("fadvise64", Fadvise64), + 222: syscalls.Undocumented("timer_create", TimerCreate), + 223: syscalls.Undocumented("timer_settime", TimerSettime), + 224: syscalls.Undocumented("timer_gettime", TimerGettime), + 225: syscalls.Undocumented("timer_getoverrun", TimerGetoverrun), + 226: syscalls.Undocumented("timer_delete", TimerDelete), + 227: syscalls.Undocumented("clock_settime", ClockSettime), + 228: syscalls.Undocumented("clock_gettime", ClockGettime), + 229: syscalls.Undocumented("clock_getres", ClockGetres), + 230: syscalls.Undocumented("clock_nanosleep", ClockNanosleep), + 231: syscalls.Undocumented("exit_group", ExitGroup), + 232: syscalls.Undocumented("epoll_wait", EpollWait), + 233: syscalls.Undocumented("epoll_ctl", EpollCtl), + 234: syscalls.Undocumented("tgkill", Tgkill), + 235: syscalls.Undocumented("utimes", Utimes), + 236: syscalls.Error("vserver", syscall.ENOSYS, "Not implemented by Linux", nil), + 237: syscalls.PartiallySupported("mbind", Mbind, "Stub implementation. Only a single NUMA node is advertised, and mempolicy is ignored accordingly, but mbind() will succeed and has effects reflected by get_mempolicy.", []string{"gvisor.dev/issue/262"}), + 238: syscalls.Undocumented("set_mempolicy", SetMempolicy), + 239: syscalls.Undocumented("get_mempolicy", GetMempolicy), + 240: syscalls.ErrorWithEvent("mq_open", syscall.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) + 241: syscalls.ErrorWithEvent("mq_unlink", syscall.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) + 242: syscalls.ErrorWithEvent("mq_timedsend", syscall.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) + 243: syscalls.ErrorWithEvent("mq_timedreceive", syscall.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) + 244: syscalls.ErrorWithEvent("mq_notify", syscall.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) + 245: syscalls.ErrorWithEvent("mq_getsetattr", syscall.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921) + 246: syscalls.CapError("kexec_load", linux.CAP_SYS_BOOT, "", nil), + 247: syscalls.Undocumented("waitid", Waitid), + 248: syscalls.Error("add_key", syscall.EACCES, "Not available to user", nil), + 249: syscalls.Error("request_key", syscall.EACCES, "Not available to user", nil), + 250: syscalls.Error("keyctl", syscall.EACCES, "Not available to user", nil), + 251: syscalls.CapError("ioprio_set", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending) + 252: syscalls.CapError("ioprio_get", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending) + 253: syscalls.Undocumented("inotify_init", InotifyInit), + 254: syscalls.Undocumented("inotify_add_watch", InotifyAddWatch), + 255: syscalls.Undocumented("inotify_rm_watch", InotifyRmWatch), + 256: syscalls.CapError("migrate_pages", linux.CAP_SYS_NICE, "", nil), + 257: syscalls.Undocumented("openat", Openat), + 258: syscalls.Undocumented("mkdirat", Mkdirat), + 259: syscalls.Undocumented("mknodat", Mknodat), + 260: syscalls.Undocumented("fchownat", Fchownat), + 261: syscalls.Undocumented("futimesat", Futimesat), + 262: syscalls.Undocumented("fstatat", Fstatat), + 263: syscalls.Undocumented("unlinkat", Unlinkat), + 264: syscalls.Undocumented("renameat", Renameat), + 265: syscalls.Undocumented("linkat", Linkat), + 266: syscalls.Undocumented("symlinkat", Symlinkat), + 267: syscalls.Undocumented("readlinkat", Readlinkat), + 268: syscalls.Undocumented("fchmodat", Fchmodat), + 269: syscalls.Undocumented("faccessat", Faccessat), + 270: syscalls.Undocumented("pselect", Pselect), + 271: syscalls.Undocumented("ppoll", Ppoll), + 272: syscalls.Undocumented("unshare", Unshare), + 273: syscalls.Error("set_robust_list", syscall.ENOSYS, "Obsolete", nil), + 274: syscalls.Error("get_robust_list", syscall.ENOSYS, "Obsolete", nil), + 275: syscalls.PartiallySupported("splice", Splice, "Stub implementation", []string{"gvisor.dev/issue/138"}), // TODO(b/29354098) + 276: syscalls.ErrorWithEvent("tee", syscall.ENOSYS, "", []string{"gvisor.dev/issue/138"}), // TODO(b/29354098) + 277: syscalls.Undocumented("sync_file_range", SyncFileRange), + 278: syscalls.ErrorWithEvent("vmsplice", syscall.ENOSYS, "", []string{"gvisor.dev/issue/138"}), // TODO(b/29354098) + 279: syscalls.CapError("move_pages", linux.CAP_SYS_NICE, "", nil), // requires cap_sys_nice (mostly) + 280: syscalls.Undocumented("utimensat", Utimensat), + 281: syscalls.Undocumented("epoll_pwait", EpollPwait), + 282: syscalls.ErrorWithEvent("signalfd", syscall.ENOSYS, "", []string{"gvisor.dev/issue/139"}), // TODO(b/19846426) + 283: syscalls.Undocumented("timerfd_create", TimerfdCreate), + 284: syscalls.Undocumented("eventfd", Eventfd), + 285: syscalls.Undocumented("fallocate", Fallocate), + 286: syscalls.Undocumented("timerfd_settime", TimerfdSettime), + 287: syscalls.Undocumented("timerfd_gettime", TimerfdGettime), + 288: syscalls.Undocumented("accept4", Accept4), + 289: syscalls.ErrorWithEvent("signalfd4", syscall.ENOSYS, "", []string{"gvisor.dev/issue/139"}), // TODO(b/19846426) + 290: syscalls.Undocumented("eventfd2", Eventfd2), + 291: syscalls.Undocumented("epoll_create1", EpollCreate1), + 292: syscalls.Undocumented("dup3", Dup3), + 293: syscalls.Undocumented("pipe2", Pipe2), + 294: syscalls.Undocumented("inotify_init1", InotifyInit1), + 295: syscalls.Undocumented("preadv", Preadv), + 296: syscalls.Undocumented("pwritev", Pwritev), + 297: syscalls.Undocumented("rt_tgsigqueueinfo", RtTgsigqueueinfo), + 298: syscalls.ErrorWithEvent("perf_event_open", syscall.ENODEV, "No support for perf counters", nil), + 299: syscalls.Undocumented("recvmmsg", RecvMMsg), + 300: syscalls.ErrorWithEvent("fanotify_init", syscall.ENOSYS, "Needs CONFIG_FANOTIFY", nil), + 301: syscalls.ErrorWithEvent("fanotify_mark", syscall.ENOSYS, "Needs CONFIG_FANOTIFY", nil), + 302: syscalls.Undocumented("prlimit64", Prlimit64), + 303: syscalls.ErrorWithEvent("name_to_handle_at", syscall.EOPNOTSUPP, "Needs filesystem support", nil), + 304: syscalls.ErrorWithEvent("open_by_handle_at", syscall.EOPNOTSUPP, "Needs filesystem support", nil), + 305: syscalls.CapError("clock_adjtime", linux.CAP_SYS_TIME, "", nil), + 306: syscalls.Undocumented("syncfs", Syncfs), + 307: syscalls.Undocumented("sendmmsg", SendMMsg), + 308: syscalls.ErrorWithEvent("setns", syscall.EOPNOTSUPP, "Needs filesystem support", []string{"gvisor.dev/issue/140"}), // TODO(b/29354995) + 309: syscalls.Undocumented("getcpu", Getcpu), + 310: syscalls.ErrorWithEvent("process_vm_readv", syscall.ENOSYS, "", []string{"gvisor.dev/issue/158"}), + 311: syscalls.ErrorWithEvent("process_vm_writev", syscall.ENOSYS, "", []string{"gvisor.dev/issue/158"}), + 312: syscalls.CapError("kcmp", linux.CAP_SYS_PTRACE, "", nil), + 313: syscalls.CapError("finit_module", linux.CAP_SYS_MODULE, "", nil), + 314: syscalls.ErrorWithEvent("sched_setattr", syscall.ENOSYS, "gVisor does not implement a scheduler.", []string{"gvisor.dev/issue/264"}), // TODO(b/118902272) + 315: syscalls.ErrorWithEvent("sched_getattr", syscall.ENOSYS, "gVisor does not implement a scheduler.", []string{"gvisor.dev/issue/264"}), // TODO(b/118902272) + 316: syscalls.ErrorWithEvent("renameat2", syscall.ENOSYS, "", []string{"gvisor.dev/issue/263"}), // TODO(b/118902772) + 317: syscalls.Undocumented("seccomp", Seccomp), + 318: syscalls.Undocumented("getrandom", GetRandom), + 319: syscalls.Undocumented("memfd_create", MemfdCreate), + 320: syscalls.CapError("kexec_file_load", linux.CAP_SYS_BOOT, "", nil), + 321: syscalls.CapError("bpf", linux.CAP_SYS_ADMIN, "", nil), + 322: syscalls.ErrorWithEvent("execveat", syscall.ENOSYS, "", []string{"gvisor.dev/issue/265"}), // TODO(b/118901836) + 323: syscalls.ErrorWithEvent("userfaultfd", syscall.ENOSYS, "", []string{"gvisor.dev/issue/266"}), // TODO(b/118906345) + 324: syscalls.ErrorWithEvent("membarrier", syscall.ENOSYS, "", []string{"gvisor.dev/issue/267"}), // TODO(b/118904897) + 325: syscalls.Undocumented("mlock2", Mlock2), + // Syscalls after 325 are "backports" from versions of Linux after 4.4. - // 326: @Syscall(CopyFileRange), - 327: Preadv2, - 328: Pwritev2, + 326: syscalls.ErrorWithEvent("copy_file_range", syscall.ENOSYS, "", nil), + 327: syscalls.Undocumented("preadv2", Preadv2), + 328: syscalls.Undocumented("pwritev2", Pwritev2), }, Emulate: map[usermem.Addr]uintptr{ diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go index 5d10b3824..48c114232 100644 --- a/pkg/sentry/syscalls/syscalls.go +++ b/pkg/sentry/syscalls/syscalls.go @@ -25,37 +25,97 @@ package syscalls import ( + "fmt" + "syscall" + "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/syserror" ) +// Supported returns a syscall that is fully supported. +func Supported(name string, fn kernel.SyscallFn) kernel.Syscall { + return kernel.Syscall{ + Name: name, + Fn: fn, + SupportLevel: kernel.SupportFull, + Note: "Full Support", + } +} + +// Undocumented returns a syscall that is undocumented. +func Undocumented(name string, fn kernel.SyscallFn) kernel.Syscall { + return kernel.Syscall{ + Name: name, + Fn: fn, + SupportLevel: kernel.SupportUndocumented, + } +} + +// PartiallySupported returns a syscall that has a partial implementation. +func PartiallySupported(name string, fn kernel.SyscallFn, note string, urls []string) kernel.Syscall { + return kernel.Syscall{ + Name: name, + Fn: fn, + SupportLevel: kernel.SupportPartial, + Note: note, + URLs: urls, + } +} + // Error returns a syscall handler that will always give the passed error. -func Error(err error) kernel.SyscallFn { - return func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { - return 0, nil, err +func Error(name string, err syscall.Errno, note string, urls []string) kernel.Syscall { + if note != "" { + note = note + "; " + } + return kernel.Syscall{ + Name: name, + Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + return 0, nil, err + }, + SupportLevel: kernel.SupportUnimplemented, + Note: fmt.Sprintf("%sReturns %q", note, err.Error()), + URLs: urls, } } // ErrorWithEvent gives a syscall function that sends an unimplemented // syscall event via the event channel and returns the passed error. -func ErrorWithEvent(err error) kernel.SyscallFn { - return func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { - t.Kernel().EmitUnimplementedEvent(t) - return 0, nil, err +func ErrorWithEvent(name string, err syscall.Errno, note string, urls []string) kernel.Syscall { + if note != "" { + note = note + "; " + } + return kernel.Syscall{ + Name: name, + Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + t.Kernel().EmitUnimplementedEvent(t) + return 0, nil, err + }, + SupportLevel: kernel.SupportUnimplemented, + Note: fmt.Sprintf("%sReturns %q", note, err.Error()), + URLs: urls, } } // CapError gives a syscall function that checks for capability c. If the task // has the capability, it returns ENOSYS, otherwise EPERM. To unprivileged // tasks, it will seem like there is an implementation. -func CapError(c linux.Capability) kernel.SyscallFn { - return func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { - if !t.HasCapability(c) { - return 0, nil, syserror.EPERM - } - t.Kernel().EmitUnimplementedEvent(t) - return 0, nil, syserror.ENOSYS +func CapError(name string, c linux.Capability, note string, urls []string) kernel.Syscall { + if note != "" { + note = note + "; " + } + return kernel.Syscall{ + Name: name, + Fn: func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + if !t.HasCapability(c) { + return 0, nil, syserror.EPERM + } + t.Kernel().EmitUnimplementedEvent(t) + return 0, nil, syserror.ENOSYS + }, + SupportLevel: kernel.SupportUnimplemented, + Note: fmt.Sprintf("%sReturns %q if the process does not have %s; %q otherwise", note, syserror.EPERM, c.String(), syserror.ENOSYS), + URLs: urls, } } diff --git a/runsc/cmd/BUILD b/runsc/cmd/BUILD index 173b7671e..df6af0ced 100644 --- a/runsc/cmd/BUILD +++ b/runsc/cmd/BUILD @@ -18,6 +18,7 @@ go_library( "events.go", "exec.go", "gofer.go", + "help.go", "kill.go", "list.go", "path.go", @@ -29,6 +30,7 @@ go_library( "spec.go", "start.go", "state.go", + "syscalls.go", "wait.go", ], importpath = "gvisor.googlesource.com/gvisor/runsc/cmd", @@ -39,6 +41,7 @@ go_library( "//pkg/log", "//pkg/p9", "//pkg/sentry/control", + "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/unet", "//pkg/urpc", diff --git a/runsc/cmd/help.go b/runsc/cmd/help.go new file mode 100644 index 000000000..ff4f901cb --- /dev/null +++ b/runsc/cmd/help.go @@ -0,0 +1,126 @@ +// Copyright 2018 Google LLC +// +// 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 cmd + +import ( + "context" + "fmt" + + "flag" + "github.com/google/subcommands" +) + +// NewHelp returns a help command for the given commander. +func NewHelp(cdr *subcommands.Commander) *Help { + return &Help{ + cdr: cdr, + } +} + +// Help implements subcommands.Command for the "help" command. The 'help' +// command prints help for commands registered to a Commander but also allows for +// registering additional help commands that print other documentation. +type Help struct { + cdr *subcommands.Commander + commands []subcommands.Command + help bool +} + +// Name implements subcommands.Command.Name. +func (*Help) Name() string { + return "help" +} + +// Synopsis implements subcommands.Command.Synopsis. +func (*Help) Synopsis() string { + return "Print help documentation." +} + +// Usage implements subcommands.Command.Usage. +func (*Help) Usage() string { + return `help []: + With an argument, prints detailed information on the use of + the specified topic or subcommand. With no argument, print a list of + all commands and a brief description of each. +` +} + +// SetFlags implements subcommands.Command.SetFlags. +func (h *Help) SetFlags(f *flag.FlagSet) {} + +// Execute implements subcommands.Command.Execute. +func (h *Help) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + switch f.NArg() { + case 0: + fmt.Fprintf(h.cdr.Output, "Usage: %s \n\n", h.cdr.Name()) + fmt.Fprintf(h.cdr.Output, `runsc is a command line client for running applications packaged in the Open +Container Initiative (OCI) format. Applications run by runsc are run in an +isolated gVisor sandbox that emulates a Linux environment. + +gVisor is a user-space kernel, written in Go, that implements a substantial +portion of the Linux system call interface. It provides an additional layer +of isolation between running applications and the host operating system. + +Functionality is provided by subcommands. For additonal help on individual +subcommands use "%s %s ". + +`, h.cdr.Name(), h.Name()) + h.cdr.VisitGroups(func(g *subcommands.CommandGroup) { + h.cdr.ExplainGroup(h.cdr.Output, g) + }) + + fmt.Fprintf(h.cdr.Output, "Additional help topics (Use \"%s %s \" to see help on the topic):\n", h.cdr.Name(), h.Name()) + for _, cmd := range h.commands { + fmt.Fprintf(h.cdr.Output, "\t%-15s %s\n", cmd.Name(), cmd.Synopsis()) + } + fmt.Fprintf(h.cdr.Output, "\nUse \"%s flags\" for a list of top-level flags\n", h.cdr.Name()) + return subcommands.ExitSuccess + default: + // Look for commands registered to the commander and print help explanation if found. + found := false + h.cdr.VisitCommands(func(g *subcommands.CommandGroup, cmd subcommands.Command) { + if f.Arg(0) == cmd.Name() { + h.cdr.ExplainCommand(h.cdr.Output, cmd) + found = true + } + }) + if found { + return subcommands.ExitSuccess + } + + // Next check commands registered to the help command. + for _, cmd := range h.commands { + if f.Arg(0) == cmd.Name() { + fs := flag.NewFlagSet(f.Arg(0), flag.ContinueOnError) + fs.Usage = func() { h.cdr.ExplainCommand(h.cdr.Error, cmd) } + cmd.SetFlags(fs) + if fs.Parse(f.Args()[1:]) != nil { + return subcommands.ExitUsageError + } + return cmd.Execute(ctx, f, args...) + } + } + + fmt.Fprintf(h.cdr.Error, "Subcommand %s not understood\n", f.Arg(0)) + } + + f.Usage() + return subcommands.ExitUsageError +} + +// Register registers a new help command. +func (h *Help) Register(cmd subcommands.Command) { + h.commands = append(h.commands, cmd) +} diff --git a/runsc/cmd/syscalls.go b/runsc/cmd/syscalls.go new file mode 100644 index 000000000..9c8a66490 --- /dev/null +++ b/runsc/cmd/syscalls.go @@ -0,0 +1,347 @@ +// 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 cmd + +import ( + "context" + "encoding/csv" + "encoding/json" + "fmt" + "io" + "os" + "sort" + "strconv" + "text/tabwriter" + + "flag" + "github.com/google/subcommands" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" +) + +// Syscalls implements subcommands.Command for the "syscalls" command. +type Syscalls struct { + output string + os string + arch string +} + +// CompatibilityInfo is a map of system and architecture to compatibility doc. +// Maps operating system to architecture to ArchInfo. +type CompatibilityInfo map[string]map[string]ArchInfo + +// ArchInfo is compatbility doc for an architecture. +type ArchInfo struct { + // Syscalls maps syscall number for the architecture to the doc. + Syscalls map[uintptr]SyscallDoc `json:"syscalls"` +} + +// SyscallDoc represents a single item of syscall documentation. +type SyscallDoc struct { + Name string `json:"name"` + num uintptr + + Support string `json:"support"` + Note string `json:"note,omitempty"` + URLs []string `json:"urls,omitempty"` +} + +type outputFunc func(io.Writer, CompatibilityInfo) error + +var ( + // The string name to use for printing compatibility for all OSes. + osAll = "all" + + // The string name to use for printing compatibility for all architectures. + archAll = "all" + + // A map of OS name to map of architecture name to syscall table. + syscallTableMap = make(map[string]map[string]*kernel.SyscallTable) + + // A map of output type names to output functions. + outputMap = map[string]outputFunc{ + "table": outputTable, + "json": outputJSON, + "csv": outputCSV, + } +) + +// Name implements subcommands.Command.Name. +func (*Syscalls) Name() string { + return "syscalls" +} + +// Synopsis implements subcommands.Command.Synopsis. +func (*Syscalls) Synopsis() string { + return "Print compatibility information for syscalls." +} + +// Usage implements subcommands.Command.Usage. +func (*Syscalls) Usage() string { + return `syscalls [options] - Print compatibility information for syscalls. +` +} + +// SetFlags implements subcommands.Command.SetFlags. +func (s *Syscalls) SetFlags(f *flag.FlagSet) { + f.StringVar(&s.output, "o", "table", "Output format (table, csv, json).") + f.StringVar(&s.os, "os", osAll, "The OS (e.g. linux)") + f.StringVar(&s.arch, "arch", archAll, "The CPU architecture (e.g. amd64).") +} + +// Execute implements subcommands.Command.Execute. +func (s *Syscalls) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + out, ok := outputMap[s.output] + if !ok { + Fatalf("Unsupported output format %q", s.output) + } + + // Build map of all supported architectures. + tables := kernel.SyscallTables() + for _, t := range tables { + osMap, ok := syscallTableMap[t.OS.String()] + if !ok { + osMap = make(map[string]*kernel.SyscallTable) + syscallTableMap[t.OS.String()] = osMap + } + osMap[t.Arch.String()] = t + } + + // Build a map of the architectures we want to output. + info, err := getCompatibilityInfo(s.os, s.arch) + if err != nil { + Fatalf("%v", err) + } + + if err := out(os.Stdout, info); err != nil { + Fatalf("Error writing output: %v", err) + } + + return subcommands.ExitSuccess +} + +// getCompatibilityInfo returns compatibility info for the given OS name and +// architecture name. Supports the special name 'all' for OS and architecture that +// specifies that all supported OSes or architectures should be included. +func getCompatibilityInfo(osName string, archName string) (CompatibilityInfo, error) { + info := CompatibilityInfo(make(map[string]map[string]ArchInfo)) + if osName == osAll { + // Special processing for the 'all' OS name. + for osName, _ := range syscallTableMap { + info[osName] = make(map[string]ArchInfo) + // osName is a specific OS name. + if err := addToCompatibilityInfo(info, osName, archName); err != nil { + return info, err + } + } + } else { + // osName is a specific OS name. + info[osName] = make(map[string]ArchInfo) + if err := addToCompatibilityInfo(info, osName, archName); err != nil { + return info, err + } + } + + return info, nil +} + +// addToCompatibilityInfo adds ArchInfo for the given specific OS name and +// architecture name. Supports the special architecture name 'all' to specify +// that all supported architectures for the OS should be included. +func addToCompatibilityInfo(info CompatibilityInfo, osName string, archName string) error { + if archName == archAll { + // Special processing for the 'all' architecture name. + for archName, _ := range syscallTableMap[osName] { + archInfo, err := getArchInfo(osName, archName) + if err != nil { + return err + } + info[osName][archName] = archInfo + } + } else { + // archName is a specific architecture name. + archInfo, err := getArchInfo(osName, archName) + if err != nil { + return err + } + info[osName][archName] = archInfo + } + + return nil +} + +// getArchInfo returns compatibility info for a specific OS and architecture. +func getArchInfo(osName string, archName string) (ArchInfo, error) { + info := ArchInfo{} + info.Syscalls = make(map[uintptr]SyscallDoc) + + t, ok := syscallTableMap[osName][archName] + if !ok { + return info, fmt.Errorf("syscall table for %s/%s not found", osName, archName) + } + + for num, sc := range t.Table { + info.Syscalls[num] = SyscallDoc{ + Name: sc.Name, + num: num, + Support: sc.SupportLevel.String(), + Note: sc.Note, + URLs: sc.URLs, + } + } + + return info, nil +} + +// outputTable outputs the syscall info in tabular format. +func outputTable(w io.Writer, info CompatibilityInfo) error { + tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) + + // Linux + for osName, osInfo := range info { + for archName, archInfo := range osInfo { + // Print the OS/arch + fmt.Fprintf(w, "%s/%s:\n\n", osName, archName) + + // Sort the syscalls for output in the table. + sortedCalls := []SyscallDoc{} + for _, sc := range archInfo.Syscalls { + sortedCalls = append(sortedCalls, sc) + } + sort.Slice(sortedCalls, func(i, j int) bool { + return sortedCalls[i].num < sortedCalls[j].num + }) + + // Write the header + _, err := fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", + "NUM", + "NAME", + "SUPPORT", + "NOTE", + ) + if err != nil { + return err + } + + // Write each syscall entry + for _, sc := range sortedCalls { + _, err = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n", + strconv.FormatInt(int64(sc.num), 10), + sc.Name, + sc.Support, + sc.Note, + ) + if err != nil { + return err + } + // Add issue urls to note. + for _, url := range sc.URLs { + _, err = fmt.Fprintf(tw, "%s\t%s\t%s\tSee: %s\t\n", + "", + "", + "", + url, + ) + if err != nil { + return err + } + } + } + + err = tw.Flush() + if err != nil { + return err + } + } + } + + return nil +} + +// outputJSON outputs the syscall info in JSON format. +func outputJSON(w io.Writer, info CompatibilityInfo) error { + e := json.NewEncoder(w) + e.SetIndent("", " ") + return e.Encode(info) +} + +// numberedRow is aCSV row annotated by syscall number (used for sorting) +type numberedRow struct { + num uintptr + row []string +} + +// outputCSV outputs the syscall info in tabular format. +func outputCSV(w io.Writer, info CompatibilityInfo) error { + csvWriter := csv.NewWriter(w) + + // Linux + for osName, osInfo := range info { + for archName, archInfo := range osInfo { + // Sort the syscalls for output in the table. + sortedCalls := []numberedRow{} + for _, sc := range archInfo.Syscalls { + // Add issue urls to note. + note := sc.Note + for _, url := range sc.URLs { + note = fmt.Sprintf("%s\nSee: %s", note, url) + } + + sortedCalls = append(sortedCalls, numberedRow{ + num: sc.num, + row: []string{ + osName, + archName, + strconv.FormatInt(int64(sc.num), 10), + sc.Name, + sc.Support, + note, + }, + }) + } + sort.Slice(sortedCalls, func(i, j int) bool { + return sortedCalls[i].num < sortedCalls[j].num + }) + + // Write the header + err := csvWriter.Write([]string{ + "OS", + "Arch", + "Num", + "Name", + "Support", + "Note", + }) + if err != nil { + return err + } + + // Write each syscall entry + for _, sc := range sortedCalls { + err = csvWriter.Write(sc.row) + if err != nil { + return err + } + } + + csvWriter.Flush() + err = csvWriter.Error() + if err != nil { + return err + } + } + } + + return nil +} diff --git a/runsc/main.go b/runsc/main.go index 6f8e6e378..a214f6ba0 100644 --- a/runsc/main.go +++ b/runsc/main.go @@ -76,7 +76,9 @@ var ( func main() { // Help and flags commands are generated automatically. - subcommands.Register(subcommands.HelpCommand(), "") + help := cmd.NewHelp(subcommands.DefaultCommander) + help.Register(new(cmd.Syscalls)) + subcommands.Register(help, "") subcommands.Register(subcommands.FlagsCommand(), "") // Register user-facing runsc commands.