-
Notifications
You must be signed in to change notification settings - Fork 66
/
daemon_linux.go
167 lines (145 loc) · 4.58 KB
/
daemon_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package daemon // import "github.com/docker/docker/daemon"
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strings"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/internal/procfs"
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/mount"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
defaultResolvConf = "/etc/resolv.conf"
alternateResolvConf = "/run/systemd/resolve/resolv.conf"
)
// 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/balena-engine/plugins"
}
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
regexps := getCleanPatterns(id)
sc := bufio.NewScanner(reader)
for sc.Scan() {
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())
}
}
}
}
}
}
if err := sc.Err(); err != nil {
return err
}
if len(errors) > 0 {
return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errors, "\n"))
}
logrus.Debugf("Cleaning up old mountid %v: done.", id)
return nil
}
// cleanupMounts umounts used by container resources and the daemon root mount
func (daemon *Daemon) cleanupMounts() error {
if err := daemon.cleanupMountsByID(""); err != nil {
return err
}
info, err := mount.GetMounts(mount.SingleEntryFilter(daemon.root))
if err != nil {
return errors.Wrap(err, "error reading mount table for cleanup")
}
if len(info) < 1 {
// no mount found, we're done here
return nil
}
// `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.
if !shouldUnmountRoot(daemon.root, info[0]) {
return nil
}
unmountFile := getUnmountOnShutdownPath(daemon.configStore)
if _, err := os.Stat(unmountFile); err != nil {
return nil
}
logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root")
if err := mount.Unmount(daemon.root); err != nil {
return err
}
return os.Remove(unmountFile)
}
func getCleanPatterns(id string) (regexps []*regexp.Regexp) {
var patterns []string
if id == "" {
id = "[0-9a-f]{64}"
patterns = append(patterns, "containers/"+id+"/shm")
}
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)
}
}
return
}
func getRealPath(path string) (string, error) {
return fileutils.ReadSymlinkedDirectory(path)
}
func shouldUnmountRoot(root string, info *mount.Info) bool {
if !strings.HasSuffix(root, info.Root) {
return false
}
return hasMountinfoOption(info.Optional, sharedPropagationOption)
}
// 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)
}
}