1package daemon // import "github.com/docker/docker/daemon"
2
3import (
4	"bufio"
5	"fmt"
6	"io"
7	"os"
8	"regexp"
9	"strings"
10
11	"github.com/docker/docker/daemon/config"
12	"github.com/docker/libnetwork/resolvconf"
13	"github.com/moby/sys/mount"
14	"github.com/moby/sys/mountinfo"
15	"github.com/pkg/errors"
16	"github.com/sirupsen/logrus"
17)
18
19// On Linux, plugins use a static path for storing execution state,
20// instead of deriving path from daemon's exec-root. This is because
21// plugin socket files are created here and they cannot exceed max
22// path length of 108 bytes.
23func getPluginExecRoot(root string) string {
24	return "/run/docker/plugins"
25}
26
27func (daemon *Daemon) cleanupMountsByID(id string) error {
28	logrus.Debugf("Cleaning up old mountid %s: start.", id)
29	f, err := os.Open("/proc/self/mountinfo")
30	if err != nil {
31		return err
32	}
33	defer f.Close()
34
35	return daemon.cleanupMountsFromReaderByID(f, id, mount.Unmount)
36}
37
38func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, unmount func(target string) error) error {
39	if daemon.root == "" {
40		return nil
41	}
42	var errs []string
43
44	regexps := getCleanPatterns(id)
45	sc := bufio.NewScanner(reader)
46	for sc.Scan() {
47		if fields := strings.Fields(sc.Text()); len(fields) >= 4 {
48			if mnt := fields[4]; strings.HasPrefix(mnt, daemon.root) {
49				for _, p := range regexps {
50					if p.MatchString(mnt) {
51						if err := unmount(mnt); err != nil {
52							logrus.Error(err)
53							errs = append(errs, err.Error())
54						}
55					}
56				}
57			}
58		}
59	}
60
61	if err := sc.Err(); err != nil {
62		return err
63	}
64
65	if len(errs) > 0 {
66		return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errs, "\n"))
67	}
68
69	logrus.Debugf("Cleaning up old mountid %v: done.", id)
70	return nil
71}
72
73// cleanupMounts umounts used by container resources and the daemon root mount
74func (daemon *Daemon) cleanupMounts() error {
75	if err := daemon.cleanupMountsByID(""); err != nil {
76		return err
77	}
78
79	info, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(daemon.root))
80	if err != nil {
81		return errors.Wrap(err, "error reading mount table for cleanup")
82	}
83
84	if len(info) < 1 {
85		// no mount found, we're done here
86		return nil
87	}
88
89	// `info.Root` here is the root mountpoint of the passed in path (`daemon.root`).
90	// The ony cases that need to be cleaned up is when the daemon has performed a
91	//   `mount --bind /daemon/root /daemon/root && mount --make-shared /daemon/root`
92	// This is only done when the daemon is started up and `/daemon/root` is not
93	// already on a shared mountpoint.
94	if !shouldUnmountRoot(daemon.root, info[0]) {
95		return nil
96	}
97
98	unmountFile := getUnmountOnShutdownPath(daemon.configStore)
99	if _, err := os.Stat(unmountFile); err != nil {
100		return nil
101	}
102
103	logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root")
104	if err := mount.Unmount(daemon.root); err != nil {
105		return err
106	}
107	return os.Remove(unmountFile)
108}
109
110func getCleanPatterns(id string) (regexps []*regexp.Regexp) {
111	var patterns []string
112	if id == "" {
113		id = "[0-9a-f]{64}"
114		patterns = append(patterns, "containers/"+id+"/shm")
115	}
116	patterns = append(patterns, "aufs/mnt/"+id+"$", "overlay/"+id+"/merged$", "zfs/graph/"+id+"$")
117	for _, p := range patterns {
118		r, err := regexp.Compile(p)
119		if err == nil {
120			regexps = append(regexps, r)
121		}
122	}
123	return
124}
125
126func shouldUnmountRoot(root string, info *mountinfo.Info) bool {
127	if !strings.HasSuffix(root, info.Root) {
128		return false
129	}
130	return hasMountInfoOption(info.Optional, sharedPropagationOption)
131}
132
133// setupResolvConf sets the appropriate resolv.conf file if not specified
134// When systemd-resolved is running the default /etc/resolv.conf points to
135// localhost. In this case fetch the alternative config file that is in a
136// different path so that containers can use it
137// In all the other cases fallback to the default one
138func setupResolvConf(config *config.Config) {
139	if config.ResolvConf != "" {
140		return
141	}
142	config.ResolvConf = resolvconf.Path()
143}
144