github.com/rawahars/moby@v24.0.4+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 "net" 8 "os" 9 "regexp" 10 "strings" 11 12 "github.com/docker/docker/daemon/config" 13 "github.com/docker/docker/libnetwork/ns" 14 "github.com/docker/docker/libnetwork/resolvconf" 15 "github.com/moby/sys/mount" 16 "github.com/moby/sys/mountinfo" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 "github.com/vishvananda/netlink" 20 ) 21 22 // On Linux, plugins use a static path for storing execution state, 23 // instead of deriving path from daemon's exec-root. This is because 24 // plugin socket files are created here and they cannot exceed max 25 // path length of 108 bytes. 26 func getPluginExecRoot(_ *config.Config) string { 27 return "/run/docker/plugins" 28 } 29 30 func (daemon *Daemon) cleanupMountsByID(id string) error { 31 logrus.Debugf("Cleaning up old mountid %s: start.", id) 32 f, err := os.Open("/proc/self/mountinfo") 33 if err != nil { 34 return err 35 } 36 defer f.Close() 37 38 return daemon.cleanupMountsFromReaderByID(f, id, mount.Unmount) 39 } 40 41 func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, unmount func(target string) error) error { 42 if daemon.root == "" { 43 return nil 44 } 45 var errs []string 46 47 regexps := getCleanPatterns(id) 48 sc := bufio.NewScanner(reader) 49 for sc.Scan() { 50 if fields := strings.Fields(sc.Text()); len(fields) > 4 { 51 if mnt := fields[4]; strings.HasPrefix(mnt, daemon.root) { 52 for _, p := range regexps { 53 if p.MatchString(mnt) { 54 if err := unmount(mnt); err != nil { 55 logrus.Error(err) 56 errs = append(errs, err.Error()) 57 } 58 } 59 } 60 } 61 } 62 } 63 64 if err := sc.Err(); err != nil { 65 return err 66 } 67 68 if len(errs) > 0 { 69 return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errs, "\n")) 70 } 71 72 logrus.Debugf("Cleaning up old mountid %v: done.", id) 73 return nil 74 } 75 76 // cleanupMounts umounts used by container resources and the daemon root mount 77 func (daemon *Daemon) cleanupMounts() error { 78 if err := daemon.cleanupMountsByID(""); err != nil { 79 return err 80 } 81 82 info, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(daemon.root)) 83 if err != nil { 84 return errors.Wrap(err, "error reading mount table for cleanup") 85 } 86 87 if len(info) < 1 { 88 // no mount found, we're done here 89 return nil 90 } 91 92 // `info.Root` here is the root mountpoint of the passed in path (`daemon.root`). 93 // The ony cases that need to be cleaned up is when the daemon has performed a 94 // `mount --bind /daemon/root /daemon/root && mount --make-shared /daemon/root` 95 // This is only done when the daemon is started up and `/daemon/root` is not 96 // already on a shared mountpoint. 97 if !shouldUnmountRoot(daemon.root, info[0]) { 98 return nil 99 } 100 101 unmountFile := getUnmountOnShutdownPath(daemon.configStore) 102 if _, err := os.Stat(unmountFile); err != nil { 103 return nil 104 } 105 106 logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root") 107 if err := mount.Unmount(daemon.root); err != nil { 108 return err 109 } 110 return os.Remove(unmountFile) 111 } 112 113 func getCleanPatterns(id string) (regexps []*regexp.Regexp) { 114 var patterns []string 115 if id == "" { 116 id = "[0-9a-f]{64}" 117 patterns = append(patterns, "containers/"+id+"/mounts/shm", "containers/"+id+"/shm") 118 } 119 patterns = append(patterns, "overlay2/"+id+"/merged$", "zfs/graph/"+id+"$") 120 for _, p := range patterns { 121 r, err := regexp.Compile(p) 122 if err == nil { 123 regexps = append(regexps, r) 124 } 125 } 126 return 127 } 128 129 func shouldUnmountRoot(root string, info *mountinfo.Info) bool { 130 if !strings.HasSuffix(root, info.Root) { 131 return false 132 } 133 return hasMountInfoOption(info.Optional, sharedPropagationOption) 134 } 135 136 // setupResolvConf sets the appropriate resolv.conf file if not specified 137 // When systemd-resolved is running the default /etc/resolv.conf points to 138 // localhost. In this case fetch the alternative config file that is in a 139 // different path so that containers can use it 140 // In all the other cases fallback to the default one 141 func setupResolvConf(config *config.Config) { 142 if config.ResolvConf != "" { 143 return 144 } 145 config.ResolvConf = resolvconf.Path() 146 } 147 148 // ifaceAddrs returns the IPv4 and IPv6 addresses assigned to the network 149 // interface with name linkName. 150 // 151 // No error is returned if the named interface does not exist. 152 func ifaceAddrs(linkName string) (v4, v6 []*net.IPNet, err error) { 153 nl := ns.NlHandle() 154 link, err := nl.LinkByName(linkName) 155 if err != nil { 156 if !errors.As(err, new(netlink.LinkNotFoundError)) { 157 return nil, nil, err 158 } 159 return nil, nil, nil 160 } 161 162 get := func(family int) ([]*net.IPNet, error) { 163 addrs, err := nl.AddrList(link, family) 164 if err != nil { 165 return nil, err 166 } 167 168 ipnets := make([]*net.IPNet, len(addrs)) 169 for i := range addrs { 170 ipnets[i] = addrs[i].IPNet 171 } 172 return ipnets, nil 173 } 174 175 v4, err = get(netlink.FAMILY_V4) 176 if err != nil { 177 return nil, nil, err 178 } 179 v6, err = get(netlink.FAMILY_V6) 180 if err != nil { 181 return nil, nil, err 182 } 183 return v4, v6, nil 184 }