diff options
-rw-r--r-- | cmd/podman/images/tree.go | 40 | ||||
-rw-r--r-- | cmd/podman/system/service.go | 19 | ||||
-rw-r--r-- | pkg/bindings/images/images.go | 2 | ||||
-rw-r--r-- | pkg/domain/entities/engine_image.go | 1 | ||||
-rw-r--r-- | pkg/domain/entities/images.go | 10 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 12 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/images.go | 4 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 45 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 113 |
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") } |