Files in the sandbox may be backed by different implementations. For host-native files (where a file descriptor is available), the Gofer may return a file descriptor to the Sentry via SCM_RIGHTS1.
These files may be read from and written to through standard system calls, and also mapped into the associated application’s address space. This allows the same host memory to be shared across multiple sandboxes, although this mechanism does not preclude the use of side-channels (see Security Model.
Note that some file systems exist only within the context of the sandbox. For example, in many cases a tmpfs
mount will be available at /tmp
or /dev/shm
, which allocates memory directly from the sandbox memory file (see below). Ultimately, these will be accounted against relevant limits in a similar way as the host native case.
root@cloud:~# ps -elf | grep docker 4 S root 926586 1 0 80 0 - 1392756 futex_ Jan14 ? 00:06:08 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock 0 S root 941940 22795 0 80 0 - 177146 futex_ 10:11 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/f1663b36dc38560d1c77639273944c2da612e5c11571bdeb717d371a78b06486 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runsc-kvm 0 S root 942946 940675 0 80 0 - 538568 futex_ 10:45 pts/0 00:00:00 docker run -it --runtime=runsc-kvm --rm debian /bin/bash 0 S root 942983 22795 0 80 0 - 177146 futex_ 10:45 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/13802c34815d499799af132ae792b796d3cbf52b5792aa536dd756c2c6d993a0 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runsc-kvm 4 S root 942997 942983 0 80 0 - 186593 futex_ 10:45 ? 00:00:03 runsc-gofer --root=/var/run/docker/runtime-runsc-kvm/moby --log=/run/containerd/io.containerd.runtime.v1.linux/moby/13802c34815d499799af132ae792b796d3cbf52b5792aa536dd756c2c6d993a0/log.json --log-format=json --platform=kvm --log-fd=3 gofer --bundle /run/containerd/io.containerd.runtime.v1.linux/moby/13802c34815d499799af132ae792b796d3cbf52b5792aa536dd756c2c6d993a0 --spec-fd=4 --mounts-fd=5 --io-fds=6 --io-fds=7 --io-fds=8 --io-fds=9 --apply-caps=false --setup-root=false 4 S nobody 943010 942983 2 80 0 - 68452017770 ep_pol 10:45 pts/3 00:00:41 runsc-sandbox --root=/var/run/docker/runtime-runsc-kvm/moby --log=/run/containerd/io.containerd.runtime.v1.linux/moby/13802c34815d499799af132ae792b796d3cbf52b5792aa536dd756c2c6d993a0/log.json --log-format=json --platform=kvm --log-fd=3 boot --bundle=/run/containerd/io.containerd.runtime.v1.linux/moby/13802c34815d499799af132ae792b796d3cbf52b5792aa536dd756c2c6d993a0 --controller-fd=4 --mounts-fd=5 --spec-fd=6 --start-sync-fd=7 --io-fds=8 --io-fds=9 --io-fds=10 --io-fds=11 --device-fd=12 --stdio-fds=13 --stdio-fds=14 --stdio-fds=15 --pidns=true --cpu-num 64 13802c34815d499799af132ae792b796d3cbf52b5792aa536dd756c2c6d993a0 0 S root 943339 940908 0 80 0 - 1418 pipe_r 11:19 pts/1 00:00:00 grep --color=auto docker root@cloud:~# dlv attach 942997
(dlv) bt 0 0x000000000008dccc in syscall.Syscall6 at src/syscall/asm_linux_arm64.s:43 1 0x00000000002c1ce8 in gvisor.dev/gvisor/pkg/flipcall.(*Endpoint).futexWaitConnState at pkg/flipcall/futex_linux.go:76 2 0x00000000002c1b58 in gvisor.dev/gvisor/pkg/flipcall.(*Endpoint).futexWaitUntilActive at pkg/flipcall/futex_linux.go:56 3 0x00000000002c08bc in gvisor.dev/gvisor/pkg/flipcall.(*Endpoint).ctrlRoundTrip at pkg/flipcall/ctrl_futex.go:133 4 0x00000000002c15c4 in gvisor.dev/gvisor/pkg/flipcall.(*Endpoint).SendRecv at pkg/flipcall/flipcall.go:234 5 0x00000000002e791c in gvisor.dev/gvisor/pkg/p9.(*channel).send at pkg/p9/transport_flipcall.go:165 6 0x00000000002e74f8 in gvisor.dev/gvisor/pkg/p9.(*channel).service at pkg/p9/transport_flipcall.go:88 7 0x00000000002ed06c in gvisor.dev/gvisor/pkg/p9.(*connState).initializeChannels.func1 at pkg/p9/server.go:457 8 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv)
cmd/help.go
// 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 <flags> <subcommand> <subcommand args> ", h.cdr.Name()) fmt.Fprintf(h.cdr.Output, `runsc is the gVisor container runtime. Functionality is provided by subcommands. For help with a specific subcommand, use "%s %s <subcommand>". `, 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 <topic>" to see help on the topic): ", h.cdr.Name(), h.Name()) for _, cmd := range h.commands { fmt.Fprintf(h.cdr.Output, " %-15s %s ", cmd.Name(), cmd.Synopsis()) } fmt.Fprintf(h.cdr.Output, " Use "%s flags" for a list of top-level flags ", 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 ", f.Arg(0)) } f.Usage() return subcommands.ExitUsageError }
// Execute implements subcommands.Command. func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { if g.bundleDir == "" || len(g.ioFDs) < 1 || g.specFD < 0 { f.Usage() return subcommands.ExitUsageError } conf := args[0].(*config.Config) specFile := os.NewFile(uintptr(g.specFD), "spec file") defer specFile.Close() spec, err := specutils.ReadSpecFromFile(g.bundleDir, specFile, conf) if err != nil { Fatalf("reading spec: %v", err) } if g.setUpRoot { if err := setupRootFS(spec, conf); err != nil { Fatalf("Error setting up root FS: %v", err) } } if g.applyCaps { // Disable caps when calling myself again. // Note: minimal argument handling for the default case to keep it simple. args := os.Args args = append(args, "--apply-caps=false", "--setup-root=false") if err := setCapsAndCallSelf(args, goferCaps); err != nil { Fatalf("Unable to apply caps: %v", err) } panic("unreachable") } // Find what path is going to be served by this gofer. root := spec.Root.Path if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { root = "/root" } // Resolve mount points paths, then replace mounts from our spec and send the // mount list over to the sandbox, so they are both in sync. // // Note that all mount points have been mounted in the proper location in // setupRootFS(). cleanMounts, err := resolveMounts(conf, spec.Mounts, root) if err != nil { Fatalf("Failure to resolve mounts: %v", err) } spec.Mounts = cleanMounts go func() { if err := g.writeMounts(cleanMounts); err != nil { panic(fmt.Sprintf("Failed to write mounts: %v", err)) } }() specutils.LogSpec(spec) // fsgofer should run with a umask of 0, because we want to preserve file // modes exactly as sent by the sandbox, which will have applied its own umask. syscall.Umask(0) if err := fsgofer.OpenProcSelfFD(); err != nil { Fatalf("failed to open /proc/self/fd: %v", err) } if err := syscall.Chroot(root); err != nil { Fatalf("failed to chroot to %q: %v", root, err) } if err := syscall.Chdir("/"); err != nil { Fatalf("changing working dir: %v", err) } log.Infof("Process chroot'd to %q", root) // Start with root mount, then add any other additional mount as needed. ats := make([]p9.Attacher, 0, len(spec.Mounts)+1) ap, err := fsgofer.NewAttachPoint("/", fsgofer.Config{ ROMount: spec.Root.Readonly || conf.Overlay, }) if err != nil { Fatalf("creating attach point: %v", err) } ats = append(ats, ap) log.Infof("Serving %q mapped to %q on FD %d (ro: %t)", "/", root, g.ioFDs[0], spec.Root.Readonly) mountIdx := 1 // first one is the root for _, m := range spec.Mounts { if specutils.Is9PMount(m) { cfg := fsgofer.Config{ ROMount: isReadonlyMount(m.Options) || conf.Overlay, HostUDS: conf.FSGoferHostUDS, } ap, err := fsgofer.NewAttachPoint(m.Destination, cfg) if err != nil { Fatalf("creating attach point: %v", err) } ats = append(ats, ap) if mountIdx >= len(g.ioFDs) { Fatalf("no FD found for mount. Did you forget --io-fd? mount: %d, %v", len(g.ioFDs), m) } log.Infof("Serving %q mapped on FD %d (ro: %t)", m.Destination, g.ioFDs[mountIdx], cfg.ROMount) mountIdx++ } } if mountIdx != len(g.ioFDs) { Fatalf("too many FDs passed for mounts. mounts: %d, FDs: %d", mountIdx, len(g.ioFDs)) } if conf.FSGoferHostUDS { filter.InstallUDSFilters() } if err := filter.Install(); err != nil { Fatalf("installing seccomp filters: %v", err) } runServers(ats, g.ioFDs) return subcommands.ExitSuccess }
I0115 09:33:42.721710 1 main.go:210] Args: [runsc-gofer --root=/run/user/0/runsc --debug-log=/tmp/runsc/ --strace=true --debug-log-fd=3 gofer --bundle /mycontainer --spec-fd=4 --mounts-fd=5 --io-fds=6 --apply-caps=false --setup-root=false] I0115 09:33:42.721755 1 main.go:211] Version release-20201216.0-106-gc49ce8ca8ab9 I0115 09:33:42.721775 1 main.go:212] GOOS: linux I0115 09:33:42.721794 1 main.go:213] GOARCH: arm64 I0115 09:33:42.721817 1 main.go:214] PID: 1 I0115 09:33:42.721837 1 main.go:215] UID: 0, GID: 0 I0115 09:33:42.721857 1 main.go:216] Configuration: I0115 09:33:42.721876 1 main.go:217] RootDir: /run/user/0/runsc I0115 09:33:42.721896 1 main.go:218] Platform: ptrace I0115 09:33:42.721915 1 main.go:219] FileAccess: 0, overlay: false I0115 09:33:42.721937 1 main.go:220] Network: 0, logging: false I0115 09:33:42.721959 1 main.go:221] Strace: true, max size: 1024, syscalls: I0115 09:33:42.721979 1 main.go:222] VFS2 enabled: false I0115 09:33:42.721998 1 main.go:223] *************************** I0115 09:33:42.723379 1 gofer.go:164] Process chroot'd to "/root" I0115 09:33:42.723409 1 gofer.go:175] Serving "/" mapped to "/root" on FD 6 (ro: true) I0115 09:33:42.723428 1 seccomp.go:61] Installing seccomp filters for 57 syscalls (action=kill process) I0115 09:33:42.724207 1 seccomp.go:85] Seccomp filters installed. I0115 09:34:32.255208 1 gofer.go:231] All 9P servers exited. I0115 09:34:32.255286 1 main.go:236] Exiting with status: 0 ~ ~ ~ ~ ~ "/tmp/runsc/runsc.log.20210115-173342.650687.gofer" 40L, 3384C
文件系统读写
Let's walk through a read operation from the application:
- The application running inside the sandbox issues a read(2) syscall to gVisor.
- gVisor looks up the FD from the read call in the task's FD table.
- That FD belongs to a file backed by pkg/sentry/fs/gofer.fileOperations.
- gofer.fileOperations.Read() is called.
- gofer.fileOperations checks whether it has a host FD (note that this is different from the FD in step 2 which is a virtual FD that doesn't exist in the host).
- If it does, it can shortcut the read and issues a read(2) syscall to the host.
- If it doesn't, then it issues a 9P read call to the gofer.
root@cloud:~# docker run -it --runtime=runsc-kvm -v share:/share --name test --rm debian /bin/bash root@a6a7c81dcfe9:/# cd share/ root@a6a7c81dcfe9:/share# ls hello.txt root@a6a7c81dcfe9:/share# touch hello.txt root@a6a7c81dcfe9:/share# echo hello hello.txt hello hello.txt root@a6a7c81dcfe9:/share# echo hello >> hello.txt root@a6a7c81dcfe9:/share# echo hello >> hello.txt root@a6a7c81dcfe9:/share# root@a6a7c81dcfe9:/share# root@a6a7c81dcfe9:/share# root@a6a7c81dcfe9:/share# root@a6a7c81dcfe9:/share# echo hello >> hello.txt root@a6a7c81dcfe9:/share#
gdb func (r *ReadWriter) Write(b []byte) (int, error)
(dlv) b fd/fd.go:110 Breakpoint 4 set at 0x286b40 for gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (dlv) c > gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (hits goroutine(102):1 total:1) (PC: 0x286b40) Warning: debugging optimized function (dlv) bt 0 0x0000000000286b40 in gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt at pkg/fd/fd.go:110 1 0x000000000069c3a4 in gvisor.dev/gvisor/pkg/secio.(*SectionWriter).Write at pkg/secio/secio.go:84 2 0x0000000000159dd4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.writeFromBlock at pkg/safemem/io.go:295 3 0x0000000000159bb4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.WriteFromBlocks at pkg/safemem/io.go:281 4 0x00000000006dd784 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*handleReadWriter).WriteFromBlocks at pkg/sentry/fs/gofer/handles.go:138 5 0x00000000006de538 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*inodeFileState).WriteFromBlocksAt at pkg/sentry/fs/gofer/inode.go:301 6 0x00000000003affb0 in gvisor.dev/gvisor/pkg/sentry/fs/fsutil.(*writer).WriteFromBlocks at pkg/sentry/fs/fsutil/host_mappable.go:210 7 0x00000000004033e0 in gvisor.dev/gvisor/pkg/safemem.Writer.WriteFromBlocks-fm at pkg/safemem/io.go:46 8 0x00000000003db314 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withInternalMappings at pkg/sentry/mm/io.go:506 9 0x00000000003dba08 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withVecInternalMappings at pkg/sentry/mm/io.go:575 10 0x00000000003d9f14 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).CopyInTo at pkg/sentry/mm/io.go:309 11 0x00000000003afe94 in gvisor.dev/gvisor/pkg/usermem.IOSequence.CopyInTo at pkg/usermem/usermem.go:514 12 0x00000000003afe94 in gvisor.dev/gvisor/pkg/sentry/fs/fsutil.(*HostMappable).Write at pkg/sentry/fs/fsutil/host_mappable.go:197 13 0x00000000006db464 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*fileOperations).Write at pkg/sentry/fs/gofer/file.go:252 14 0x000000000031f78c in gvisor.dev/gvisor/pkg/sentry/fs.(*File).Writev at pkg/sentry/fs/file.go:306 15 0x0000000000597b28 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.writev at pkg/sentry/syscalls/linux/sys_write.go:262 16 0x0000000000596470 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.Write at pkg/sentry/syscalls/linux/sys_write.go:72 17 0x0000000000522ea4 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).executeSyscall at pkg/sentry/kernel/task_syscall.go:104 18 0x0000000000523c5c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallInvoke at pkg/sentry/kernel/task_syscall.go:239 19 0x00000000005238dc in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallEnter at pkg/sentry/kernel/task_syscall.go:199 20 0x00000000005233e0 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscall at pkg/sentry/kernel/task_syscall.go:174 21 0x0000000000518e00 in gvisor.dev/gvisor/pkg/sentry/kernel.(*runApp).execute at pkg/sentry/kernel/task_run.go:282 22 0x0000000000517d9c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).run at pkg/sentry/kernel/task_run.go:97 23 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv) c > gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (hits goroutine(102):2 total:2) (PC: 0x286b40) Warning: debugging optimized function (dlv) bt 0 0x0000000000286b40 in gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt at pkg/fd/fd.go:110 1 0x000000000069c3a4 in gvisor.dev/gvisor/pkg/secio.(*SectionWriter).Write at pkg/secio/secio.go:84 2 0x0000000000159dd4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.writeFromBlock at pkg/safemem/io.go:295 3 0x0000000000159bb4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.WriteFromBlocks at pkg/safemem/io.go:281 4 0x00000000006dd784 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*handleReadWriter).WriteFromBlocks at pkg/sentry/fs/gofer/handles.go:138 5 0x00000000006de538 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*inodeFileState).WriteFromBlocksAt at pkg/sentry/fs/gofer/inode.go:301 6 0x00000000003affb0 in gvisor.dev/gvisor/pkg/sentry/fs/fsutil.(*writer).WriteFromBlocks at pkg/sentry/fs/fsutil/host_mappable.go:210 7 0x00000000004033e0 in gvisor.dev/gvisor/pkg/safemem.Writer.WriteFromBlocks-fm at pkg/safemem/io.go:46 8 0x00000000003db314 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withInternalMappings at pkg/sentry/mm/io.go:506 9 0x00000000003dba08 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withVecInternalMappings at pkg/sentry/mm/io.go:575 10 0x00000000003d9f14 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).CopyInTo at pkg/sentry/mm/io.go:309 11 0x00000000003afe94 in gvisor.dev/gvisor/pkg/usermem.IOSequence.CopyInTo at pkg/usermem/usermem.go:514 12 0x00000000003afe94 in gvisor.dev/gvisor/pkg/sentry/fs/fsutil.(*HostMappable).Write at pkg/sentry/fs/fsutil/host_mappable.go:197 13 0x00000000006db464 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*fileOperations).Write at pkg/sentry/fs/gofer/file.go:252 14 0x000000000031f78c in gvisor.dev/gvisor/pkg/sentry/fs.(*File).Writev at pkg/sentry/fs/file.go:306 15 0x0000000000597b28 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.writev at pkg/sentry/syscalls/linux/sys_write.go:262 16 0x0000000000596470 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.Write at pkg/sentry/syscalls/linux/sys_write.go:72 17 0x0000000000522ea4 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).executeSyscall at pkg/sentry/kernel/task_syscall.go:104 18 0x0000000000523c5c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallInvoke at pkg/sentry/kernel/task_syscall.go:239 19 0x00000000005238dc in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallEnter at pkg/sentry/kernel/task_syscall.go:199 20 0x00000000005233e0 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscall at pkg/sentry/kernel/task_syscall.go:174 21 0x0000000000518e00 in gvisor.dev/gvisor/pkg/sentry/kernel.(*runApp).execute at pkg/sentry/kernel/task_run.go:282 22 0x0000000000517d9c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).run at pkg/sentry/kernel/task_run.go:97 23 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv) c
I am trying to figure out the whole code path of a write system call from application(the hello-world docker image) under KVM platform. I find that the control flow actually goes into Write -- pkg/sentry/fs/host/file.go Eventually, after a long calling chain, it reaches Write -- pkg/fd/fd.go
(dlv) b fd/fd.go:80 Breakpoint 2 set at 0x286970 for gvisor.dev/gvisor/pkg/fd.(*ReadWriter).Write() pkg/fd/fd.go:80 (dlv) b fd/fd.go:110 Breakpoint 3 set at 0x286b40 for gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (dlv) c > gvisor.dev/gvisor/pkg/fd.(*ReadWriter).Write() pkg/fd/fd.go:80 (hits goroutine(102):1 total:1) (PC: 0x286970) Warning: debugging optimized function (dlv) bt 0 0x0000000000286970 in gvisor.dev/gvisor/pkg/fd.(*ReadWriter).Write at pkg/fd/fd.go:80 1 0x0000000000159dd4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.writeFromBlock at pkg/safemem/io.go:295 2 0x0000000000159bb4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.WriteFromBlocks at pkg/safemem/io.go:281 3 0x000000000015c480 in gvisor.dev/gvisor/pkg/safemem.(*FromIOWriter).WriteFromBlocks at <autogenerated>:1 4 0x00000000004033e0 in gvisor.dev/gvisor/pkg/safemem.Writer.WriteFromBlocks-fm at pkg/safemem/io.go:46 5 0x00000000003db314 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withInternalMappings at pkg/sentry/mm/io.go:506 6 0x00000000003dba08 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withVecInternalMappings at pkg/sentry/mm/io.go:575 7 0x00000000003d9f14 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).CopyInTo at pkg/sentry/mm/io.go:309 8 0x000000000069e060 in gvisor.dev/gvisor/pkg/usermem.IOSequence.CopyInTo at pkg/usermem/usermem.go:514 9 0x000000000069e060 in gvisor.dev/gvisor/pkg/sentry/fs/host.(*fileOperations).Write at pkg/sentry/fs/host/file.go:208 10 0x00000000006a2c90 in gvisor.dev/gvisor/pkg/sentry/fs/host.(*TTYFileOperations).Write at pkg/sentry/fs/host/tty.go:113 11 0x000000000031f78c in gvisor.dev/gvisor/pkg/sentry/fs.(*File).Writev at pkg/sentry/fs/file.go:306 12 0x0000000000597b28 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.writev at pkg/sentry/syscalls/linux/sys_write.go:262 13 0x0000000000596470 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.Write at pkg/sentry/syscalls/linux/sys_write.go:72 14 0x0000000000522ea4 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).executeSyscall at pkg/sentry/kernel/task_syscall.go:104 15 0x0000000000523c5c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallInvoke at pkg/sentry/kernel/task_syscall.go:239 16 0x00000000005238dc in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallEnter at pkg/sentry/kernel/task_syscall.go:199 17 0x00000000005233e0 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscall at pkg/sentry/kernel/task_syscall.go:174 18 0x0000000000518e00 in gvisor.dev/gvisor/pkg/sentry/kernel.(*runApp).execute at pkg/sentry/kernel/task_run.go:282 19 0x0000000000517d9c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).run at pkg/sentry/kernel/task_run.go:97 20 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv) c > gvisor.dev/gvisor/pkg/fd.(*ReadWriter).Write() pkg/fd/fd.go:80 (hits goroutine(102):2 total:2) (PC: 0x286970) Warning: debugging optimized function (dlv) c > gvisor.dev/gvisor/pkg/fd.(*ReadWriter).Write() pkg/fd/fd.go:80 (hits goroutine(102):3 total:3) (PC: 0x286970) Warning: debugging optimized function (dlv) clearall Breakpoint 1 cleared at 0x9685b0 for gvisor.dev/gvisor/runsc/fsgofer.(*localFile).Open() runsc/fsgofer/fsgofer.go:381 Breakpoint 2 cleared at 0x286970 for gvisor.dev/gvisor/pkg/fd.(*ReadWriter).Write() pkg/fd/fd.go:80 Breakpoint 3 cleared at 0x286b40 for gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (dlv) b fd/fd.go:110 Breakpoint 4 set at 0x286b40 for gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (dlv) c > gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt() pkg/fd/fd.go:110 (hits goroutine(102):1 total:1) (PC: 0x286b40) Warning: debugging optimized function (dlv) bt 0 0x0000000000286b40 in gvisor.dev/gvisor/pkg/fd.(*ReadWriter).WriteAt at pkg/fd/fd.go:110 1 0x000000000069c3a4 in gvisor.dev/gvisor/pkg/secio.(*SectionWriter).Write at pkg/secio/secio.go:84 2 0x0000000000159dd4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.writeFromBlock at pkg/safemem/io.go:295 3 0x0000000000159bb4 in gvisor.dev/gvisor/pkg/safemem.FromIOWriter.WriteFromBlocks at pkg/safemem/io.go:281 4 0x00000000006dd784 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*handleReadWriter).WriteFromBlocks at pkg/sentry/fs/gofer/handles.go:138 5 0x00000000006de538 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*inodeFileState).WriteFromBlocksAt at pkg/sentry/fs/gofer/inode.go:301 6 0x00000000003affb0 in gvisor.dev/gvisor/pkg/sentry/fs/fsutil.(*writer).WriteFromBlocks at pkg/sentry/fs/fsutil/host_mappable.go:210 7 0x00000000004033e0 in gvisor.dev/gvisor/pkg/safemem.Writer.WriteFromBlocks-fm at pkg/safemem/io.go:46 8 0x00000000003db314 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withInternalMappings at pkg/sentry/mm/io.go:506 9 0x00000000003dba08 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).withVecInternalMappings at pkg/sentry/mm/io.go:575 10 0x00000000003d9f14 in gvisor.dev/gvisor/pkg/sentry/mm.(*MemoryManager).CopyInTo at pkg/sentry/mm/io.go:309 11 0x00000000003afe94 in gvisor.dev/gvisor/pkg/usermem.IOSequence.CopyInTo at pkg/usermem/usermem.go:514 12 0x00000000003afe94 in gvisor.dev/gvisor/pkg/sentry/fs/fsutil.(*HostMappable).Write at pkg/sentry/fs/fsutil/host_mappable.go:197 13 0x00000000006db464 in gvisor.dev/gvisor/pkg/sentry/fs/gofer.(*fileOperations).Write at pkg/sentry/fs/gofer/file.go:252 14 0x000000000031f78c in gvisor.dev/gvisor/pkg/sentry/fs.(*File).Writev at pkg/sentry/fs/file.go:306 15 0x0000000000597b28 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.writev at pkg/sentry/syscalls/linux/sys_write.go:262 16 0x0000000000596470 in gvisor.dev/gvisor/pkg/sentry/syscalls/linux.Write at pkg/sentry/syscalls/linux/sys_write.go:72 17 0x0000000000522ea4 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).executeSyscall at pkg/sentry/kernel/task_syscall.go:104 18 0x0000000000523c5c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallInvoke at pkg/sentry/kernel/task_syscall.go:239 19 0x00000000005238dc in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscallEnter at pkg/sentry/kernel/task_syscall.go:199 20 0x00000000005233e0 in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).doSyscall at pkg/sentry/kernel/task_syscall.go:174 21 0x0000000000518e00 in gvisor.dev/gvisor/pkg/sentry/kernel.(*runApp).execute at pkg/sentry/kernel/task_run.go:282 22 0x0000000000517d9c in gvisor.dev/gvisor/pkg/sentry/kernel.(*Task).run at pkg/sentry/kernel/task_run.go:97 23 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136
SCM_RIGHTS
The gofer mediates file system access, but may pass a FD to the sandbox via SCM_RIGHTS for regular files.
dlv gofer 进程
root@cloud:~# docker run -it --runtime=runsc-kvm -v share:/share --name test --rm debian /bin/bash root@a6a7c81dcfe9:/share# exit exit
runsc/cmd/gofer.go:213:
func runServers(ats []p9.Attacher, ioFDs []int) { // Run the loops and wait for all to exit. var wg sync.WaitGroup for i, ioFD := range ioFDs { wg.Add(1) go func(ioFD int, at p9.Attacher) { socket, err := unet.NewSocket(ioFD) if err != nil { Fatalf("creating server on FD %d: %v", ioFD, err) } s := p9.NewServer(at) if err := s.Handle(socket); err != nil { Fatalf("P9 server returned error. Gofer is shutting down. FD: %d, err: %v", ioFD, err) } wg.Done() }(ioFD, ats[i]) } wg.Wait() log.Infof("All 9P servers exited.") }
// NewServer returns a new server. func NewServer(attacher Attacher) *Server { return &Server{ attacher: attacher, pathTree: newPathNode(), } }
// Handle handles a single connection. func (s *Server) Handle(conn *unet.Socket) error { cs := &connState{ server: s, fids: make(map[FID]*fidRef), tags: make(map[Tag]chan struct{}), conn: conn, } defer cs.stop() // Serve requests from conn in the current goroutine; handleRequests() will // create more goroutines as needed. cs.handleRequests() return nil }
root@cloud:~# dlv attach 944937 Type 'help' for list of commands. (dlv) b p9/server.go:532 Breakpoint 1 set at 0x2e4ea0 for gvisor.dev/gvisor/pkg/p9.(*connState).handleRequest() pkg/p9/server.go:532 (dlv) c > gvisor.dev/gvisor/pkg/p9.(*connState).handleRequest() pkg/p9/server.go:532 (hits goroutine(19):1 total:1) (PC: 0x2e4ea0) Warning: debugging optimized function (dlv) bt 0 0x00000000002e4ea0 in gvisor.dev/gvisor/pkg/p9.(*connState).handleRequest at pkg/p9/server.go:532 1 0x00000000002e5478 in gvisor.dev/gvisor/pkg/p9.(*connState).handleRequests at pkg/p9/server.go:590 2 0x00000000002e5788 in gvisor.dev/gvisor/pkg/p9.(*Server).Handle at pkg/p9/server.go:645 3 0x00000000009900f4 in gvisor.dev/gvisor/runsc/cmd.runServers.func1 at runsc/cmd/gofer.go:224 4 0x0000000000077c84 in runtime.goexit at src/runtime/asm_arm64.s:1136 (dlv) n > gvisor.dev/gvisor/pkg/p9.(*connState).handleRequest() pkg/p9/server.go:526 (PC: 0x2e4ec4) Warning: debugging optimized function (dlv) list > gvisor.dev/gvisor/pkg/p9.(*connState).handleRequest() pkg/p9/server.go:526 (PC: 0x2e4ec4) Warning: debugging optimized function Command failed: open pkg/p9/server.go: no such file or directory (dlv) n > gvisor.dev/gvisor/pkg/p9.(*connState).handleRequest() pkg/p9/server.go:533 (PC: 0x2e4ef4) Warning: debugging optimized function (dlv) p m gvisor.dev/gvisor/pkg/p9.message(*gvisor.dev/gvisor/pkg/p9.Tclunk) *{FID: 77} (dlv) p cs *gvisor.dev/gvisor/pkg/p9.connState { server: *gvisor.dev/gvisor/pkg/p9.Server { attacher: gvisor.dev/gvisor/pkg/p9.Attacher(*gvisor.dev/gvisor/runsc/fsgofer.attachPoint) ..., pathTree: *(*"gvisor.dev/gvisor/pkg/p9.pathNode")(0x400048e000), renameMu: (*"gvisor.dev/gvisor/pkg/sync.RWMutex")(0x400048c0d8),}, fidMu: gvisor.dev/gvisor/pkg/sync.Mutex { m: (*"gvisor.dev/gvisor/pkg/sync.CrossGoroutineMutex")(0x4000490008),}, fids: map[gvisor.dev/gvisor/pkg/p9.FID]*gvisor.dev/gvisor/pkg/p9.fidRef [ 4: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e410), 64: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40002148c0), 67: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40002144b0), 75: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000214ff0), 5: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e550), 39: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018e5f0), 50: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f270), 73: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000214e10), 2: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e190), 19: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048f6d0), 28: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400081a7d0), 37: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018e3c0), 15: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048f220), 38: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018e4b0), 61: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000126a50), 68: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40002145a0), 70: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000214af0), 14: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048f130), 18: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048f590), 42: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018e9b0), 45: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018ed70), 72: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000214cd0), 9: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048ea50), 16: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048f310), 48: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f040), 49: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f130), 60: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000126960), 69: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40002149b0), 6: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e690), 20: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048f7c0), 24: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400081a3c0), 47: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018ef50), 53: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000126320), 77: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f9f0), 76: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018fae0), 12: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048ef00), 13: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048eff0), 29: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400081a8c0), 57: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000214140), 11: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048ee10), 8: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e910), 10: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048eb90), 43: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018eaf0), 46: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018ee60), 52: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40001261e0), 54: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000126460), 79: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018fd10), 41: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018e870), 51: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f360), 56: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40001266e0), 66: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f540), 22: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400081a190), 36: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018e2d0), 23: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400081a280), 30: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400081aa00), 33: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40007aa0a0), 44: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018ec30), 65: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400018f450), 1: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e0a0), 55: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40001265a0), 59: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40001267d0), 63: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x40002147d0), 71: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x4000214be0), 3: *(*"gvisor.dev/gvisor/pkg/p9.fidRef")(0x400048e2d0), ...+14 more ], tagMu: gvisor.dev/gvisor/pkg/sync.Mutex { m: (*"gvisor.dev/gvisor/pkg/sync.CrossGoroutineMutex")(0x4000490018),}, tags: map[gvisor.dev/gvisor/pkg/p9.Tag]chan struct {} [ 1: { qcount: 0, dataqsiz: 0, buf: *[0]struct struct {} [], elemsize: 0, closed: 0, elemtype: *runtime._type {size: 0, ptrdata: 0, hash: 670477339, tflag: tflagExtraStar|tflagRegularMemory (10), align: 1, fieldAlign: 1, kind: 25, equal: runtime.memequal0, gcdata: *1, str: 54665, ptrToThis: 498784}, sendx: 0, recvx: 0, recvq: waitq<struct {}> { first: *sudog<struct {}> nil, last: *sudog<struct {}> nil,}, sendq: waitq<struct {}> { first: *sudog<struct {}> nil, last: *sudog<struct {}> nil,}, lock: runtime.mutex { lockRankStruct: runtime.lockRankStruct {}, key: 0,},}, ], messageSize: 1048576, version: 12, pendingWg: sync.WaitGroup { noCopy: sync.noCopy {}, state1: [3]uint32 [0,0,0],}, recvMu: gvisor.dev/gvisor/pkg/sync.Mutex { m: (*"gvisor.dev/gvisor/pkg/sync.CrossGoroutineMutex")(0x400049003c),}, recvIdle: 0, recvShutdown: false, sendMu: gvisor.dev/gvisor/pkg/sync.Mutex { m: (*"gvisor.dev/gvisor/pkg/sync.CrossGoroutineMutex")(0x400049004c),}, conn: *gvisor.dev/gvisor/pkg/unet.Socket { gate: (*"gvisor.dev/gvisor/pkg/gate.Gate")(0x400048c000), fd: 6, efd: 22, race: *int32 nil,}, channelMu: gvisor.dev/gvisor/pkg/sync.Mutex { m: (*"gvisor.dev/gvisor/pkg/sync.CrossGoroutineMutex")(0x4000490060),}, channelWg: sync.WaitGroup { noCopy: sync.noCopy {}, state1: [3]uint32 [0,4,0],}, channelAlloc: *gvisor.dev/gvisor/pkg/flipcall.PacketWindowAllocator {fd: 23, nextAlloc: 4210688, fileSize: 8388608}, channels: []*gvisor.dev/gvisor/pkg/p9.channel len: 4, cap: 4, [ *(*"gvisor.dev/gvisor/pkg/p9.channel")(0x40004a2000), *(*"gvisor.dev/gvisor/pkg/p9.channel")(0x40004a20c0), *(*"gvisor.dev/gvisor/pkg/p9.channel")(0x40004a2180), *(*"gvisor.dev/gvisor/pkg/p9.channel")(0x40004a2240), ],} (dlv) p tag 5
Command
// Register registers a new help command. func (h *Help) Register(cmd subcommands.Command) { h.commands = append(h.commands, cmd) }
"github.com/google/subcommands"
// A Command represents a single command. type Command interface { // Name returns the name of the command. Name() string // Synopsis returns a short string (less than one line) describing the command. Synopsis() string // Usage returns a long string explaining the command and giving usage // information. Usage() string // SetFlags adds the flags for this command to the specified set. SetFlags(*flag.FlagSet) // Execute executes the command and returns an ExitStatus. Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) ExitStatus }
// Register adds a subcommand to the supported subcommands in the // specified group. (Help output is sorted and arranged by group // name.) The empty string is an acceptable group name; such // subcommands are explained first before named groups. It is a // wrapper around DefaultCommander.Register. func Register(cmd Command, group string) { DefaultCommander.Register(cmd, group) }
func Execute(ctx context.Context, args ...interface{}) ExitStatus { return DefaultCommander.Execute(ctx, args...) }
cli/main.go:59: help.Register(new(cmd.Syscalls)) cli/main.go:60: subcommands.Register(help, "") cli/main.go:61: subcommands.Register(subcommands.FlagsCommand(), "") cli/main.go:65: subcommands.Register(new(cmd.Install), helperGroup) cli/main.go:66: subcommands.Register(new(cmd.Uninstall), helperGroup) cli/main.go:68: // Register user-facing runsc commands. cli/main.go:69: subcommands.Register(new(cmd.Checkpoint), "") cli/main.go:70: subcommands.Register(new(cmd.Create), "") cli/main.go:71: subcommands.Register(new(cmd.Delete), "") cli/main.go:72: subcommands.Register(new(cmd.Do), "") cli/main.go:73: subcommands.Register(new(cmd.Events), "") cli/main.go:74: subcommands.Register(new(cmd.Exec), "") cli/main.go:75: subcommands.Register(new(cmd.Gofer), "") cli/main.go:76: subcommands.Register(new(cmd.Kill), "") cli/main.go:77: subcommands.Register(new(cmd.List), "") cli/main.go:78: subcommands.Register(new(cmd.Pause), "") cli/main.go:79: subcommands.Register(new(cmd.PS), "") cli/main.go:80: subcommands.Register(new(cmd.Restore), "") cli/main.go:81: subcommands.Register(new(cmd.Resume), "") cli/main.go:82: subcommands.Register(new(cmd.Run), "") cli/main.go:83: subcommands.Register(new(cmd.Spec), "") cli/main.go:84: subcommands.Register(new(cmd.State), "") cli/main.go:85: subcommands.Register(new(cmd.Start), "") cli/main.go:86: subcommands.Register(new(cmd.Symbolize), "") cli/main.go:87: subcommands.Register(new(cmd.Wait), "") cli/main.go:89: // Register internal commands with the internal group name. This causes cli/main.go:93: subcommands.Register(new(cmd.Boot), internalGroup) cli/main.go:94: subcommands.Register(new(cmd.Debug), internalGroup) cli/main.go:95: subcommands.Register(new(cmd.Gofer), internalGroup) cli/main.go:96: subcommands.Register(new(cmd.Statefile), internalGroup) cli/main.go:98: config.RegisterFlags()
var ws syscall.WaitStatus subcmdCode := subcommands.Execute(context.Background(), conf, &ws) if subcmdCode == subcommands.ExitSuccess { log.Infof("Exiting with status: %v", ws) if ws.Signaled() { // No good way to return it, emulate what the shell does. Maybe raise // signal to self? os.Exit(128 + int(ws.Signal())) } os.Exit(ws.ExitStatus()) }