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  }