container.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. package container
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "os"
  7. "path/filepath"
  8. "sync"
  9. "syscall"
  10. "time"
  11. "github.com/Sirupsen/logrus"
  12. containertypes "github.com/docker/docker/api/types/container"
  13. "github.com/docker/docker/daemon/exec"
  14. "github.com/docker/docker/daemon/execdriver"
  15. "github.com/docker/docker/daemon/logger"
  16. "github.com/docker/docker/daemon/logger/jsonfilelog"
  17. "github.com/docker/docker/daemon/network"
  18. derr "github.com/docker/docker/errors"
  19. "github.com/docker/docker/image"
  20. "github.com/docker/docker/layer"
  21. "github.com/docker/docker/pkg/promise"
  22. "github.com/docker/docker/pkg/signal"
  23. "github.com/docker/docker/pkg/symlink"
  24. "github.com/docker/docker/runconfig"
  25. "github.com/docker/docker/volume"
  26. "github.com/docker/go-connections/nat"
  27. "github.com/opencontainers/runc/libcontainer/label"
  28. )
  29. const configFileName = "config.v2.json"
  30. // CommonContainer holds the fields for a container which are
  31. // applicable across all platforms supported by the daemon.
  32. type CommonContainer struct {
  33. *runconfig.StreamConfig
  34. // embed for Container to support states directly.
  35. *State `json:"State"` // Needed for remote api version <= 1.11
  36. Root string `json:"-"` // Path to the "home" of the container, including metadata.
  37. BaseFS string `json:"-"` // Path to the graphdriver mountpoint
  38. RWLayer layer.RWLayer `json:"-"`
  39. ID string
  40. Created time.Time
  41. Path string
  42. Args []string
  43. Config *containertypes.Config
  44. ImageID image.ID `json:"Image"`
  45. NetworkSettings *network.Settings
  46. LogPath string
  47. Name string
  48. Driver string
  49. // MountLabel contains the options for the 'mount' command
  50. MountLabel string
  51. ProcessLabel string
  52. RestartCount int
  53. HasBeenStartedBefore bool
  54. HasBeenManuallyStopped bool // used for unless-stopped restart policy
  55. MountPoints map[string]*volume.MountPoint
  56. HostConfig *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
  57. Command *execdriver.Command `json:"-"`
  58. monitor *containerMonitor
  59. ExecCommands *exec.Store `json:"-"`
  60. // logDriver for closing
  61. LogDriver logger.Logger `json:"-"`
  62. LogCopier *logger.Copier `json:"-"`
  63. }
  64. // NewBaseContainer creates a new container with its
  65. // basic configuration.
  66. func NewBaseContainer(id, root string) *Container {
  67. return &Container{
  68. CommonContainer: CommonContainer{
  69. ID: id,
  70. State: NewState(),
  71. ExecCommands: exec.NewStore(),
  72. Root: root,
  73. MountPoints: make(map[string]*volume.MountPoint),
  74. StreamConfig: runconfig.NewStreamConfig(),
  75. },
  76. }
  77. }
  78. // FromDisk loads the container configuration stored in the host.
  79. func (container *Container) FromDisk() error {
  80. pth, err := container.ConfigPath()
  81. if err != nil {
  82. return err
  83. }
  84. jsonSource, err := os.Open(pth)
  85. if err != nil {
  86. return err
  87. }
  88. defer jsonSource.Close()
  89. dec := json.NewDecoder(jsonSource)
  90. // Load container settings
  91. if err := dec.Decode(container); err != nil {
  92. return err
  93. }
  94. if err := label.ReserveLabel(container.ProcessLabel); err != nil {
  95. return err
  96. }
  97. return container.readHostConfig()
  98. }
  99. // ToDisk saves the container configuration on disk.
  100. func (container *Container) ToDisk() error {
  101. pth, err := container.ConfigPath()
  102. if err != nil {
  103. return err
  104. }
  105. jsonSource, err := os.Create(pth)
  106. if err != nil {
  107. return err
  108. }
  109. defer jsonSource.Close()
  110. enc := json.NewEncoder(jsonSource)
  111. // Save container settings
  112. if err := enc.Encode(container); err != nil {
  113. return err
  114. }
  115. return container.WriteHostConfig()
  116. }
  117. // ToDiskLocking saves the container configuration on disk in a thread safe way.
  118. func (container *Container) ToDiskLocking() error {
  119. container.Lock()
  120. err := container.ToDisk()
  121. container.Unlock()
  122. return err
  123. }
  124. // readHostConfig reads the host configuration from disk for the container.
  125. func (container *Container) readHostConfig() error {
  126. container.HostConfig = &containertypes.HostConfig{}
  127. // If the hostconfig file does not exist, do not read it.
  128. // (We still have to initialize container.HostConfig,
  129. // but that's OK, since we just did that above.)
  130. pth, err := container.HostConfigPath()
  131. if err != nil {
  132. return err
  133. }
  134. f, err := os.Open(pth)
  135. if err != nil {
  136. if os.IsNotExist(err) {
  137. return nil
  138. }
  139. return err
  140. }
  141. defer f.Close()
  142. if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil {
  143. return err
  144. }
  145. container.InitDNSHostConfig()
  146. return nil
  147. }
  148. // WriteHostConfig saves the host configuration on disk for the container.
  149. func (container *Container) WriteHostConfig() error {
  150. pth, err := container.HostConfigPath()
  151. if err != nil {
  152. return err
  153. }
  154. f, err := os.Create(pth)
  155. if err != nil {
  156. return err
  157. }
  158. defer f.Close()
  159. return json.NewEncoder(f).Encode(&container.HostConfig)
  160. }
  161. // GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path
  162. // sanitisation. Symlinks are all scoped to the BaseFS of the container, as
  163. // though the container's BaseFS was `/`.
  164. //
  165. // The BaseFS of a container is the host-facing path which is bind-mounted as
  166. // `/` inside the container. This method is essentially used to access a
  167. // particular path inside the container as though you were a process in that
  168. // container.
  169. //
  170. // NOTE: The returned path is *only* safely scoped inside the container's BaseFS
  171. // if no component of the returned path changes (such as a component
  172. // symlinking to a different path) between using this method and using the
  173. // path. See symlink.FollowSymlinkInScope for more details.
  174. func (container *Container) GetResourcePath(path string) (string, error) {
  175. // IMPORTANT - These are paths on the OS where the daemon is running, hence
  176. // any filepath operations must be done in an OS agnostic way.
  177. cleanPath := filepath.Join(string(os.PathSeparator), path)
  178. r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS)
  179. return r, e
  180. }
  181. // GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path
  182. // sanitisation. Symlinks are all scoped to the root of the container, as
  183. // though the container's root was `/`.
  184. //
  185. // The root of a container is the host-facing configuration metadata directory.
  186. // Only use this method to safely access the container's `container.json` or
  187. // other metadata files. If in doubt, use container.GetResourcePath.
  188. //
  189. // NOTE: The returned path is *only* safely scoped inside the container's root
  190. // if no component of the returned path changes (such as a component
  191. // symlinking to a different path) between using this method and using the
  192. // path. See symlink.FollowSymlinkInScope for more details.
  193. func (container *Container) GetRootResourcePath(path string) (string, error) {
  194. // IMPORTANT - These are paths on the OS where the daemon is running, hence
  195. // any filepath operations must be done in an OS agnostic way.
  196. cleanPath := filepath.Join(string(os.PathSeparator), path)
  197. return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root)
  198. }
  199. // ExitOnNext signals to the monitor that it should not restart the container
  200. // after we send the kill signal.
  201. func (container *Container) ExitOnNext() {
  202. container.monitor.ExitOnNext()
  203. }
  204. // Resize changes the TTY of the process running inside the container
  205. // to the given height and width. The container must be running.
  206. func (container *Container) Resize(h, w int) error {
  207. if err := container.Command.ProcessConfig.Terminal.Resize(h, w); err != nil {
  208. return err
  209. }
  210. return nil
  211. }
  212. // HostConfigPath returns the path to the container's JSON hostconfig
  213. func (container *Container) HostConfigPath() (string, error) {
  214. return container.GetRootResourcePath("hostconfig.json")
  215. }
  216. // ConfigPath returns the path to the container's JSON config
  217. func (container *Container) ConfigPath() (string, error) {
  218. return container.GetRootResourcePath(configFileName)
  219. }
  220. func validateID(id string) error {
  221. if id == "" {
  222. return derr.ErrorCodeEmptyID
  223. }
  224. return nil
  225. }
  226. // Returns true if the container exposes a certain port
  227. func (container *Container) exposes(p nat.Port) bool {
  228. _, exists := container.Config.ExposedPorts[p]
  229. return exists
  230. }
  231. // GetLogConfig returns the log configuration for the container.
  232. func (container *Container) GetLogConfig(defaultConfig containertypes.LogConfig) containertypes.LogConfig {
  233. cfg := container.HostConfig.LogConfig
  234. if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
  235. if cfg.Type == "" {
  236. cfg.Type = jsonfilelog.Name
  237. }
  238. return cfg
  239. }
  240. // Use daemon's default log config for containers
  241. return defaultConfig
  242. }
  243. // StartLogger starts a new logger driver for the container.
  244. func (container *Container) StartLogger(cfg containertypes.LogConfig) (logger.Logger, error) {
  245. c, err := logger.GetLogDriver(cfg.Type)
  246. if err != nil {
  247. return nil, derr.ErrorCodeLoggingFactory.WithArgs(err)
  248. }
  249. ctx := logger.Context{
  250. Config: cfg.Config,
  251. ContainerID: container.ID,
  252. ContainerName: container.Name,
  253. ContainerEntrypoint: container.Path,
  254. ContainerArgs: container.Args,
  255. ContainerImageID: container.ImageID.String(),
  256. ContainerImageName: container.Config.Image,
  257. ContainerCreated: container.Created,
  258. ContainerEnv: container.Config.Env,
  259. ContainerLabels: container.Config.Labels,
  260. }
  261. // Set logging file for "json-logger"
  262. if cfg.Type == jsonfilelog.Name {
  263. ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID))
  264. if err != nil {
  265. return nil, err
  266. }
  267. }
  268. return c(ctx)
  269. }
  270. // GetProcessLabel returns the process label for the container.
  271. func (container *Container) GetProcessLabel() string {
  272. // even if we have a process label return "" if we are running
  273. // in privileged mode
  274. if container.HostConfig.Privileged {
  275. return ""
  276. }
  277. return container.ProcessLabel
  278. }
  279. // GetMountLabel returns the mounting label for the container.
  280. // This label is empty if the container is privileged.
  281. func (container *Container) GetMountLabel() string {
  282. if container.HostConfig.Privileged {
  283. return ""
  284. }
  285. return container.MountLabel
  286. }
  287. // GetExecIDs returns the list of exec commands running on the container.
  288. func (container *Container) GetExecIDs() []string {
  289. return container.ExecCommands.List()
  290. }
  291. // Attach connects to the container's TTY, delegating to standard
  292. // streams or websockets depending on the configuration.
  293. func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
  294. return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys)
  295. }
  296. // AttachStreams connects streams to a TTY.
  297. // Used by exec too. Should this move somewhere else?
  298. func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
  299. var (
  300. cStdout, cStderr io.ReadCloser
  301. cStdin io.WriteCloser
  302. wg sync.WaitGroup
  303. errors = make(chan error, 3)
  304. )
  305. if stdin != nil && openStdin {
  306. cStdin = streamConfig.StdinPipe()
  307. wg.Add(1)
  308. }
  309. if stdout != nil {
  310. cStdout = streamConfig.StdoutPipe()
  311. wg.Add(1)
  312. }
  313. if stderr != nil {
  314. cStderr = streamConfig.StderrPipe()
  315. wg.Add(1)
  316. }
  317. // Connect stdin of container to the http conn.
  318. go func() {
  319. if stdin == nil || !openStdin {
  320. return
  321. }
  322. logrus.Debugf("attach: stdin: begin")
  323. defer func() {
  324. if stdinOnce && !tty {
  325. cStdin.Close()
  326. } else {
  327. // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
  328. if cStdout != nil {
  329. cStdout.Close()
  330. }
  331. if cStderr != nil {
  332. cStderr.Close()
  333. }
  334. }
  335. wg.Done()
  336. logrus.Debugf("attach: stdin: end")
  337. }()
  338. var err error
  339. if tty {
  340. _, err = copyEscapable(cStdin, stdin, keys)
  341. } else {
  342. _, err = io.Copy(cStdin, stdin)
  343. }
  344. if err == io.ErrClosedPipe {
  345. err = nil
  346. }
  347. if err != nil {
  348. logrus.Errorf("attach: stdin: %s", err)
  349. errors <- err
  350. return
  351. }
  352. }()
  353. attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) {
  354. if stream == nil {
  355. return
  356. }
  357. defer func() {
  358. // Make sure stdin gets closed
  359. if stdin != nil {
  360. stdin.Close()
  361. }
  362. streamPipe.Close()
  363. wg.Done()
  364. logrus.Debugf("attach: %s: end", name)
  365. }()
  366. logrus.Debugf("attach: %s: begin", name)
  367. _, err := io.Copy(stream, streamPipe)
  368. if err == io.ErrClosedPipe {
  369. err = nil
  370. }
  371. if err != nil {
  372. logrus.Errorf("attach: %s: %v", name, err)
  373. errors <- err
  374. }
  375. }
  376. go attachStream("stdout", stdout, cStdout)
  377. go attachStream("stderr", stderr, cStderr)
  378. return promise.Go(func() error {
  379. wg.Wait()
  380. close(errors)
  381. for err := range errors {
  382. if err != nil {
  383. return err
  384. }
  385. }
  386. return nil
  387. })
  388. }
  389. // Code c/c from io.Copy() modified to handle escape sequence
  390. func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) {
  391. if len(keys) == 0 {
  392. // Default keys : ctrl-p ctrl-q
  393. keys = []byte{16, 17}
  394. }
  395. buf := make([]byte, 32*1024)
  396. for {
  397. nr, er := src.Read(buf)
  398. if nr > 0 {
  399. // ---- Docker addition
  400. for i, key := range keys {
  401. if nr != 1 || buf[0] != key {
  402. break
  403. }
  404. if i == len(keys)-1 {
  405. if err := src.Close(); err != nil {
  406. return 0, err
  407. }
  408. return 0, nil
  409. }
  410. nr, er = src.Read(buf)
  411. }
  412. // ---- End of docker
  413. nw, ew := dst.Write(buf[0:nr])
  414. if nw > 0 {
  415. written += int64(nw)
  416. }
  417. if ew != nil {
  418. err = ew
  419. break
  420. }
  421. if nr != nw {
  422. err = io.ErrShortWrite
  423. break
  424. }
  425. }
  426. if er == io.EOF {
  427. break
  428. }
  429. if er != nil {
  430. err = er
  431. break
  432. }
  433. }
  434. return written, err
  435. }
  436. // ShouldRestart decides whether the daemon should restart the container or not.
  437. // This is based on the container's restart policy.
  438. func (container *Container) ShouldRestart() bool {
  439. return container.HostConfig.RestartPolicy.Name == "always" ||
  440. (container.HostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
  441. (container.HostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
  442. }
  443. // AddBindMountPoint adds a new bind mount point configuration to the container.
  444. func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) {
  445. container.MountPoints[destination] = &volume.MountPoint{
  446. Name: name,
  447. Source: source,
  448. Destination: destination,
  449. RW: rw,
  450. }
  451. }
  452. // AddLocalMountPoint adds a new local mount point configuration to the container.
  453. func (container *Container) AddLocalMountPoint(name, destination string, rw bool) {
  454. container.MountPoints[destination] = &volume.MountPoint{
  455. Name: name,
  456. Driver: volume.DefaultDriverName,
  457. Destination: destination,
  458. RW: rw,
  459. }
  460. }
  461. // AddMountPointWithVolume adds a new mount point configured with a volume to the container.
  462. func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
  463. container.MountPoints[destination] = &volume.MountPoint{
  464. Name: vol.Name(),
  465. Driver: vol.DriverName(),
  466. Destination: destination,
  467. RW: rw,
  468. Volume: vol,
  469. }
  470. }
  471. // IsDestinationMounted checks whether a path is mounted on the container or not.
  472. func (container *Container) IsDestinationMounted(destination string) bool {
  473. return container.MountPoints[destination] != nil
  474. }
  475. // StopSignal returns the signal used to stop the container.
  476. func (container *Container) StopSignal() int {
  477. var stopSignal syscall.Signal
  478. if container.Config.StopSignal != "" {
  479. stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
  480. }
  481. if int(stopSignal) == 0 {
  482. stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal)
  483. }
  484. return int(stopSignal)
  485. }
  486. // InitDNSHostConfig ensures that the dns fields are never nil.
  487. // New containers don't ever have those fields nil,
  488. // but pre created containers can still have those nil values.
  489. // The non-recommended host configuration in the start api can
  490. // make these fields nil again, this corrects that issue until
  491. // we remove that behavior for good.
  492. // See https://github.com/docker/docker/pull/17779
  493. // for a more detailed explanation on why we don't want that.
  494. func (container *Container) InitDNSHostConfig() {
  495. container.Lock()
  496. defer container.Unlock()
  497. if container.HostConfig.DNS == nil {
  498. container.HostConfig.DNS = make([]string, 0)
  499. }
  500. if container.HostConfig.DNSSearch == nil {
  501. container.HostConfig.DNSSearch = make([]string, 0)
  502. }
  503. if container.HostConfig.DNSOptions == nil {
  504. container.HostConfig.DNSOptions = make([]string, 0)
  505. }
  506. }