diff options
-rw-r--r-- | cmd/podman/system/unshare.go | 21 | ||||
-rw-r--r-- | docs/source/markdown/podman-unshare.1.md | 29 | ||||
-rw-r--r-- | test/e2e/unshare_test.go | 35 |
3 files changed, 82 insertions, 3 deletions
diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go index f930d8b62..50230609e 100644 --- a/cmd/podman/system/unshare.go +++ b/cmd/podman/system/unshare.go @@ -2,6 +2,7 @@ package system import ( "os" + "os/exec" "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" @@ -50,5 +51,23 @@ func unshare(cmd *cobra.Command, args []string) error { args = []string{shell} } - return registry.ContainerEngine().Unshare(registry.Context(), args, unshareOptions) + err := registry.ContainerEngine().Unshare(registry.Context(), args, unshareOptions) + if err != nil { + if exitError, ok := err.(*exec.ExitError); ok { + // the user command inside the unshare env has failed + // we set the exit code, do not return the error to the user + // otherwise "exit status X" will be printed + registry.SetExitCode(exitError.ExitCode()) + return nil + } + // cmd.Run() can return fs.ErrNotExist, fs.ErrPermission or exec.ErrNotFound + // follow podman run/exec standard with the exit codes + if errors.Is(err, os.ErrNotExist) || errors.Is(err, exec.ErrNotFound) { + registry.SetExitCode(127) + } else if errors.Is(err, os.ErrPermission) { + registry.SetExitCode(126) + } + return err + } + return nil } diff --git a/docs/source/markdown/podman-unshare.1.md b/docs/source/markdown/podman-unshare.1.md index 2e7adfd34..72821b6e5 100644 --- a/docs/source/markdown/podman-unshare.1.md +++ b/docs/source/markdown/podman-unshare.1.md @@ -37,6 +37,35 @@ connect to a rootless container via IP address (CNI networking). This is otherwi not possible from the host network namespace. _Note: Using this option with more than one unshare session can have unexpected results._ +## Exit Codes + +The exit code from `podman unshare` gives information about why the container +failed to run or why it exited. When `podman unshare` commands exit with a non-zero code, +the exit codes follow the `chroot` standard, see below: + + **125** The error is with podman **_itself_** + + $ podman unshare --foo; echo $? + Error: unknown flag: --foo + 125 + + **126** Executing a _contained command_ and the _command_ cannot be invoked + + $ podman unshare /etc; echo $? + Error: fork/exec /etc: permission denied + 126 + + **127** Executing a _contained command_ and the _command_ cannot be found + + $ podman run busybox foo; echo $? + Error: fork/exec /usr/bin/bogus: no such file or directory + 127 + + **Exit code** _contained command_ exit code + + $ podman run busybox /bin/sh -c 'exit 3'; echo $? + 3 + ## EXAMPLE ``` diff --git a/test/e2e/unshare_test.go b/test/e2e/unshare_test.go index eacdda68a..79ce68e89 100644 --- a/test/e2e/unshare_test.go +++ b/test/e2e/unshare_test.go @@ -47,8 +47,7 @@ var _ = Describe("Podman unshare", func() { session := podmanTest.Podman([]string{"unshare", "readlink", "/proc/self/ns/user"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - ok, _ := session.GrepString(userNS) - Expect(ok).To(BeFalse()) + Expect(session.OutputToString()).ToNot(ContainSubstring(userNS)) }) It("podman unshare --rootles-cni", func() { @@ -57,4 +56,36 @@ var _ = Describe("Podman unshare", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("tap0")) }) + + It("podman unshare exit codes", func() { + session := podmanTest.Podman([]string{"unshare", "false"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(1)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(Equal("")) + + session = podmanTest.Podman([]string{"unshare", "/usr/bin/bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(127)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("no such file or directory")) + + session = podmanTest.Podman([]string{"unshare", "bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(127)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("executable file not found in $PATH")) + + session = podmanTest.Podman([]string{"unshare", "/usr"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(126)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("permission denied")) + + session = podmanTest.Podman([]string{"unshare", "--bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("unknown flag: --bogus")) + }) }) |