daemon/container_operations_unix.go
6bb0d181
 // +build linux freebsd
 
 package daemon
 
 import (
 	"fmt"
3716ec25
 	"io/ioutil"
6bb0d181
 	"os"
 	"path/filepath"
 	"strconv"
 	"syscall"
 	"time"
 
 	"github.com/Sirupsen/logrus"
857e60c2
 	"github.com/cloudflare/cfssl/log"
6bb0d181
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/links"
 	"github.com/docker/docker/pkg/idtools"
427c70d9
 	"github.com/docker/docker/pkg/mount"
6bb0d181
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/runconfig"
600f0ad2
 	"github.com/docker/libnetwork"
6bb0d181
 	"github.com/opencontainers/runc/libcontainer/label"
857e60c2
 	"github.com/pkg/errors"
6bb0d181
 )
 
 func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
 	var env []string
0f9f9950
 	children := daemon.children(container)
6bb0d181
 
e8026d8a
 	bridgeSettings := container.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()]
99a98ccc
 	if bridgeSettings == nil || bridgeSettings.EndpointSettings == nil {
6bb0d181
 		return nil, nil
 	}
 
0f9f9950
 	for linkAlias, child := range children {
 		if !child.IsRunning() {
a793564b
 			return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.Name, linkAlias)
0f9f9950
 		}
6bb0d181
 
e8026d8a
 		childBridgeSettings := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()]
99a98ccc
 		if childBridgeSettings == nil || childBridgeSettings.EndpointSettings == nil {
0f9f9950
 			return nil, fmt.Errorf("container %s not attached to default bridge network", child.ID)
 		}
6bb0d181
 
0f9f9950
 		link := links.NewLink(
 			bridgeSettings.IPAddress,
 			childBridgeSettings.IPAddress,
 			linkAlias,
 			child.Config.Env,
 			child.Config.ExposedPorts,
 		)
6bb0d181
 
64238fef
 		env = append(env, link.ToEnv()...)
6bb0d181
 	}
0f9f9950
 
6bb0d181
 	return env, nil
 }
 
 func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) {
 	containerID := container.HostConfig.IpcMode.Container()
d7d512bb
 	c, err := daemon.GetContainer(containerID)
6bb0d181
 	if err != nil {
 		return nil, err
 	}
 	if !c.IsRunning() {
a793564b
 		return nil, fmt.Errorf("cannot join IPC of a non running container: %s", containerID)
6bb0d181
 	}
3c0a91d2
 	if c.IsRestarting() {
a793564b
 		return nil, errContainerIsRestarting(container.ID)
3c0a91d2
 	}
6bb0d181
 	return c, nil
 }
 
fb43ef64
 func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) {
 	containerID := container.HostConfig.PidMode.Container()
 	c, err := daemon.GetContainer(containerID)
 	if err != nil {
 		return nil, err
 	}
 	if !c.IsRunning() {
 		return nil, fmt.Errorf("cannot join PID of a non running container: %s", containerID)
 	}
 	if c.IsRestarting() {
 		return nil, errContainerIsRestarting(container.ID)
 	}
 	return c, nil
 }
 
6bb0d181
 func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
9c4570a9
 	var err error
 
 	c.ShmPath, err = c.ShmResourcePath()
 	if err != nil {
 		return err
 	}
 
 	if c.HostConfig.IpcMode.IsContainer() {
 		ic, err := daemon.getIpcContainer(c)
6bb0d181
 		if err != nil {
 			return err
 		}
9c4570a9
 		c.ShmPath = ic.ShmPath
 	} else if c.HostConfig.IpcMode.IsHost() {
 		if _, err := os.Stat("/dev/shm"); err != nil {
 			return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host")
6bb0d181
 		}
9c4570a9
 		c.ShmPath = "/dev/shm"
 	} else {
 		rootUID, rootGID := daemon.GetRemappedUIDGID()
 		if !c.HasMountFor("/dev/shm") {
 			shmPath, err := c.ShmResourcePath()
 			if err != nil {
 				return err
 			}
6bb0d181
 
9c4570a9
 			if err := idtools.MkdirAllAs(shmPath, 0700, rootUID, rootGID); err != nil {
 				return err
 			}
 
 			shmSize := container.DefaultSHMSize
 			if c.HostConfig.ShmSize != 0 {
 				shmSize = c.HostConfig.ShmSize
 			}
 			shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10)
 			if err := syscall.Mount("shm", shmPath, "tmpfs", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil {
 				return fmt.Errorf("mounting shm tmpfs: %s", err)
 			}
 			if err := os.Chown(shmPath, rootUID, rootGID); err != nil {
 				return err
 			}
6bb0d181
 		}
9c4570a9
 
6bb0d181
 	}
 
 	return nil
 }
3716ec25
 
0c170a76
 func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) {
01aab8ba
 	if len(c.SecretReferences) == 0 {
6d12de53
 		return nil
 	}
 
3716ec25
 	localMountPath := c.SecretMountPath()
 	logrus.Debugf("secrets: setting up secret dir: %s", localMountPath)
 
00237a96
 	defer func() {
 		if setupErr != nil {
857e60c2
 			// cleanup
 			_ = detachMounted(localMountPath)
 
 			if err := os.RemoveAll(localMountPath); err != nil {
 				log.Errorf("error cleaning up secret mount: %s", err)
 			}
 		}
00237a96
 	}()
857e60c2
 
8119809b
 	// retrieve possible remapped range start for root UID, GID
 	rootUID, rootGID := daemon.GetRemappedUIDGID()
3716ec25
 	// create tmpfs
8119809b
 	if err := idtools.MkdirAllAs(localMountPath, 0700, rootUID, rootGID); err != nil {
0c170a76
 		return errors.Wrap(err, "error creating secret local mount path")
3716ec25
 	}
8119809b
 	tmpfsOwnership := fmt.Sprintf("uid=%d,gid=%d", rootUID, rootGID)
 	if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "nodev,nosuid,noexec,"+tmpfsOwnership); err != nil {
0c170a76
 		return errors.Wrap(err, "unable to setup secret mount")
3716ec25
 	}
 
01aab8ba
 	for _, s := range c.SecretReferences {
 		if c.SecretStore == nil {
 			return fmt.Errorf("secret store is not initialized")
 		}
 
 		// TODO (ehazlett): use type switch when more are supported
 		if s.File == nil {
 			return fmt.Errorf("secret target type is not a file target")
 		}
 
 		targetPath := filepath.Clean(s.File.Name)
857e60c2
 		// ensure that the target is a filename only; no paths allowed
583c013a
 		if targetPath != filepath.Base(targetPath) {
 			return fmt.Errorf("error creating secret: secret must not be a path")
857e60c2
 		}
 
583c013a
 		fPath := filepath.Join(localMountPath, targetPath)
8119809b
 		if err := idtools.MkdirAllAs(filepath.Dir(fPath), 0700, rootUID, rootGID); err != nil {
0c170a76
 			return errors.Wrap(err, "error creating secret mount path")
3716ec25
 		}
 
857e60c2
 		logrus.WithFields(logrus.Fields{
01aab8ba
 			"name": s.File.Name,
857e60c2
 			"path": fPath,
 		}).Debug("injecting secret")
01aab8ba
 		secret := c.SecretStore.Get(s.SecretID)
 		if secret == nil {
 			return fmt.Errorf("unable to get secret from secret store")
 		}
 		if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil {
0c170a76
 			return errors.Wrap(err, "error injecting secret")
3716ec25
 		}
 
01aab8ba
 		uid, err := strconv.Atoi(s.File.UID)
b2e4c7f3
 		if err != nil {
 			return err
 		}
01aab8ba
 		gid, err := strconv.Atoi(s.File.GID)
b2e4c7f3
 		if err != nil {
 			return err
 		}
 
8119809b
 		if err := os.Chown(fPath, rootUID+uid, rootGID+gid); err != nil {
0c170a76
 			return errors.Wrap(err, "error setting ownership for secret")
3716ec25
 		}
 	}
 
 	// remount secrets ro
8119809b
 	if err := mount.Mount("tmpfs", localMountPath, "tmpfs", "remount,ro,"+tmpfsOwnership); err != nil {
0c170a76
 		return errors.Wrap(err, "unable to remount secret dir as readonly")
3716ec25
 	}
 
0c170a76
 	return nil
3716ec25
 }
 
6bb0d181
 func killProcessDirectly(container *container.Container) error {
 	if _, err := container.WaitStop(10 * time.Second); err != nil {
 		// Ensure that we don't kill ourselves
 		if pid := container.GetPID(); pid != 0 {
 			logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(container.ID))
 			if err := syscall.Kill(pid, 9); err != nil {
 				if err != syscall.ESRCH {
 					return err
 				}
1a729c3d
 				e := errNoSuchProcess{pid, 9}
 				logrus.Debug(e)
 				return e
6bb0d181
 			}
 		}
 	}
 	return nil
 }
 
 func detachMounted(path string) error {
 	return syscall.Unmount(path, syscall.MNT_DETACH)
 }
 
 func isLinkable(child *container.Container) bool {
 	// A container is linkable only if it belongs to the default network
e8026d8a
 	_, ok := child.NetworkSettings.Networks[runconfig.DefaultDaemonNetworkMode().NetworkName()]
6bb0d181
 	return ok
 }
a793564b
 
1991f6eb
 func enableIPOnPredefinedNetwork() bool {
 	return false
 }
50f02b58
 
 func (daemon *Daemon) isNetworkHotPluggable() bool {
 	return true
 }
600f0ad2
 
 func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
 	var err error
 
 	container.HostsPath, err = container.GetRootResourcePath("hosts")
 	if err != nil {
 		return err
 	}
 	*sboxOptions = append(*sboxOptions, libnetwork.OptionHostsPath(container.HostsPath))
 
 	container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf")
 	if err != nil {
 		return err
 	}
 	*sboxOptions = append(*sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath))
 	return nil
 }
 
 func initializeNetworkingPaths(container *container.Container, nc *container.Container) {
 	container.HostnamePath = nc.HostnamePath
 	container.HostsPath = nc.HostsPath
 	container.ResolvConfPath = nc.ResolvConfPath
 }