diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_exec.go | 40 | ||||
-rw-r--r-- | libpod/container_inspect.go | 2 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 42 | ||||
-rw-r--r-- | libpod/networking_linux.go | 44 | ||||
-rw-r--r-- | libpod/oci_attach_linux.go | 2 | ||||
-rw-r--r-- | libpod/oci_conmon_exec_linux.go | 1 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 6 | ||||
-rw-r--r-- | libpod/reset.go | 17 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 50 | ||||
-rw-r--r-- | libpod/util.go | 13 |
10 files changed, 153 insertions, 64 deletions
diff --git a/libpod/container_exec.go b/libpod/container_exec.go index 7d4e28d5d..d1c190905 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -14,6 +14,7 @@ import ( "github.com/containers/storage/pkg/stringid" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) // ExecConfig contains the configuration of an exec session @@ -774,13 +775,40 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi return exitCode, nil } -// cleanup an exec session after its done -func (c *Container) cleanupExecBundle(sessionID string) error { - if err := os.RemoveAll(c.execBundlePath(sessionID)); err != nil && !os.IsNotExist(err) { - return err +// cleanupExecBundle cleanups an exec session after its done +// Please be careful when using this function since it might temporarily unlock +// the container when os.RemoveAll($bundlePath) fails with ENOTEMPTY or EBUSY +// errors. +func (c *Container) cleanupExecBundle(sessionID string) (Err error) { + path := c.execBundlePath(sessionID) + for attempts := 0; attempts < 50; attempts++ { + Err = os.RemoveAll(path) + if Err == nil || os.IsNotExist(Err) { + return nil + } + if pathErr, ok := Err.(*os.PathError); ok { + Err = pathErr.Err + if errors.Cause(Err) == unix.ENOTEMPTY || errors.Cause(Err) == unix.EBUSY { + // give other processes a chance to use the container + if !c.batched { + if err := c.save(); err != nil { + return err + } + c.lock.Unlock() + } + time.Sleep(time.Millisecond * 100) + if !c.batched { + c.lock.Lock() + if err := c.syncContainer(); err != nil { + return err + } + } + continue + } + } + return } - - return nil + return } // the path to a containers exec session bundle diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 5389d05a4..1344fc659 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -274,7 +274,7 @@ func (c *Container) GetInspectMounts(namedVolumes []*ContainerNamedVolume, image return inspectMounts, nil } -// GetSecurityOptions retrives and returns the security related annotations and process information upon inspection +// GetSecurityOptions retrieves and returns the security related annotations and process information upon inspection func (c *Container) GetSecurityOptions() []string { ctrSpec := c.config.Spec SecurityOpt := []string{} diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 84293ccb2..86d8586d0 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -391,18 +391,52 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } overlayFlag := false + upperDir := "" + workDir := "" for _, o := range namedVol.Options { if o == "O" { overlayFlag = true } + if overlayFlag && strings.Contains(o, "upperdir") { + splitOpt := strings.SplitN(o, "=", 2) + if len(splitOpt) > 1 { + upperDir = splitOpt[1] + if upperDir == "" { + return nil, errors.New("cannot accept empty value for upperdir") + } + } + } + if overlayFlag && strings.Contains(o, "workdir") { + splitOpt := strings.SplitN(o, "=", 2) + if len(splitOpt) > 1 { + workDir = splitOpt[1] + if workDir == "" { + return nil, errors.New("cannot accept empty value for workdir") + } + } + } } if overlayFlag { + var overlayMount spec.Mount + var overlayOpts *overlay.Options contentDir, err := overlay.TempDir(c.config.StaticDir, c.RootUID(), c.RootGID()) if err != nil { return nil, err } - overlayMount, err := overlay.Mount(contentDir, mountPoint, namedVol.Dest, c.RootUID(), c.RootGID(), c.runtime.store.GraphOptions()) + + if (upperDir != "" && workDir == "") || (upperDir == "" && workDir != "") { + return nil, errors.Wrapf(err, "must specify both upperdir and workdir") + } + + overlayOpts = &overlay.Options{RootUID: c.RootUID(), + RootGID: c.RootGID(), + UpperDirOptionFragment: upperDir, + WorkDirOptionFragment: workDir, + GraphOpts: c.runtime.store.GraphOptions(), + } + + overlayMount, err = overlay.MountWithOptions(contentDir, mountPoint, namedVol.Dest, overlayOpts) if err != nil { return nil, errors.Wrapf(err, "mounting overlay failed %q", mountPoint) } @@ -476,6 +510,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { if c.IsReadOnly() && dstPath != "/dev/shm" { newMount.Options = append(newMount.Options, "ro", "nosuid", "noexec", "nodev") } + if dstPath == "/dev/shm" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir { + newMount.Options = append(newMount.Options, "nosuid", "noexec", "nodev") + } if !MountExists(g.Mounts(), dstPath) { g.AddMount(newMount) } else { @@ -1536,6 +1573,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti if c.IsReadOnly() && dstPath != "/dev/shm" { newMount.Options = append(newMount.Options, "ro", "nosuid", "noexec", "nodev") } + if dstPath == "/dev/shm" && c.state.BindMounts["/dev/shm"] == c.config.ShmDir { + newMount.Options = append(newMount.Options, "nosuid", "noexec", "nodev") + } if !MountExists(g.Mounts(), dstPath) { g.AddMount(newMount) } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 110f37b91..f490ac626 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -834,21 +834,25 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { return nil } -func getContainerNetNS(ctr *Container) (string, error) { +func getContainerNetNS(ctr *Container) (string, *Container, error) { if ctr.state.NetNS != nil { - return ctr.state.NetNS.Path(), nil + return ctr.state.NetNS.Path(), nil, nil } if ctr.config.NetNsCtr != "" { c, err := ctr.runtime.GetContainer(ctr.config.NetNsCtr) if err != nil { - return "", err + return "", nil, err } if err = c.syncContainer(); err != nil { - return "", err + return "", c, err } - return getContainerNetNS(c) + netNs, c2, err := getContainerNetNS(c) + if c2 != nil { + c = c2 + } + return netNs, c, err } - return "", nil + return "", nil, nil } // isBridgeNetMode checks if the given network mode is bridge. @@ -919,12 +923,8 @@ func (r *Runtime) reloadContainerNetwork(ctr *Container) (map[string]types.Statu func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { var netStats *netlink.LinkStatistics - // With slirp4netns, we can't collect statistics at present. - // For now, we allow stats to at least run by returning nil - if rootless.IsRootless() || ctr.config.NetMode.IsSlirp4netns() { - return netStats, nil - } - netNSPath, netPathErr := getContainerNetNS(ctr) + + netNSPath, otherCtr, netPathErr := getContainerNetNS(ctr) if netPathErr != nil { return nil, netPathErr } @@ -933,9 +933,18 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { // this is a valid state and thus return no error, nor any statistics return nil, nil } + + // FIXME get the interface from the container netstatus + dev := "eth0" + netMode := ctr.config.NetMode + if otherCtr != nil { + netMode = otherCtr.config.NetMode + } + if netMode.IsSlirp4netns() { + dev = "tap0" + } err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error { - // FIXME get the interface from the container netstatus - link, err := netlink.LinkByName("eth0") + link, err := netlink.LinkByName(dev) if err != nil { return err } @@ -1198,13 +1207,6 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe // get network status before we connect networkStatus := c.getNetworkStatus() - network, err := c.runtime.network.NetworkInspect(netName) - if err != nil { - return err - } - if !network.DNSEnabled && len(netOpts.Aliases) > 0 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set network aliases for network %q because dns is disabled", netName) - } // always add the short id as alias for docker compat netOpts.Aliases = append(netOpts.Aliases, c.config.ID[:12]) diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go index 1c15d567c..1ee664e81 100644 --- a/libpod/oci_attach_linux.go +++ b/libpod/oci_attach_linux.go @@ -273,9 +273,11 @@ func readStdio(conn *net.UnixConn, streams *define.AttachStreams, receiveStdoutE var err error select { case err = <-receiveStdoutError: + conn.CloseWrite() return err case err = <-stdinDone: if err == define.ErrDetach { + conn.CloseWrite() return err } if err == nil { diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index 29c600109..04deaac83 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -389,6 +389,7 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex if err != nil { return nil, nil, err } + defer processFile.Close() args := r.sharedConmonArgs(c, sessionID, c.execBundlePath(sessionID), c.execPidPath(sessionID), c.execLogPath(sessionID), c.execExitFileDir(sessionID), ociLog, define.NoLogging, "") diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 3fa43aed9..268a301fb 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -660,13 +660,13 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http. } errChan <- err }() + if err := ctr.ReadLog(context.Background(), logOpts, logChan); err != nil { + return err + } go func() { logOpts.WaitGroup.Wait() close(logChan) }() - if err := ctr.ReadLog(context.Background(), logOpts, logChan); err != nil { - return err - } logrus.Debugf("Done reading logs for container %s, %d bytes", ctr.ID(), logSize) if err := <-errChan; err != nil { return err diff --git a/libpod/reset.go b/libpod/reset.go index 2b2b586bc..28d0ee3f6 100644 --- a/libpod/reset.go +++ b/libpod/reset.go @@ -7,6 +7,7 @@ import ( "path/filepath" "github.com/containers/common/libimage" + "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/rootless" @@ -70,6 +71,22 @@ func (r *Runtime) Reset(ctx context.Context) error { } } + // remove all networks + nets, err := r.network.NetworkList() + if err != nil { + return err + } + for _, net := range nets { + // do not delete the default network + if net.Name == r.network.DefaultNetworkName() { + continue + } + // ignore not exists errors because of the TOCTOU problem + if err := r.network.NetworkRemove(net.Name); err != nil && !errors.Is(err, types.ErrNoSuchNetwork) { + logrus.Errorf("Removing network %s: %v", net.Name, err) + } + } + xdgRuntimeDir := filepath.Clean(os.Getenv("XDG_RUNTIME_DIR")) _, prevError := r.store.Shutdown(true) graphRoot := filepath.Clean(r.store.GraphRoot()) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 6ee25c0ec..3799b463f 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -254,15 +254,6 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if err != nil { return nil, err } - if len(opts.Aliases) > 0 { - network, err := r.network.NetworkInspect(netName) - if err != nil { - return nil, err - } - if !network.DNSEnabled { - return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set network aliases for network %q because dns is disabled", netName) - } - } // assign interface name if empty if opts.InterfaceName == "" { for i < 100000 { @@ -653,6 +644,20 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo } } + // Check that no other containers depend on the container. + // Only used if not removing a pod - pods guarantee that all + // deps will be evicted at the same time. + if !removePod { + deps, err := r.state.ContainerInUse(c) + if err != nil { + return err + } + if len(deps) != 0 { + depsStr := strings.Join(deps, ", ") + return errors.Wrapf(define.ErrCtrExists, "container %s has dependent containers which must be removed before it: %s", c.ID(), depsStr) + } + } + // Check that the container's in a good state to be removed. if c.state.State == define.ContainerStateRunning { time := c.StopTimeout() @@ -675,25 +680,6 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo } } - // Remove all active exec sessions - if err := c.removeAllExecSessions(); err != nil { - return err - } - - // Check that no other containers depend on the container. - // Only used if not removing a pod - pods guarantee that all - // deps will be evicted at the same time. - if !removePod { - deps, err := r.state.ContainerInUse(c) - if err != nil { - return err - } - if len(deps) != 0 { - depsStr := strings.Join(deps, ", ") - return errors.Wrapf(define.ErrCtrExists, "container %s has dependent containers which must be removed before it: %s", c.ID(), depsStr) - } - } - var cleanupErr error // Clean up network namespace, cgroups, mounts. @@ -713,6 +699,14 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo return errors.Wrapf(err, "unable to set container %s removing state in database", c.ID()) } + // Remove all active exec sessions + // removing the exec sessions might temporarily unlock the container's lock. Using it + // after setting the state to ContainerStateRemoving will prevent that the container is + // restarted + if err := c.removeAllExecSessions(); err != nil { + return err + } + // Stop the container's storage if err := c.teardownStorage(); err != nil { if cleanupErr == nil { diff --git a/libpod/util.go b/libpod/util.go index 2b96a9449..307caa8c5 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -150,6 +150,10 @@ func queryPackageVersion(cmdArg ...string) string { if outp, err := cmd.Output(); err == nil { output = string(outp) } + if cmdArg[0] == "/sbin/apk" { + prefix := cmdArg[len(cmdArg)-1] + " is owned by " + output = strings.Replace(output, prefix, "", 1) + } } return strings.Trim(output, "\n") } @@ -157,10 +161,11 @@ func queryPackageVersion(cmdArg ...string) string { func packageVersion(program string) string { // program is full path packagers := [][]string{ {"/usr/bin/rpm", "-q", "-f"}, - {"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu - {"/usr/bin/pacman", "-Qo"}, // Arch - {"/usr/bin/qfile", "-qv"}, // Gentoo (quick) - {"/usr/bin/equery", "b"}, // Gentoo (slow) + {"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu + {"/usr/bin/pacman", "-Qo"}, // Arch + {"/usr/bin/qfile", "-qv"}, // Gentoo (quick) + {"/usr/bin/equery", "b"}, // Gentoo (slow) + {"/sbin/apk", "info", "-W"}, // Alpine } for _, cmd := range packagers { |