summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/images/tree.go40
-rw-r--r--cmd/podman/system/service.go19
-rw-r--r--pkg/bindings/images/images.go2
-rw-r--r--pkg/domain/entities/engine_image.go1
-rw-r--r--pkg/domain/entities/images.go10
-rw-r--r--pkg/domain/infra/abi/images.go12
-rw-r--r--pkg/domain/infra/tunnel/images.go4
-rw-r--r--pkg/rootless/rootless_linux.c45
-rw-r--r--pkg/rootless/rootless_linux.go113
9 files changed, 120 insertions, 126 deletions
diff --git a/cmd/podman/images/tree.go b/cmd/podman/images/tree.go
new file mode 100644
index 000000000..5e82e9dea
--- /dev/null
+++ b/cmd/podman/images/tree.go
@@ -0,0 +1,40 @@
+package images
+
+import (
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ treeDescription = "Prints layer hierarchy of an image in a tree format"
+ treeCmd = &cobra.Command{
+ Use: "tree [flags] IMAGE",
+ Args: cobra.ExactArgs(1),
+ Short: treeDescription,
+ Long: treeDescription,
+ RunE: tree,
+ Example: "podman image tree alpine:latest",
+ }
+ treeOpts entities.ImageTreeOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: treeCmd,
+ Parent: imageCmd,
+ })
+ treeCmd.Flags().BoolVar(&treeOpts.WhatRequires, "whatrequires", false, "Show all child images and layers of the specified image")
+}
+
+func tree(_ *cobra.Command, args []string) error {
+ results, err := registry.ImageEngine().Tree(registry.Context(), args[0], treeOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println(results.Tree)
+ return nil
+}
diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go
index 6522a45f8..e09107b53 100644
--- a/cmd/podman/system/service.go
+++ b/cmd/podman/system/service.go
@@ -2,8 +2,10 @@ package system
import (
"fmt"
+ "net/url"
"os"
"path/filepath"
+ "syscall"
"time"
"github.com/containers/libpod/cmd/podman/registry"
@@ -59,6 +61,23 @@ func service(cmd *cobra.Command, args []string) error {
}
logrus.Infof("using API endpoint: '%s'", apiURI)
+ // Clean up any old existing unix domain socket
+ if len(apiURI) > 0 {
+ uri, err := url.Parse(apiURI)
+ if err != nil {
+ return err
+ }
+
+ // socket activation uses a unix:// socket in the shipped unit files but apiURI is coded as "" at this layer.
+ if "unix" == uri.Scheme && !registry.IsRemote() {
+ if err := syscall.Unlink(uri.Path); err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ mask := syscall.Umask(0177)
+ defer syscall.Umask(mask)
+ }
+ }
+
opts := entities.ServiceOptions{
URI: apiURI,
Timeout: time.Duration(srvArgs.Timeout) * time.Second,
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 3550c3968..e0f523ebd 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -74,7 +74,7 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.Image
return &inspectedData, response.Process(&inspectedData)
}
-func ImageTree(ctx context.Context, nameOrId string) error {
+func Tree(ctx context.Context, nameOrId string) error {
return bindings.ErrNotImplemented
}
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 052e7bee5..b6283b6ad 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -23,5 +23,6 @@ type ImageEngine interface {
Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
+ Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error)
Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error
}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 3a6d159e4..56c4c0ac5 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -273,3 +273,13 @@ type ImageSaveOptions struct {
Output string
Quiet bool
}
+
+// ImageTreeOptions provides options for ImageEngine.Tree()
+type ImageTreeOptions struct {
+ WhatRequires bool // Show all child images and layers of the specified image
+}
+
+// ImageTreeReport provides results from ImageEngine.Tree()
+type ImageTreeReport struct {
+ Tree string // TODO: Refactor move presentation work out of server
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 0f710ad28..4353e0798 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -476,3 +476,15 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts
}
return &entities.BuildReport{ID: id}, nil
}
+
+func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
+ img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ results, err := img.GenerateTree(opts.WhatRequires)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ImageTreeReport{Tree: results}, nil
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 6ea2bd9f2..2decd605d 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -263,3 +263,7 @@ func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) {
func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) {
return nil, errors.New("not implemented yet")
}
+
+func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) {
+ return nil, errors.New("not implemented yet")
+}
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index da52a7217..72d461cdc 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -535,8 +535,36 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
}
}
+static void
+join_namespace_or_die (int pid_to_join, const char *ns_file)
+{
+ char ns_path[PATH_MAX];
+ int ret;
+ int fd;
+
+ ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file);
+ if (ret == PATH_MAX)
+ {
+ fprintf (stderr, "internal error: namespace path too long\n");
+ _exit (EXIT_FAILURE);
+ }
+
+ fd = open (ns_path, O_CLOEXEC | O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf (stderr, "cannot open: %s\n", ns_path);
+ _exit (EXIT_FAILURE);
+ }
+ if (setns (fd, 0) < 0)
+ {
+ fprintf (stderr, "cannot set namespace to %s: %s\n", ns_path, strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+ close (fd);
+}
+
int
-reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
+reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
{
char uid[16];
char gid[16];
@@ -606,19 +634,8 @@ reexec_userns_join (int userns, int mountns, char *pause_pid_file_path)
_exit (EXIT_FAILURE);
}
- if (setns (userns, 0) < 0)
- {
- fprintf (stderr, "cannot setns: %s\n", strerror (errno));
- _exit (EXIT_FAILURE);
- }
- close (userns);
-
- if (mountns >= 0 && setns (mountns, 0) < 0)
- {
- fprintf (stderr, "cannot setns: %s\n", strerror (errno));
- _exit (EXIT_FAILURE);
- }
- close (mountns);
+ join_namespace_or_die (pid_to_join, "user");
+ join_namespace_or_die (pid_to_join, "mnt");
if (syscall_setresgid (0, 0, 0) < 0)
{
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 5ddfab7ad..3de136f12 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -31,7 +31,7 @@ extern uid_t rootless_uid();
extern uid_t rootless_gid();
extern int reexec_in_user_namespace(int ready, char *pause_pid_file_path, char *file_to_read, int fd);
extern int reexec_in_user_namespace_wait(int pid, int options);
-extern int reexec_userns_join(int userns, int mountns, char *pause_pid_file_path);
+extern int reexec_userns_join(int pid, char *pause_pid_file_path);
*/
import "C"
@@ -124,91 +124,6 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap)
return nil
}
-func readUserNs(path string) (string, error) {
- b := make([]byte, 256)
- _, err := unix.Readlink(path, b)
- if err != nil {
- return "", err
- }
- return string(b), nil
-}
-
-func readUserNsFd(fd uintptr) (string, error) {
- return readUserNs(fmt.Sprintf("/proc/self/fd/%d", fd))
-}
-
-func getParentUserNs(fd uintptr) (uintptr, error) {
- const nsGetParent = 0xb702
- ret, _, errno := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(nsGetParent), 0)
- if errno != 0 {
- return 0, errno
- }
- return (uintptr)(unsafe.Pointer(ret)), nil
-}
-
-// getUserNSFirstChild returns an open FD for the first direct child user namespace that created the process
-// Each container creates a new user namespace where the runtime runs. The current process in the container
-// might have created new user namespaces that are child of the initial namespace we created.
-// This function finds the initial namespace created for the container that is a child of the current namespace.
-//
-// current ns
-// / \
-// TARGET -> a [other containers]
-// /
-// b
-// /
-// NS READ USING THE PID -> c
-func getUserNSFirstChild(fd uintptr) (*os.File, error) {
- currentNS, err := readUserNs("/proc/self/ns/user")
- if err != nil {
- return nil, err
- }
-
- ns, err := readUserNsFd(fd)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot read user namespace")
- }
- if ns == currentNS {
- return nil, errors.New("process running in the same user namespace")
- }
-
- for {
- nextFd, err := getParentUserNs(fd)
- if err != nil {
- if err == unix.ENOTTY {
- return os.NewFile(fd, "userns child"), nil
- }
- return nil, errors.Wrapf(err, "cannot get parent user namespace")
- }
-
- ns, err = readUserNsFd(nextFd)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot read user namespace")
- }
-
- if ns == currentNS {
- if err := unix.Close(int(nextFd)); err != nil {
- return nil, err
- }
-
- // Drop O_CLOEXEC for the fd.
- _, _, errno := unix.Syscall(unix.SYS_FCNTL, fd, unix.F_SETFD, 0)
- if errno != 0 {
- if err := unix.Close(int(fd)); err != nil {
- logrus.Errorf("failed to close file descriptor %d", fd)
- }
- return nil, errno
- }
-
- return os.NewFile(fd, "userns child"), nil
- }
- if err := unix.Close(int(fd)); err != nil {
- return nil, err
- }
- fd = nextFd
- }
-}
-
// joinUserAndMountNS re-exec podman in a new userNS and join the user and mount
// namespace of the specified PID without looking up its parent. Useful to join directly
// the conmon process.
@@ -220,31 +135,7 @@ func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) {
cPausePid := C.CString(pausePid)
defer C.free(unsafe.Pointer(cPausePid))
- userNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/user", pid))
- if err != nil {
- return false, -1, err
- }
- defer func() {
- if err := userNS.Close(); err != nil {
- logrus.Errorf("unable to close namespace: %q", err)
- }
- }()
-
- mountNS, err := os.Open(fmt.Sprintf("/proc/%d/ns/mnt", pid))
- if err != nil {
- return false, -1, err
- }
- defer func() {
- if err := mountNS.Close(); err != nil {
- logrus.Errorf("unable to close namespace: %q", err)
- }
- }()
-
- fd, err := getUserNSFirstChild(userNS.Fd())
- if err != nil {
- return false, -1, err
- }
- pidC := C.reexec_userns_join(C.int(fd.Fd()), C.int(mountNS.Fd()), cPausePid)
+ pidC := C.reexec_userns_join(C.int(pid), cPausePid)
if int(pidC) < 0 {
return false, -1, errors.Errorf("cannot re-exec process")
}