fix compile error
[platform/upstream/docker-engine.git] / container / container_unix.go
1 // +build linux freebsd solaris
2
3 package container
4
5 import (
6         "fmt"
7         "io/ioutil"
8         "os"
9         "path/filepath"
10         "strings"
11
12         "github.com/Sirupsen/logrus"
13         "github.com/docker/docker/api/types"
14         containertypes "github.com/docker/docker/api/types/container"
15         mounttypes "github.com/docker/docker/api/types/mount"
16         "github.com/docker/docker/pkg/chrootarchive"
17         "github.com/docker/docker/pkg/mount"
18         "github.com/docker/docker/pkg/stringid"
19         "github.com/docker/docker/pkg/symlink"
20         "github.com/docker/docker/pkg/system"
21         "github.com/docker/docker/volume"
22         "github.com/opencontainers/selinux/go-selinux/label"
23         "golang.org/x/sys/unix"
24 )
25
26 const (
27         containerSecretMountPath = "/run/secrets"
28 )
29
30 // ExitStatus provides exit reasons for a container.
31 type ExitStatus struct {
32         // The exit code with which the container exited.
33         ExitCode int
34
35         // Whether the container encountered an OOM.
36         OOMKilled bool
37 }
38
39 // TrySetNetworkMount attempts to set the network mounts given a provided destination and
40 // the path to use for it; return true if the given destination was a network mount file
41 func (container *Container) TrySetNetworkMount(destination string, path string) bool {
42         if destination == "/etc/resolv.conf" {
43                 container.ResolvConfPath = path
44                 return true
45         }
46         if destination == "/etc/hostname" {
47                 container.HostnamePath = path
48                 return true
49         }
50         if destination == "/etc/hosts" {
51                 container.HostsPath = path
52                 return true
53         }
54
55         return false
56 }
57
58 // BuildHostnameFile writes the container's hostname file.
59 func (container *Container) BuildHostnameFile() error {
60         hostnamePath, err := container.GetRootResourcePath("hostname")
61         if err != nil {
62                 return err
63         }
64         container.HostnamePath = hostnamePath
65         return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
66 }
67
68 // NetworkMounts returns the list of network mounts.
69 func (container *Container) NetworkMounts() []Mount {
70         var mounts []Mount
71         shared := container.HostConfig.NetworkMode.IsContainer()
72         if container.ResolvConfPath != "" {
73                 if _, err := os.Stat(container.ResolvConfPath); err != nil {
74                         logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err)
75                 } else {
76                         if !container.HasMountFor("/etc/resolv.conf") {
77                                 label.Relabel(container.ResolvConfPath, container.MountLabel, shared)
78                         }
79                         writable := !container.HostConfig.ReadonlyRootfs
80                         if m, exists := container.MountPoints["/etc/resolv.conf"]; exists {
81                                 writable = m.RW
82                         }
83                         mounts = append(mounts, Mount{
84                                 Source:      container.ResolvConfPath,
85                                 Destination: "/etc/resolv.conf",
86                                 Writable:    writable,
87                                 Propagation: string(volume.DefaultPropagationMode),
88                         })
89                 }
90         }
91         if container.HostnamePath != "" {
92                 if _, err := os.Stat(container.HostnamePath); err != nil {
93                         logrus.Warnf("HostnamePath set to %q, but can't stat this filename (err = %v); skipping", container.HostnamePath, err)
94                 } else {
95                         if !container.HasMountFor("/etc/hostname") {
96                                 label.Relabel(container.HostnamePath, container.MountLabel, shared)
97                         }
98                         writable := !container.HostConfig.ReadonlyRootfs
99                         if m, exists := container.MountPoints["/etc/hostname"]; exists {
100                                 writable = m.RW
101                         }
102                         mounts = append(mounts, Mount{
103                                 Source:      container.HostnamePath,
104                                 Destination: "/etc/hostname",
105                                 Writable:    writable,
106                                 Propagation: string(volume.DefaultPropagationMode),
107                         })
108                 }
109         }
110         if container.HostsPath != "" {
111                 if _, err := os.Stat(container.HostsPath); err != nil {
112                         logrus.Warnf("HostsPath set to %q, but can't stat this filename (err = %v); skipping", container.HostsPath, err)
113                 } else {
114                         if !container.HasMountFor("/etc/hosts") {
115                                 label.Relabel(container.HostsPath, container.MountLabel, shared)
116                         }
117                         writable := !container.HostConfig.ReadonlyRootfs
118                         if m, exists := container.MountPoints["/etc/hosts"]; exists {
119                                 writable = m.RW
120                         }
121                         mounts = append(mounts, Mount{
122                                 Source:      container.HostsPath,
123                                 Destination: "/etc/hosts",
124                                 Writable:    writable,
125                                 Propagation: string(volume.DefaultPropagationMode),
126                         })
127                 }
128         }
129         return mounts
130 }
131
132 // CopyImagePathContent copies files in destination to the volume.
133 func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error {
134         rootfs, err := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, destination), container.BaseFS)
135         if err != nil {
136                 return err
137         }
138
139         if _, err = ioutil.ReadDir(rootfs); err != nil {
140                 if os.IsNotExist(err) {
141                         return nil
142                 }
143                 return err
144         }
145
146         id := stringid.GenerateNonCryptoID()
147         path, err := v.Mount(id)
148         if err != nil {
149                 return err
150         }
151
152         defer func() {
153                 if err := v.Unmount(id); err != nil {
154                         logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err)
155                 }
156         }()
157         if err := label.Relabel(path, container.MountLabel, true); err != nil && err != unix.ENOTSUP {
158                 return err
159         }
160         return copyExistingContents(rootfs, path)
161 }
162
163 // ShmResourcePath returns path to shm
164 func (container *Container) ShmResourcePath() (string, error) {
165         return container.GetRootResourcePath("shm")
166 }
167
168 // HasMountFor checks if path is a mountpoint
169 func (container *Container) HasMountFor(path string) bool {
170         _, exists := container.MountPoints[path]
171         return exists
172 }
173
174 // UnmountIpcMounts uses the provided unmount function to unmount shm and mqueue if they were mounted
175 func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
176         if container.HostConfig.IpcMode.IsContainer() || container.HostConfig.IpcMode.IsHost() {
177                 return
178         }
179
180         var warnings []string
181
182         if !container.HasMountFor("/dev/shm") {
183                 shmPath, err := container.ShmResourcePath()
184                 if err != nil {
185                         logrus.Error(err)
186                         warnings = append(warnings, err.Error())
187                 } else if shmPath != "" {
188                         if err := unmount(shmPath); err != nil && !os.IsNotExist(err) {
189                                 if mounted, mErr := mount.Mounted(shmPath); mounted || mErr != nil {
190                                         warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err))
191                                 }
192                         }
193
194                 }
195         }
196
197         if len(warnings) > 0 {
198                 logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n"))
199         }
200 }
201
202 // IpcMounts returns the list of IPC mounts
203 func (container *Container) IpcMounts() []Mount {
204         var mounts []Mount
205
206         if !container.HasMountFor("/dev/shm") {
207                 label.SetFileLabel(container.ShmPath, container.MountLabel)
208                 mounts = append(mounts, Mount{
209                         Source:      container.ShmPath,
210                         Destination: "/dev/shm",
211                         Writable:    true,
212                         Propagation: string(volume.DefaultPropagationMode),
213                 })
214         }
215
216         return mounts
217 }
218
219 // SecretMounts returns the mounts for the secret path.
220 func (container *Container) SecretMounts() []Mount {
221         var mounts []Mount
222         for _, r := range container.SecretReferences {
223                 if r.File == nil {
224                         continue
225                 }
226                 mounts = append(mounts, Mount{
227                         Source:      container.SecretFilePath(*r),
228                         Destination: getSecretTargetPath(r),
229                         Writable:    false,
230                 })
231         }
232
233         return mounts
234 }
235
236 // UnmountSecrets unmounts the local tmpfs for secrets
237 func (container *Container) UnmountSecrets() error {
238         if _, err := os.Stat(container.SecretMountPath()); err != nil {
239                 if os.IsNotExist(err) {
240                         return nil
241                 }
242                 return err
243         }
244
245         return detachMounted(container.SecretMountPath())
246 }
247
248 // ConfigMounts returns the mounts for configs.
249 func (container *Container) ConfigMounts() []Mount {
250         var mounts []Mount
251         for _, configRef := range container.ConfigReferences {
252                 if configRef.File == nil {
253                         continue
254                 }
255                 mounts = append(mounts, Mount{
256                         Source:      container.ConfigFilePath(*configRef),
257                         Destination: configRef.File.Name,
258                         Writable:    false,
259                 })
260         }
261
262         return mounts
263 }
264
265 // UpdateContainer updates configuration of a container. Callers must hold a Lock on the Container.
266 func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
267         // update resources of container
268         resources := hostConfig.Resources
269         cResources := &container.HostConfig.Resources
270
271         // validate NanoCPUs, CPUPeriod, and CPUQuota
272         // Becuase NanoCPU effectively updates CPUPeriod/CPUQuota,
273         // once NanoCPU is already set, updating CPUPeriod/CPUQuota will be blocked, and vice versa.
274         // In the following we make sure the intended update (resources) does not conflict with the existing (cResource).
275         if resources.NanoCPUs > 0 && cResources.CPUPeriod > 0 {
276                 return fmt.Errorf("Conflicting options: Nano CPUs cannot be updated as CPU Period has already been set")
277         }
278         if resources.NanoCPUs > 0 && cResources.CPUQuota > 0 {
279                 return fmt.Errorf("Conflicting options: Nano CPUs cannot be updated as CPU Quota has already been set")
280         }
281         if resources.CPUPeriod > 0 && cResources.NanoCPUs > 0 {
282                 return fmt.Errorf("Conflicting options: CPU Period cannot be updated as NanoCPUs has already been set")
283         }
284         if resources.CPUQuota > 0 && cResources.NanoCPUs > 0 {
285                 return fmt.Errorf("Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set")
286         }
287
288         if resources.BlkioWeight != 0 {
289                 cResources.BlkioWeight = resources.BlkioWeight
290         }
291         if resources.CPUShares != 0 {
292                 cResources.CPUShares = resources.CPUShares
293         }
294         if resources.NanoCPUs != 0 {
295                 cResources.NanoCPUs = resources.NanoCPUs
296         }
297         if resources.CPUPeriod != 0 {
298                 cResources.CPUPeriod = resources.CPUPeriod
299         }
300         if resources.CPUQuota != 0 {
301                 cResources.CPUQuota = resources.CPUQuota
302         }
303         if resources.CpusetCpus != "" {
304                 cResources.CpusetCpus = resources.CpusetCpus
305         }
306         if resources.CpusetMems != "" {
307                 cResources.CpusetMems = resources.CpusetMems
308         }
309         if resources.Memory != 0 {
310                 // if memory limit smaller than already set memoryswap limit and doesn't
311                 // update the memoryswap limit, then error out.
312                 if resources.Memory > cResources.MemorySwap && resources.MemorySwap == 0 {
313                         return fmt.Errorf("Memory limit should be smaller than already set memoryswap limit, update the memoryswap at the same time")
314                 }
315                 cResources.Memory = resources.Memory
316         }
317         if resources.MemorySwap != 0 {
318                 cResources.MemorySwap = resources.MemorySwap
319         }
320         if resources.MemoryReservation != 0 {
321                 cResources.MemoryReservation = resources.MemoryReservation
322         }
323         if resources.KernelMemory != 0 {
324                 cResources.KernelMemory = resources.KernelMemory
325         }
326
327         // update HostConfig of container
328         if hostConfig.RestartPolicy.Name != "" {
329                 if container.HostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() {
330                         return fmt.Errorf("Restart policy cannot be updated because AutoRemove is enabled for the container")
331                 }
332                 container.HostConfig.RestartPolicy = hostConfig.RestartPolicy
333         }
334
335         return nil
336 }
337
338 // DetachAndUnmount uses a detached mount on all mount destinations, then
339 // unmounts each volume normally.
340 // This is used from daemon/archive for `docker cp`
341 func (container *Container) DetachAndUnmount(volumeEventLog func(name, action string, attributes map[string]string)) error {
342         networkMounts := container.NetworkMounts()
343         mountPaths := make([]string, 0, len(container.MountPoints)+len(networkMounts))
344
345         for _, mntPoint := range container.MountPoints {
346                 dest, err := container.GetResourcePath(mntPoint.Destination)
347                 if err != nil {
348                         logrus.Warnf("Failed to get volume destination path for container '%s' at '%s' while lazily unmounting: %v", container.ID, mntPoint.Destination, err)
349                         continue
350                 }
351                 mountPaths = append(mountPaths, dest)
352         }
353
354         for _, m := range networkMounts {
355                 dest, err := container.GetResourcePath(m.Destination)
356                 if err != nil {
357                         logrus.Warnf("Failed to get volume destination path for container '%s' at '%s' while lazily unmounting: %v", container.ID, m.Destination, err)
358                         continue
359                 }
360                 mountPaths = append(mountPaths, dest)
361         }
362
363         for _, mountPath := range mountPaths {
364                 if err := detachMounted(mountPath); err != nil {
365                         logrus.Warnf("%s unmountVolumes: Failed to do lazy umount fo volume '%s': %v", container.ID, mountPath, err)
366                 }
367         }
368         return container.UnmountVolumes(volumeEventLog)
369 }
370
371 // copyExistingContents copies from the source to the destination and
372 // ensures the ownership is appropriately set.
373 func copyExistingContents(source, destination string) error {
374         volList, err := ioutil.ReadDir(source)
375         if err != nil {
376                 return err
377         }
378         if len(volList) > 0 {
379                 srcList, err := ioutil.ReadDir(destination)
380                 if err != nil {
381                         return err
382                 }
383                 if len(srcList) == 0 {
384                         // If the source volume is empty, copies files from the root into the volume
385                         if err := chrootarchive.NewArchiver(nil).CopyWithTar(source, destination); err != nil {
386                                 return err
387                         }
388                 }
389         }
390         return copyOwnership(source, destination)
391 }
392
393 // copyOwnership copies the permissions and uid:gid of the source file
394 // to the destination file
395 func copyOwnership(source, destination string) error {
396         stat, err := system.Stat(source)
397         if err != nil {
398                 return err
399         }
400
401         destStat, err := system.Stat(destination)
402         if err != nil {
403                 return err
404         }
405
406         // In some cases, even though UID/GID match and it would effectively be a no-op,
407         // this can return a permission denied error... for example if this is an NFS
408         // mount.
409         // Since it's not really an error that we can't chown to the same UID/GID, don't
410         // even bother trying in such cases.
411         if stat.UID() != destStat.UID() || stat.GID() != destStat.GID() {
412                 if err := os.Chown(destination, int(stat.UID()), int(stat.GID())); err != nil {
413                         return err
414                 }
415         }
416
417         if stat.Mode() != destStat.Mode() {
418                 return os.Chmod(destination, os.FileMode(stat.Mode()))
419         }
420         return nil
421 }
422
423 // TmpfsMounts returns the list of tmpfs mounts
424 func (container *Container) TmpfsMounts() ([]Mount, error) {
425         var mounts []Mount
426         for dest, data := range container.HostConfig.Tmpfs {
427                 mounts = append(mounts, Mount{
428                         Source:      "tmpfs",
429                         Destination: dest,
430                         Data:        data,
431                 })
432         }
433         for dest, mnt := range container.MountPoints {
434                 if mnt.Type == mounttypes.TypeTmpfs {
435                         data, err := volume.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly)
436                         if err != nil {
437                                 return nil, err
438                         }
439                         mounts = append(mounts, Mount{
440                                 Source:      "tmpfs",
441                                 Destination: dest,
442                                 Data:        data,
443                         })
444                 }
445         }
446         return mounts, nil
447 }
448
449 // cleanResourcePath cleans a resource path and prepares to combine with mnt path
450 func cleanResourcePath(path string) string {
451         return filepath.Join(string(os.PathSeparator), path)
452 }
453
454 // EnableServiceDiscoveryOnDefaultNetwork Enable service discovery on default network
455 func (container *Container) EnableServiceDiscoveryOnDefaultNetwork() bool {
456         return false
457 }
458
459 // GetMountPoints gives a platform specific transformation to types.MountPoint. Callers must hold a Container lock.
460 func (container *Container) GetMountPoints() []types.MountPoint {
461         mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
462         for _, m := range container.MountPoints {
463                 mountPoints = append(mountPoints, types.MountPoint{
464                         Type:        m.Type,
465                         Name:        m.Name,
466                         Source:      m.Path(),
467                         Destination: m.Destination,
468                         Driver:      m.Driver,
469                         Mode:        m.Mode,
470                         RW:          m.RW,
471                         Propagation: m.Propagation,
472                 })
473         }
474         return mountPoints
475 }