summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/adapter/containers.go28
-rw-r--r--pkg/adapter/containers_remote.go9
-rw-r--r--pkg/adapter/network.go14
-rw-r--r--pkg/adapter/runtime.go7
-rw-r--r--pkg/adapter/runtime_remote.go37
-rw-r--r--pkg/adapter/terminal_linux.go2
-rw-r--r--pkg/hooks/docs/oci-hooks.5.md8
-rw-r--r--pkg/network/config.go14
-rw-r--r--pkg/network/devices.go17
-rw-r--r--pkg/network/files.go26
-rw-r--r--pkg/network/netconflist.go21
-rw-r--r--pkg/rootless/rootless_linux.go126
-rw-r--r--pkg/rootless/rootless_unsupported.go11
-rw-r--r--pkg/spec/createconfig.go17
-rw-r--r--pkg/spec/spec.go24
-rw-r--r--pkg/spec/storage.go22
-rw-r--r--pkg/util/utils.go2
-rw-r--r--pkg/varlinkapi/attach.go1
-rw-r--r--pkg/varlinkapi/containers.go22
-rw-r--r--pkg/varlinkapi/virtwriter/virtwriter.go24
-rw-r--r--pkg/varlinkapi/volumes.go25
21 files changed, 279 insertions, 178 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 51efdccc7..ae91bd812 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -269,7 +269,7 @@ func (r *LocalRuntime) UmountRootFilesystems(ctx context.Context, cli *cliconfig
logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID())
continue
}
- failures[ctr.ID()] = errors.Wrapf(err, "error unmounting continaner %s", ctr.ID())
+ failures[ctr.ID()] = errors.Wrapf(err, "error unmounting container %s", ctr.ID())
} else {
ok = append(ok, ctr.ID())
}
@@ -437,8 +437,12 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
}
if c.IsSet("rm") {
- if err := r.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
- logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
+ if err := r.Runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err)
+ } else {
+ logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
+ }
}
}
@@ -612,7 +616,9 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP
if c.Attach {
inputStream := os.Stdin
if !c.Interactive {
- inputStream = nil
+ if !ctr.Stdin() {
+ inputStream = nil
+ }
}
// attach to the container and also start it not already running
@@ -663,7 +669,7 @@ func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigP
lastError = errors.Wrapf(err, "unable to start container %q", container)
continue
}
- fmt.Println(container)
+ fmt.Println(ctr.ID())
}
return exitCode, lastError
}
@@ -899,7 +905,7 @@ func (r *LocalRuntime) execPS(c *libpod.Container, args []string) ([]string, err
}()
cmd := append([]string{"ps"}, args...)
- ec, err := c.Exec(false, false, []string{}, cmd, "", "", streams, 0, nil, "")
+ ec, err := c.Exec(false, false, map[string]string{}, cmd, "", "", streams, 0, nil, "")
if err != nil {
return nil, err
} else if ec != 0 {
@@ -959,12 +965,6 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
return ec, errors.Wrapf(err, "unable to process environment variables")
}
- // Build env slice of key=value strings for Exec
- envs := []string{}
- for k, v := range env {
- envs = append(envs, fmt.Sprintf("%s=%s", k, v))
- }
-
streams := new(libpod.AttachStreams)
streams.OutputStream = os.Stdout
streams.ErrorStream = os.Stderr
@@ -975,7 +975,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal
streams.AttachOutput = true
streams.AttachError = true
- ec, err = ExecAttachCtr(ctx, ctr.Container, cli.Tty, cli.Privileged, envs, cmd, cli.User, cli.Workdir, streams, cli.PreserveFDs, cli.DetachKeys)
+ ec, err = ExecAttachCtr(ctx, ctr.Container, cli.Tty, cli.Privileged, env, cmd, cli.User, cli.Workdir, streams, uint(cli.PreserveFDs), cli.DetachKeys)
return define.TranslateExecErrorToExitCode(ec, err), err
}
@@ -1057,7 +1057,7 @@ func (r *LocalRuntime) CleanupContainers(ctx context.Context, cli *cliconfig.Cle
// Only used when cleaning up containers
func removeContainer(ctx context.Context, ctr *libpod.Container, runtime *LocalRuntime) error {
- if err := runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
+ if err := runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
return errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID())
}
return nil
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index f7cb28b0c..f4e83a975 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -1092,6 +1092,7 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
// These are the special writers that encode input from the client.
varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin)
varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize)
+ varlinkHangupWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.HangUpFromClient)
go func() {
// Read from the wire and direct to stdout or stderr
@@ -1117,7 +1118,6 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
}
}
}()
-
if stdin != nil {
// Takes stdinput and sends it over the wire after being encoded
go func() {
@@ -1126,7 +1126,12 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
sendGenericError(ecChan)
errChan <- err
}
-
+ _, err := varlinkHangupWriter.Write([]byte("EOF"))
+ if err != nil {
+ logrus.Errorf("unable to notify server to hangup: %q", err)
+ }
+ err = varlinkStdinWriter.Close()
+ errChan <- err
}()
}
return errChan
diff --git a/pkg/adapter/network.go b/pkg/adapter/network.go
index d407984ce..9659ae339 100644
--- a/pkg/adapter/network.go
+++ b/pkg/adapter/network.go
@@ -155,15 +155,14 @@ func (r *LocalRuntime) removeNetwork(ctx context.Context, name string, container
// NetworkCreate creates a CNI network
func (r *LocalRuntime) NetworkCreate(cli *cliconfig.NetworkCreateValues) (string, error) {
- var (
- err error
- )
-
isGateway := true
ipMasq := true
subnet := &cli.Network
ipRange := cli.IPRange
-
+ runtimeConfig, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
// if range is provided, make sure it is "in" network
if cli.IsSet("subnet") {
// if network is provided, does it conflict with existing CNI or live networks
@@ -245,6 +244,11 @@ func (r *LocalRuntime) NetworkCreate(cli *cliconfig.NetworkCreateValues) (string
plugins = append(plugins, bridge)
plugins = append(plugins, network.NewPortMapPlugin())
plugins = append(plugins, network.NewFirewallPlugin())
+ // if we find the dnsname plugin, we add configuration for it
+ if network.HasDNSNamePlugin(runtimeConfig.CNIPluginDir) && !cli.DisableDNS {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
+ }
ncList["plugins"] = plugins
b, err := json.MarshalIndent(ncList, "", " ")
if err != nil {
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 0706d4b6a..84d43c337 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -186,7 +186,12 @@ func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCrea
}
if len(opts) != 0 {
- options = append(options, libpod.WithVolumeOptions(opts))
+ // We need to process -o for uid, gid
+ parsedOptions, err := shared.ParseVolumeOptions(opts)
+ if err != nil {
+ return "", err
+ }
+ options = append(options, parsedOptions...)
}
newVolume, err := r.NewVolume(ctx, options...)
if err != nil {
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 3b808a2ee..870e86896 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -659,12 +659,39 @@ func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestM
}
// InspectVolumes returns a slice of volumes based on an arg list or --all
-func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) {
- reply, err := iopodman.GetVolumes().Call(r.Conn, c.InputArgs, c.All)
- if err != nil {
- return nil, err
+func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*libpod.InspectVolumeData, error) {
+ var (
+ inspectData []*libpod.InspectVolumeData
+ volumes []string
+ )
+
+ if c.All {
+ allVolumes, err := r.Volumes(ctx)
+ if err != nil {
+ return nil, err
+ }
+ for _, vol := range allVolumes {
+ volumes = append(volumes, vol.Name())
+ }
+ } else {
+ for _, arg := range c.InputArgs {
+ volumes = append(volumes, arg)
+ }
}
- return varlinkVolumeToVolume(r, reply), nil
+
+ for _, vol := range volumes {
+ jsonString, err := iopodman.InspectVolume().Call(r.Conn, vol)
+ if err != nil {
+ return nil, err
+ }
+ inspectJSON := new(libpod.InspectVolumeData)
+ if err := json.Unmarshal([]byte(jsonString), inspectJSON); err != nil {
+ return nil, errors.Wrapf(err, "error unmarshalling inspect JSON for volume %s", vol)
+ }
+ inspectData = append(inspectData, inspectJSON)
+ }
+
+ return inspectData, nil
}
// Volumes returns a slice of adapter.volumes based on information about libpod
diff --git a/pkg/adapter/terminal_linux.go b/pkg/adapter/terminal_linux.go
index 26cfd7b5e..16e552802 100644
--- a/pkg/adapter/terminal_linux.go
+++ b/pkg/adapter/terminal_linux.go
@@ -13,7 +13,7 @@ import (
)
// ExecAttachCtr execs and attaches to a container
-func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs int, detachKeys string) (int, error) {
+func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *libpod.AttachStreams, preserveFDs uint, detachKeys string) (int, error) {
resize := make(chan remotecommand.TerminalSize)
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
diff --git a/pkg/hooks/docs/oci-hooks.5.md b/pkg/hooks/docs/oci-hooks.5.md
index fc0442283..0a01e1bb8 100644
--- a/pkg/hooks/docs/oci-hooks.5.md
+++ b/pkg/hooks/docs/oci-hooks.5.md
@@ -88,9 +88,9 @@ $ cat /etc/containers/oci/hooks.d/oci-systemd-hook.json
"version": "1.0.0",
"hook": {
"path": "/usr/libexec/oci/hooks.d/oci-systemd-hook"
- }
+ },
"when": {
- "commands": [".*/init$" , ".*/systemd$"],
+ "commands": [".*/init$" , ".*/systemd$"]
},
"stages": ["prestart", "poststop"]
}
@@ -105,9 +105,9 @@ $ cat /etc/containers/oci/hooks.d/oci-umount.json
"hook": {
"path": "/usr/libexec/oci/hooks.d/oci-umount",
"args": ["oci-umount", "--debug"],
- }
+ },
"when": {
- "hasBindMounts": true,
+ "hasBindMounts": true
},
"stages": ["prestart"]
}
diff --git a/pkg/network/config.go b/pkg/network/config.go
index 7eaa83833..37eb0dd64 100644
--- a/pkg/network/config.go
+++ b/pkg/network/config.go
@@ -14,6 +14,9 @@ const (
// CNIDeviceName is the default network device name and in
// reality should have an int appended to it (cni-podman4)
CNIDeviceName = "cni-podman"
+ // DefaultPodmanDomainName is used for the dnsname plugin to define
+ // a localized domain name for a created network
+ DefaultPodmanDomainName = "dns.podman"
)
// GetDefaultPodmanNetwork outputs the default network for podman
@@ -97,3 +100,14 @@ type FirewallConfig struct {
func (f FirewallConfig) Bytes() ([]byte, error) {
return json.MarshalIndent(f, "", "\t")
}
+
+// DNSNameConfig describes the dns container name resolution plugin config
+type DNSNameConfig struct {
+ PluginType string `json:"type"`
+ DomainName string `json:"domainName"`
+}
+
+// Bytes outputs the configuration as []byte
+func (d DNSNameConfig) Bytes() ([]byte, error) {
+ return json.MarshalIndent(d, "", "\t")
+}
diff --git a/pkg/network/devices.go b/pkg/network/devices.go
index 85068a7d1..78e1a5aa5 100644
--- a/pkg/network/devices.go
+++ b/pkg/network/devices.go
@@ -24,19 +24,26 @@ func GetFreeDeviceName() (string, error) {
if err != nil {
return "", err
}
+ bridgeNames, err := GetBridgeNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
for {
deviceName = fmt.Sprintf("%s%d", CNIDeviceName, deviceNum)
- logrus.Debugf("checking if device name %s exists in other cni networks", deviceName)
+ logrus.Debugf("checking if device name %q exists in other cni networks", deviceName)
if util.StringInSlice(deviceName, networkNames) {
deviceNum++
continue
}
- logrus.Debugf("checking if device name %s exists in live networks", deviceName)
- if !util.StringInSlice(deviceName, liveNetworksNames) {
+ logrus.Debugf("checking if device name %q exists in live networks", deviceName)
+ if util.StringInSlice(deviceName, liveNetworksNames) {
+ deviceNum++
+ continue
+ }
+ logrus.Debugf("checking if device name %q already exists as a bridge name ", deviceName)
+ if !util.StringInSlice(deviceName, bridgeNames) {
break
}
- // TODO Still need to check the bridge names for a conflict but I dont know
- // how to get them yet!
deviceNum++
}
return deviceName, nil
diff --git a/pkg/network/files.go b/pkg/network/files.go
index d55ec2dfd..2f3932974 100644
--- a/pkg/network/files.go
+++ b/pkg/network/files.go
@@ -129,3 +129,29 @@ func GetInterfaceNameFromConfig(path string) (string, error) {
}
return name, nil
}
+
+// GetBridgeNamesFromFileSystem is a convenience function to get all the bridge
+// names from the configured networks
+func GetBridgeNamesFromFileSystem() ([]string, error) {
+ var bridgeNames []string
+ networks, err := LoadCNIConfsFromDir(CNIConfigDir)
+ if err != nil {
+ return nil, err
+ }
+ for _, n := range networks {
+ var name string
+ // iterate network conflists
+ for _, cniplugin := range n.Plugins {
+ // iterate plugins
+ if cniplugin.Network.Type == "bridge" {
+ plugin := make(map[string]interface{})
+ if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
+ continue
+ }
+ name = plugin["bridge"].(string)
+ }
+ }
+ bridgeNames = append(bridgeNames, name)
+ }
+ return bridgeNames, nil
+}
diff --git a/pkg/network/netconflist.go b/pkg/network/netconflist.go
index c3b11b409..e19051b88 100644
--- a/pkg/network/netconflist.go
+++ b/pkg/network/netconflist.go
@@ -2,6 +2,8 @@ package network
import (
"net"
+ "os"
+ "path/filepath"
)
// NcList describes a generic map
@@ -111,3 +113,22 @@ func NewFirewallPlugin() FirewallConfig {
Backend: "iptables",
}
}
+
+// NewDNSNamePlugin creates the dnsname config with a given
+// domainname
+func NewDNSNamePlugin(domainName string) DNSNameConfig {
+ return DNSNameConfig{
+ PluginType: "dnsname",
+ DomainName: domainName,
+ }
+}
+
+// HasDNSNamePlugin looks to see if the dnsname cni plugin is present
+func HasDNSNamePlugin(paths []string) bool {
+ for _, p := range paths {
+ if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil {
+ return true
+ }
+ }
+ return false
+}
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 05d641383..94c42f7d0 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -11,20 +11,16 @@ import (
"os/exec"
gosignal "os/signal"
"os/user"
- "path/filepath"
"runtime"
"strconv"
- "strings"
"sync"
- "syscall"
"unsafe"
"github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/storage/pkg/idtools"
- "github.com/docker/docker/pkg/signal"
- "github.com/godbus/dbus"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
)
/*
@@ -130,7 +126,7 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap)
func readUserNs(path string) (string, error) {
b := make([]byte, 256)
- _, err := syscall.Readlink(path, b)
+ _, err := unix.Readlink(path, b)
if err != nil {
return "", err
}
@@ -143,7 +139,7 @@ func readUserNsFd(fd uintptr) (string, error) {
func getParentUserNs(fd uintptr) (uintptr, error) {
const nsGetParent = 0xb702
- ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(nsGetParent), 0)
+ ret, _, errno := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(nsGetParent), 0)
if errno != 0 {
return 0, errno
}
@@ -179,7 +175,7 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) {
for {
nextFd, err := getParentUserNs(fd)
if err != nil {
- if err == syscall.ENOTTY {
+ if err == unix.ENOTTY {
return os.NewFile(fd, "userns child"), nil
}
return nil, errors.Wrapf(err, "cannot get parent user namespace")
@@ -191,14 +187,14 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) {
}
if ns == currentNS {
- if err := syscall.Close(int(nextFd)); err != nil {
+ if err := unix.Close(int(nextFd)); err != nil {
return nil, err
}
// Drop O_CLOEXEC for the fd.
- _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_SETFD, 0)
+ _, _, errno := unix.Syscall(unix.SYS_FCNTL, fd, unix.F_SETFD, 0)
if errno != 0 {
- if err := syscall.Close(int(fd)); err != nil {
+ if err := unix.Close(int(fd)); err != nil {
logrus.Errorf("failed to close file descriptor %d", fd)
}
return nil, errno
@@ -206,99 +202,13 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) {
return os.NewFile(fd, "userns child"), nil
}
- if err := syscall.Close(int(fd)); err != nil {
+ if err := unix.Close(int(fd)); err != nil {
return nil, err
}
fd = nextFd
}
}
-// EnableLinger configures the system to not kill the user processes once the session
-// terminates
-func EnableLinger() (string, error) {
- uid := fmt.Sprintf("%d", GetRootlessUID())
-
- conn, err := dbus.SystemBus()
- if err == nil {
- defer func() {
- if err := conn.Close(); err != nil {
- logrus.Errorf("unable to close dbus connection: %q", err)
- }
- }()
- }
-
- lingerEnabled := false
-
- // If we have a D-BUS connection, attempt to read the LINGER property from it.
- if conn != nil {
- path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid))
- ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.Linger")
- if err == nil && ret.Value().(bool) {
- lingerEnabled = true
- }
- }
-
- xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
- lingerFile := ""
- if xdgRuntimeDir != "" && !lingerEnabled {
- lingerFile = filepath.Join(xdgRuntimeDir, "libpod/linger")
- _, err := os.Stat(lingerFile)
- if err == nil {
- lingerEnabled = true
- }
- }
-
- if !lingerEnabled {
- // First attempt with D-BUS, if it fails, then attempt with "loginctl enable-linger"
- if conn != nil {
- o := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1")
- ret := o.Call("org.freedesktop.login1.Manager.SetUserLinger", 0, uint32(GetRootlessUID()), true, true)
- if ret.Err == nil {
- lingerEnabled = true
- }
- }
- if !lingerEnabled {
- err := exec.Command("loginctl", "enable-linger", uid).Run()
- if err == nil {
- lingerEnabled = true
- } else {
- logrus.Debugf("cannot run `loginctl enable-linger` for the current user: %v", err)
- }
- }
- if lingerEnabled && lingerFile != "" {
- f, err := os.Create(lingerFile)
- if err == nil {
- if err := f.Close(); err != nil {
- logrus.Errorf("failed to close %s", f.Name())
- }
- } else {
- logrus.Debugf("could not create linger file: %v", err)
- }
- }
- }
-
- if !lingerEnabled {
- return "", nil
- }
-
- // If we have a D-BUS connection, attempt to read the RUNTIME PATH from it.
- if conn != nil {
- path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid))
- ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.RuntimePath")
- if err == nil {
- return strings.Trim(ret.String(), "\"\n"), nil
- }
- }
-
- // If XDG_RUNTIME_DIR is not set and the D-BUS call didn't work, try to get the runtime path with "loginctl"
- output, err := exec.Command("loginctl", "-pRuntimePath", "show-user", uid).Output()
- if err != nil {
- logrus.Debugf("could not get RuntimePath using loginctl: %v", err)
- return "", nil
- }
- return strings.Trim(strings.Replace(string(output), "RuntimePath=", "", -1), "\"\n"), nil
-}
-
// 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.
@@ -394,7 +304,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
+ fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
if err != nil {
return false, -1, err
}
@@ -431,12 +341,14 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
if err != nil {
return false, -1, errors.Wrapf(err, "cannot write setgroups file")
}
+ logrus.Debugf("write setgroups file exited with 0")
uidMap := fmt.Sprintf("/proc/%d/uid_map", pid)
err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Geteuid())), 0666)
if err != nil {
return false, -1, errors.Wrapf(err, "cannot write uid_map")
}
+ logrus.Debugf("write uid_map exited with 0")
}
gidsMapped := false
@@ -489,21 +401,21 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
signals := []os.Signal{}
for sig := 0; sig < numSig; sig++ {
- if sig == int(syscall.SIGTSTP) {
+ if sig == int(unix.SIGTSTP) {
continue
}
- signals = append(signals, syscall.Signal(sig))
+ signals = append(signals, unix.Signal(sig))
}
gosignal.Notify(c, signals...)
defer gosignal.Reset()
go func() {
for s := range c {
- if s == signal.SIGCHLD || s == signal.SIGPIPE {
+ if s == unix.SIGCHLD || s == unix.SIGPIPE {
continue
}
- if err := syscall.Kill(int(pidC), s.(syscall.Signal)); err != nil {
+ if err := unix.Kill(int(pidC), s.(unix.Signal)); err != nil {
logrus.Errorf("failed to kill %d", int(pidC))
}
}
@@ -558,7 +470,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st
lastErr = nil
break
} else {
- fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
+ fds, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
if err != nil {
lastErr = err
continue
@@ -602,7 +514,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st
return joinUserAndMountNS(uint(pausePid), pausePidPath)
}
-func readMappingsProc(path string) ([]idtools.IDMap, error) {
+func ReadMappingsProc(path string) ([]idtools.IDMap, error) {
file, err := os.Open(path)
if err != nil {
return nil, errors.Wrapf(err, "cannot open %s", path)
@@ -668,7 +580,7 @@ func ConfigurationMatches() (bool, error) {
return false, err
}
- currentUIDs, err := readMappingsProc("/proc/self/uid_map")
+ currentUIDs, err := ReadMappingsProc("/proc/self/uid_map")
if err != nil {
return false, err
}
@@ -677,7 +589,7 @@ func ConfigurationMatches() (bool, error) {
return false, err
}
- currentGIDs, err := readMappingsProc("/proc/self/gid_map")
+ currentGIDs, err := ReadMappingsProc("/proc/self/gid_map")
if err != nil {
return false, err
}
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index ddd9182b0..1499b737f 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -37,12 +37,6 @@ func GetRootlessGID() int {
return -1
}
-// EnableLinger configures the system to not kill the user processes once the session
-// terminates
-func EnableLinger() (string, error) {
- return "", nil
-}
-
// TryJoinFromFilePaths attempts to join the namespaces of the pid files in paths.
// This is useful when there are already running containers and we
// don't have a pause process yet. We can use the paths to the conmon
@@ -65,3 +59,8 @@ func ConfigurationMatches() (bool, error) {
func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) {
return nil, nil, errors.New("this function is not supported on this os")
}
+
+// ReadMappingsProc returns the uid_map and gid_map
+func ReadMappingsProc(path string) ([]idtools.IDMap, error) {
+ return nil, nil
+}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index a65263b7d..2addfda4b 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -104,7 +104,8 @@ type CreateConfig struct {
NetworkAlias []string //network-alias
PidMode namespaces.PidMode //pid
Pod string //pod
- CgroupMode namespaces.CgroupMode //cgroup
+ PodmanPath string
+ CgroupMode namespaces.CgroupMode //cgroup
PortBindings nat.PortMap
Privileged bool //privileged
Publish []string //publish
@@ -153,7 +154,16 @@ func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, err
return nil, err
}
- cmd, _ := os.Executable()
+ // We need a cleanup process for containers in the current model.
+ // But we can't assume that the caller is Podman - it could be another
+ // user of the API.
+ // As such, provide a way to specify a path to Podman, so we can
+ // still invoke a cleanup process.
+ cmd := c.PodmanPath
+ if cmd == "" {
+ cmd, _ = os.Executable()
+ }
+
command := []string{cmd,
"--root", config.StorageConfig.GraphRoot,
"--runroot", config.StorageConfig.RunRoot,
@@ -195,8 +205,7 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
if c.Interactive {
options = append(options, libpod.WithStdin())
}
- if c.Systemd && (strings.HasSuffix(c.Command[0], "init") ||
- strings.HasSuffix(c.Command[0], "systemd")) {
+ if c.Systemd {
options = append(options, libpod.WithSystemd())
}
if c.Name != "" {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 57c6e8da7..da5c14948 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -300,10 +300,19 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
blockAccessToKernelFilesystems(config, &g)
+ var runtimeConfig *libpod.RuntimeConfig
+
+ if runtime != nil {
+ runtimeConfig, err = runtime.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ }
+
// RESOURCES - PIDS
if config.Resources.PidsLimit > 0 {
- // if running on rootless on a cgroupv1 machine, pids limit is
- // not supported. If the value is still the default
+ // if running on rootless on a cgroupv1 machine or using the cgroupfs manager, pids
+ // limit is not supported. If the value is still the default
// then ignore the settings. If the caller asked for a
// non-default, then try to use it.
setPidLimit := true
@@ -312,7 +321,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
if err != nil {
return nil, err
}
- if !cgroup2 && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
+ if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.CgroupManager != libpod.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
setPidLimit = false
}
}
@@ -407,10 +416,13 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
if !addedResources {
configSpec.Linux.Resources = &spec.LinuxResources{}
}
- if addedResources && !cgroup2 {
- return nil, errors.New("invalid configuration, cannot set resources with rootless containers not using cgroups v2 unified mode")
+
+ canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.CgroupManager == libpod.SystemdCgroupsManager)
+
+ if addedResources && !canUseResources {
+ return nil, errors.New("invalid configuration, cannot specify resource limits without cgroups v2 and --cgroup-manager=systemd")
}
- if !cgroup2 {
+ if !canUseResources {
// Force the resources block to be empty instead of having default values.
configSpec.Linux.Resources = &spec.LinuxResources{}
}
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index 93919dd0a..095534589 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/util"
pmount "github.com/containers/storage/pkg/mount"
- "github.com/containers/storage/pkg/stringid"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -648,7 +647,7 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*libpod.ContainerNamedVolume)
- volumeFormatErr := errors.Errorf("incorrect volume format, should be host-dir:ctr-dir[:option]")
+ volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
for _, vol := range config.Volumes {
var (
@@ -665,7 +664,11 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
src = splitVol[0]
if len(splitVol) == 1 {
- dest = src
+ // This is an anonymous named volume. Only thing given
+ // is destination.
+ // Name/source will be blank, and populated by libpod.
+ src = ""
+ dest = splitVol[0]
} else if len(splitVol) > 1 {
dest = splitVol[1]
}
@@ -675,8 +678,11 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
}
}
- if err := parse.ValidateVolumeHostDir(src); err != nil {
- return nil, nil, err
+ // Do not check source dir for anonymous volumes
+ if len(splitVol) > 1 {
+ if err := parse.ValidateVolumeHostDir(src); err != nil {
+ return nil, nil, err
+ }
}
if err := parse.ValidateVolumeCtrDir(dest); err != nil {
return nil, nil, err
@@ -732,13 +738,13 @@ func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string
Destination: cleanDest,
Source: TypeTmpfs,
Type: TypeTmpfs,
- Options: []string{"rprivate", "rw", "nodev"},
+ Options: []string{"rprivate", "rw", "nodev", "exec"},
}
mounts[vol] = mount
} else {
+ // Anonymous volumes have no name.
namedVolume := new(libpod.ContainerNamedVolume)
- namedVolume.Name = stringid.GenerateNonCryptoID()
- namedVolume.Options = []string{"rprivate", "rw", "nodev"}
+ namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
namedVolume.Dest = cleanDest
volumes[vol] = namedVolume
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 0190b106d..d9a84e4e5 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -318,7 +318,7 @@ func WriteStorageConfigFile(storageOpts *storage.StoreOptions, storageConf strin
if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
return err
}
- storageFile, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ storageFile, err := os.OpenFile(storageConf, os.O_RDWR|os.O_TRUNC, 0600)
if err != nil {
return errors.Wrapf(err, "cannot open %s", storageConf)
}
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index f8557ae0c..37adbbf55 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -70,7 +70,6 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
}
reader, writer, _, pw, streams := setupStreams(call)
-
go func() {
if err := virtwriter.Reader(reader, nil, nil, pw, resize, nil); err != nil {
errChan <- err
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 79fcef11a..b471ee2cf 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -9,6 +9,7 @@ import (
"io"
"io/ioutil"
"os"
+ "strings"
"sync"
"syscall"
"time"
@@ -563,9 +564,14 @@ func (i *LibpodAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) err
}
}
+ sockPath, err := ctr.AttachSocketPath()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+
s := iopodman.Sockets{
Container_id: ctr.ID(),
- Io_socket: ctr.AttachSocketPath(),
+ Io_socket: sockPath,
Control_socket: ctr.ControlSocketPath(),
}
return call.ReplyGetAttachSockets(s)
@@ -811,9 +817,19 @@ func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecO
// ACK the client upgrade request
call.ReplyExecContainer()
- envs := []string{}
+ envs := make(map[string]string)
if opts.Env != nil {
- envs = *opts.Env
+ // HACK: The Varlink API uses the old []string format for env,
+ // storage as "k=v". Split on the = and turn into the new map
+ // format.
+ for _, env := range *opts.Env {
+ splitEnv := strings.SplitN(env, "=", 2)
+ if len(splitEnv) == 1 {
+ logrus.Errorf("Got badly-formatted environment variable %q in exec", env)
+ continue
+ }
+ envs[splitEnv[0]] = splitEnv[1]
+ }
}
var user string
diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go
index 27ecd1f52..dd171943f 100644
--- a/pkg/varlinkapi/virtwriter/virtwriter.go
+++ b/pkg/varlinkapi/virtwriter/virtwriter.go
@@ -5,6 +5,7 @@ import (
"encoding/binary"
"encoding/json"
"io"
+ "time"
"github.com/pkg/errors"
"k8s.io/client-go/tools/remotecommand"
@@ -26,8 +27,14 @@ const (
TerminalResize SocketDest = iota
// Quit and detach
Quit SocketDest = iota
+ // Quit from the client
+ HangUpFromClient SocketDest = iota
)
+// ClientHangup signifies that the client wants to drop its
+// connection from the server
+var ClientHangup = errors.New("client hangup")
+
// IntToSocketDest returns a socketdest based on integer input
func IntToSocketDest(i int) SocketDest {
switch i {
@@ -41,6 +48,8 @@ func IntToSocketDest(i int) SocketDest {
return TerminalResize
case Quit.Int():
return Quit
+ case HangUpFromClient.Int():
+ return HangUpFromClient
default:
return ToStderr
}
@@ -65,7 +74,7 @@ func NewVirtWriteCloser(w *bufio.Writer, dest SocketDest) VirtWriteCloser {
// Close is a required method for a writecloser
func (v VirtWriteCloser) Close() error {
- return nil
+ return v.writer.Flush()
}
// Write prepends a header to the input message. The header is
@@ -96,7 +105,6 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
if r == nil {
return errors.Errorf("Reader must not be nil")
}
-
for {
n, err := io.ReadFull(r, headerBytes)
if err != nil {
@@ -107,7 +115,6 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
}
messageSize = int64(binary.BigEndian.Uint32(headerBytes[4:8]))
-
switch IntToSocketDest(int(headerBytes[0])) {
case ToStdout:
if output != nil {
@@ -161,7 +168,16 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
execEcChan <- int(ecInt)
}
return nil
-
+ case HangUpFromClient:
+ // This sleep allows the pipes to flush themselves before tearing everything down.
+ // It makes me sick to do it but after a full day I cannot put my finger on the race
+ // that occurs when closing things up. It would require a significant rewrite of code
+ // to make the pipes close down properly. Given that we are currently discussing a
+ // rewrite of all things remote, this hardly seems worth resolving.
+ //
+ // reproducer: echo hello | (podman-remote run -i alpine cat)
+ time.Sleep(1 * time.Second)
+ return ClientHangup
default:
// Something really went wrong
return errors.New("unknown multiplex destination")
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
index b41eb5086..2dddd3008 100644
--- a/pkg/varlinkapi/volumes.go
+++ b/pkg/varlinkapi/volumes.go
@@ -3,6 +3,8 @@
package varlinkapi
import (
+ "encoding/json"
+
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
@@ -22,7 +24,11 @@ func (i *LibpodAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.Vol
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels))
}
if len(options.Options) > 0 {
- volumeOptions = append(volumeOptions, libpod.WithVolumeOptions(options.Options))
+ parsedOptions, err := shared.ParseVolumeOptions(options.Options)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ volumeOptions = append(volumeOptions, parsedOptions...)
}
newVolume, err := i.Runtime.NewVolume(getContext(), volumeOptions...)
if err != nil {
@@ -80,6 +86,23 @@ func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all boo
return call.ReplyGetVolumes(volumes)
}
+// InspectVolume inspects a single volume, returning its JSON as a string.
+func (i *LibpodAPI) InspectVolume(call iopodman.VarlinkCall, name string) error {
+ vol, err := i.Runtime.LookupVolume(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ inspectOut, err := vol.Inspect()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ inspectJSON, err := json.Marshal(inspectOut)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyInspectVolume(string(inspectJSON))
+}
+
// VolumesPrune removes unused images via a varlink call
func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
var errs []string