2018-02-05 16:05:59 -05:00
|
|
|
package daemon // import "github.com/docker/docker/daemon"
|
2015-08-03 18:05:34 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2015-08-26 08:00:01 -04:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2015-08-03 18:05:34 -04:00
|
|
|
"os"
|
2016-03-29 18:27:04 -04:00
|
|
|
"regexp"
|
2015-08-03 18:05:34 -04:00
|
|
|
"strings"
|
|
|
|
|
2018-07-17 15:11:38 -04:00
|
|
|
"github.com/docker/docker/daemon/config"
|
|
|
|
"github.com/docker/docker/internal/procfs"
|
2017-05-31 20:11:42 -04:00
|
|
|
"github.com/docker/docker/pkg/fileutils"
|
2015-10-30 14:55:52 -04:00
|
|
|
"github.com/docker/docker/pkg/mount"
|
2018-01-24 18:10:01 -05:00
|
|
|
"github.com/pkg/errors"
|
2017-07-26 17:42:13 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2015-08-03 18:05:34 -04:00
|
|
|
)
|
|
|
|
|
2018-07-17 15:11:38 -04:00
|
|
|
const (
|
|
|
|
defaultResolvConf = "/etc/resolv.conf"
|
|
|
|
alternateResolvConf = "/run/systemd/resolve/resolv.conf"
|
|
|
|
)
|
|
|
|
|
2017-02-02 14:22:12 -05:00
|
|
|
// On Linux, plugins use a static path for storing execution state,
|
|
|
|
// instead of deriving path from daemon's exec-root. This is because
|
|
|
|
// plugin socket files are created here and they cannot exceed max
|
|
|
|
// path length of 108 bytes.
|
|
|
|
func getPluginExecRoot(root string) string {
|
|
|
|
return "/run/docker/plugins"
|
|
|
|
}
|
|
|
|
|
2016-03-18 14:50:19 -04:00
|
|
|
func (daemon *Daemon) cleanupMountsByID(id string) error {
|
|
|
|
logrus.Debugf("Cleaning up old mountid %s: start.", id)
|
|
|
|
f, err := os.Open("/proc/self/mountinfo")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
return daemon.cleanupMountsFromReaderByID(f, id, mount.Unmount)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, unmount func(target string) error) error {
|
|
|
|
if daemon.root == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var errors []string
|
2016-03-29 18:27:04 -04:00
|
|
|
|
|
|
|
regexps := getCleanPatterns(id)
|
2016-03-18 14:50:19 -04:00
|
|
|
sc := bufio.NewScanner(reader)
|
|
|
|
for sc.Scan() {
|
2016-03-29 18:27:04 -04:00
|
|
|
if fields := strings.Fields(sc.Text()); len(fields) >= 4 {
|
|
|
|
if mnt := fields[4]; strings.HasPrefix(mnt, daemon.root) {
|
|
|
|
for _, p := range regexps {
|
|
|
|
if p.MatchString(mnt) {
|
|
|
|
if err := unmount(mnt); err != nil {
|
|
|
|
logrus.Error(err)
|
|
|
|
errors = append(errors, err.Error())
|
|
|
|
}
|
|
|
|
}
|
2016-03-18 14:50:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := sc.Err(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errors) > 0 {
|
2016-03-29 18:27:04 -04:00
|
|
|
return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errors, "\n"))
|
2016-03-18 14:50:19 -04:00
|
|
|
}
|
|
|
|
|
2016-03-29 18:27:04 -04:00
|
|
|
logrus.Debugf("Cleaning up old mountid %v: done.", id)
|
2016-03-18 14:50:19 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-24 18:10:01 -05:00
|
|
|
// cleanupMounts umounts used by container resources and the daemon root mount
|
2015-08-03 18:05:34 -04:00
|
|
|
func (daemon *Daemon) cleanupMounts() error {
|
2018-01-24 18:10:01 -05:00
|
|
|
if err := daemon.cleanupMountsByID(""); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-03-01 00:55:57 -05:00
|
|
|
info, err := mount.GetMounts(mount.SingleEntryFilter(daemon.root))
|
2018-01-24 18:10:01 -05:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error reading mount table for cleanup")
|
|
|
|
}
|
|
|
|
|
2018-03-01 00:55:57 -05:00
|
|
|
if len(info) < 1 {
|
|
|
|
// no mount found, we're done here
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-24 18:10:01 -05:00
|
|
|
// `info.Root` here is the root mountpoint of the passed in path (`daemon.root`).
|
|
|
|
// The ony cases that need to be cleaned up is when the daemon has performed a
|
|
|
|
// `mount --bind /daemon/root /daemon/root && mount --make-shared /daemon/root`
|
|
|
|
// This is only done when the daemon is started up and `/daemon/root` is not
|
|
|
|
// already on a shared mountpoint.
|
2018-03-01 00:55:57 -05:00
|
|
|
if !shouldUnmountRoot(daemon.root, info[0]) {
|
2018-01-24 18:10:01 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-17 11:30:39 -04:00
|
|
|
unmountFile := getUnmountOnShutdownPath(daemon.configStore)
|
|
|
|
if _, err := os.Stat(unmountFile); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-24 18:10:01 -05:00
|
|
|
logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root")
|
2018-04-17 11:30:39 -04:00
|
|
|
if err := mount.Unmount(daemon.root); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.Remove(unmountFile)
|
2015-08-26 08:00:01 -04:00
|
|
|
}
|
|
|
|
|
2016-03-29 18:27:04 -04:00
|
|
|
func getCleanPatterns(id string) (regexps []*regexp.Regexp) {
|
|
|
|
var patterns []string
|
|
|
|
if id == "" {
|
|
|
|
id = "[0-9a-f]{64}"
|
|
|
|
patterns = append(patterns, "containers/"+id+"/shm")
|
2015-08-25 03:58:33 -04:00
|
|
|
}
|
2016-03-29 18:27:04 -04:00
|
|
|
patterns = append(patterns, "aufs/mnt/"+id+"$", "overlay/"+id+"/merged$", "zfs/graph/"+id+"$")
|
|
|
|
for _, p := range patterns {
|
|
|
|
r, err := regexp.Compile(p)
|
|
|
|
if err == nil {
|
|
|
|
regexps = append(regexps, r)
|
2015-08-03 18:05:34 -04:00
|
|
|
}
|
|
|
|
}
|
2016-03-29 18:27:04 -04:00
|
|
|
return
|
2015-08-03 18:05:34 -04:00
|
|
|
}
|
2017-05-31 20:11:42 -04:00
|
|
|
|
|
|
|
func getRealPath(path string) (string, error) {
|
|
|
|
return fileutils.ReadSymlinkedDirectory(path)
|
|
|
|
}
|
2018-01-24 18:10:01 -05:00
|
|
|
|
|
|
|
func shouldUnmountRoot(root string, info *mount.Info) bool {
|
|
|
|
if !strings.HasSuffix(root, info.Root) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return hasMountinfoOption(info.Optional, sharedPropagationOption)
|
|
|
|
}
|
2018-07-17 15:11:38 -04:00
|
|
|
|
|
|
|
// setupResolvConf sets the appropriate resolv.conf file if not specified
|
|
|
|
// When systemd-resolved is running the default /etc/resolv.conf points to
|
|
|
|
// localhost. In this case fetch the alternative config file that is in a
|
|
|
|
// different path so that containers can use it
|
|
|
|
// In all the other cases fallback to the default one
|
|
|
|
func setupResolvConf(config *config.Config) {
|
|
|
|
if config.ResolvConf != "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
config.ResolvConf = defaultResolvConf
|
|
|
|
pids, err := procfs.PidOf("systemd-resolved")
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("unable to check systemd-resolved status: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(pids) > 0 && pids[0] > 0 {
|
|
|
|
_, err := os.Stat(alternateResolvConf)
|
|
|
|
if err == nil {
|
|
|
|
logrus.Infof("systemd-resolved is running, so using resolvconf: %s", alternateResolvConf)
|
|
|
|
config.ResolvConf = alternateResolvConf
|
|
|
|
return
|
|
|
|
}
|
|
|
|
logrus.Infof("systemd-resolved is running, but %s is not present, fallback to %s", alternateResolvConf, defaultResolvConf)
|
|
|
|
}
|
|
|
|
}
|