summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_exec.go40
-rw-r--r--libpod/container_inspect.go2
-rw-r--r--libpod/container_internal_linux.go42
-rw-r--r--libpod/networking_linux.go44
-rw-r--r--libpod/oci_attach_linux.go2
-rw-r--r--libpod/oci_conmon_exec_linux.go1
-rw-r--r--libpod/oci_conmon_linux.go6
-rw-r--r--libpod/reset.go17
-rw-r--r--libpod/runtime_ctr.go50
-rw-r--r--libpod/util.go13
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 {