github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/daemon_linux.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 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. 23 func getPluginExecRoot(root string) string { 24 return "/run/docker/plugins" 25 } 26 27 func (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 38 func (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 74 func (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 110 func 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 126 func 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 138 func setupResolvConf(config *config.Config) { 139 if config.ResolvConf != "" { 140 return 141 } 142 config.ResolvConf = resolvconf.Path() 143 }