container.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089
  1. package docker
  2. import (
  3. "encoding/json"
  4. "flag"
  5. "fmt"
  6. "github.com/dotcloud/docker/term"
  7. "github.com/dotcloud/docker/utils"
  8. "github.com/kr/pty"
  9. "io"
  10. "io/ioutil"
  11. "log"
  12. "os"
  13. "os/exec"
  14. "path"
  15. "path/filepath"
  16. "sort"
  17. "strconv"
  18. "strings"
  19. "syscall"
  20. "time"
  21. )
  22. type Container struct {
  23. root string
  24. ID string
  25. Created time.Time
  26. Path string
  27. Args []string
  28. Config *Config
  29. State State
  30. Image string
  31. network *NetworkInterface
  32. NetworkSettings *NetworkSettings
  33. SysInitPath string
  34. ResolvConfPath string
  35. cmd *exec.Cmd
  36. stdout *utils.WriteBroadcaster
  37. stderr *utils.WriteBroadcaster
  38. stdin io.ReadCloser
  39. stdinPipe io.WriteCloser
  40. ptyMaster io.Closer
  41. runtime *Runtime
  42. waitLock chan struct{}
  43. Volumes map[string]string
  44. // Store rw/ro in a separate structure to preserve reserve-compatibility on-disk.
  45. // Easier than migrating older container configs :)
  46. VolumesRW map[string]bool
  47. }
  48. type Config struct {
  49. Hostname string
  50. User string
  51. Memory int64 // Memory limit (in bytes)
  52. MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
  53. CpuShares int64 // CPU shares (relative weight vs. other containers)
  54. AttachStdin bool
  55. AttachStdout bool
  56. AttachStderr bool
  57. PortSpecs []string
  58. Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
  59. OpenStdin bool // Open stdin
  60. StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
  61. Env []string
  62. Cmd []string
  63. Dns []string
  64. Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
  65. Volumes map[string]struct{}
  66. VolumesFrom string
  67. Entrypoint []string
  68. NetworkDisabled bool
  69. }
  70. type HostConfig struct {
  71. Binds []string
  72. ContainerIDFile string
  73. }
  74. type BindMap struct {
  75. SrcPath string
  76. DstPath string
  77. Mode string
  78. }
  79. func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
  80. cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
  81. if len(args) > 0 && args[0] != "--help" {
  82. cmd.SetOutput(ioutil.Discard)
  83. cmd.Usage = nil
  84. }
  85. flHostname := cmd.String("h", "", "Container host name")
  86. flUser := cmd.String("u", "", "Username or UID")
  87. flDetach := cmd.Bool("d", false, "Detached mode: leave the container running in the background")
  88. flAttach := NewAttachOpts()
  89. cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.")
  90. flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
  91. flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
  92. flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
  93. flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file")
  94. flNetwork := cmd.Bool("n", true, "Enable networking for this container")
  95. if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
  96. //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
  97. *flMemory = 0
  98. }
  99. flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
  100. var flPorts ListOpts
  101. cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
  102. var flEnv ListOpts
  103. cmd.Var(&flEnv, "e", "Set environment variables")
  104. var flDns ListOpts
  105. cmd.Var(&flDns, "dns", "Set custom dns servers")
  106. flVolumes := NewPathOpts()
  107. cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
  108. flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
  109. flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
  110. if err := cmd.Parse(args); err != nil {
  111. return nil, nil, cmd, err
  112. }
  113. if *flDetach && len(flAttach) > 0 {
  114. return nil, nil, cmd, fmt.Errorf("Conflicting options: -a and -d")
  115. }
  116. // If neither -d or -a are set, attach to everything by default
  117. if len(flAttach) == 0 && !*flDetach {
  118. if !*flDetach {
  119. flAttach.Set("stdout")
  120. flAttach.Set("stderr")
  121. if *flStdin {
  122. flAttach.Set("stdin")
  123. }
  124. }
  125. }
  126. var binds []string
  127. // add any bind targets to the list of container volumes
  128. for bind := range flVolumes {
  129. arr := strings.Split(bind, ":")
  130. if len(arr) > 1 {
  131. dstDir := arr[1]
  132. flVolumes[dstDir] = struct{}{}
  133. binds = append(binds, bind)
  134. delete(flVolumes, bind)
  135. }
  136. }
  137. parsedArgs := cmd.Args()
  138. runCmd := []string{}
  139. entrypoint := []string{}
  140. image := ""
  141. if len(parsedArgs) >= 1 {
  142. image = cmd.Arg(0)
  143. }
  144. if len(parsedArgs) > 1 {
  145. runCmd = parsedArgs[1:]
  146. }
  147. if *flEntrypoint != "" {
  148. entrypoint = []string{*flEntrypoint}
  149. }
  150. config := &Config{
  151. Hostname: *flHostname,
  152. PortSpecs: flPorts,
  153. User: *flUser,
  154. Tty: *flTty,
  155. NetworkDisabled: !*flNetwork,
  156. OpenStdin: *flStdin,
  157. Memory: *flMemory,
  158. CpuShares: *flCpuShares,
  159. AttachStdin: flAttach.Get("stdin"),
  160. AttachStdout: flAttach.Get("stdout"),
  161. AttachStderr: flAttach.Get("stderr"),
  162. Env: flEnv,
  163. Cmd: runCmd,
  164. Dns: flDns,
  165. Image: image,
  166. Volumes: flVolumes,
  167. VolumesFrom: *flVolumesFrom,
  168. Entrypoint: entrypoint,
  169. }
  170. hostConfig := &HostConfig{
  171. Binds: binds,
  172. ContainerIDFile: *flContainerIDFile,
  173. }
  174. if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
  175. //fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
  176. config.MemorySwap = -1
  177. }
  178. // When allocating stdin in attached mode, close stdin at client disconnect
  179. if config.OpenStdin && config.AttachStdin {
  180. config.StdinOnce = true
  181. }
  182. return config, hostConfig, cmd, nil
  183. }
  184. type PortMapping map[string]string
  185. type NetworkSettings struct {
  186. IPAddress string
  187. IPPrefixLen int
  188. Gateway string
  189. Bridge string
  190. PortMapping map[string]PortMapping
  191. }
  192. // String returns a human-readable description of the port mapping defined in the settings
  193. func (settings *NetworkSettings) PortMappingHuman() string {
  194. var mapping []string
  195. for private, public := range settings.PortMapping["Tcp"] {
  196. mapping = append(mapping, fmt.Sprintf("%s->%s", public, private))
  197. }
  198. for private, public := range settings.PortMapping["Udp"] {
  199. mapping = append(mapping, fmt.Sprintf("%s->%s/udp", public, private))
  200. }
  201. sort.Strings(mapping)
  202. return strings.Join(mapping, ", ")
  203. }
  204. // Inject the io.Reader at the given path. Note: do not close the reader
  205. func (container *Container) Inject(file io.Reader, pth string) error {
  206. // Make sure the directory exists
  207. if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
  208. return err
  209. }
  210. // FIXME: Handle permissions/already existing dest
  211. dest, err := os.Create(path.Join(container.rwPath(), pth))
  212. if err != nil {
  213. return err
  214. }
  215. if _, err := io.Copy(dest, file); err != nil {
  216. return err
  217. }
  218. return nil
  219. }
  220. func (container *Container) Cmd() *exec.Cmd {
  221. return container.cmd
  222. }
  223. func (container *Container) When() time.Time {
  224. return container.Created
  225. }
  226. func (container *Container) FromDisk() error {
  227. data, err := ioutil.ReadFile(container.jsonPath())
  228. if err != nil {
  229. return err
  230. }
  231. // Load container settings
  232. if err := json.Unmarshal(data, container); err != nil {
  233. return err
  234. }
  235. return nil
  236. }
  237. func (container *Container) ToDisk() (err error) {
  238. data, err := json.Marshal(container)
  239. if err != nil {
  240. return
  241. }
  242. return ioutil.WriteFile(container.jsonPath(), data, 0666)
  243. }
  244. func (container *Container) ReadHostConfig() (*HostConfig, error) {
  245. data, err := ioutil.ReadFile(container.hostConfigPath())
  246. if err != nil {
  247. return &HostConfig{}, err
  248. }
  249. hostConfig := &HostConfig{}
  250. if err := json.Unmarshal(data, hostConfig); err != nil {
  251. return &HostConfig{}, err
  252. }
  253. return hostConfig, nil
  254. }
  255. func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) {
  256. data, err := json.Marshal(hostConfig)
  257. if err != nil {
  258. return
  259. }
  260. return ioutil.WriteFile(container.hostConfigPath(), data, 0666)
  261. }
  262. func (container *Container) generateLXCConfig() error {
  263. fo, err := os.Create(container.lxcConfigPath())
  264. if err != nil {
  265. return err
  266. }
  267. defer fo.Close()
  268. if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
  269. return err
  270. }
  271. return nil
  272. }
  273. func (container *Container) startPty() error {
  274. ptyMaster, ptySlave, err := pty.Open()
  275. if err != nil {
  276. return err
  277. }
  278. container.ptyMaster = ptyMaster
  279. container.cmd.Stdout = ptySlave
  280. container.cmd.Stderr = ptySlave
  281. // Copy the PTYs to our broadcasters
  282. go func() {
  283. defer container.stdout.CloseWriters()
  284. utils.Debugf("[startPty] Begin of stdout pipe")
  285. io.Copy(container.stdout, ptyMaster)
  286. utils.Debugf("[startPty] End of stdout pipe")
  287. }()
  288. // stdin
  289. if container.Config.OpenStdin {
  290. container.cmd.Stdin = ptySlave
  291. container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
  292. go func() {
  293. defer container.stdin.Close()
  294. utils.Debugf("[startPty] Begin of stdin pipe")
  295. io.Copy(ptyMaster, container.stdin)
  296. utils.Debugf("[startPty] End of stdin pipe")
  297. }()
  298. }
  299. if err := container.cmd.Start(); err != nil {
  300. return err
  301. }
  302. ptySlave.Close()
  303. return nil
  304. }
  305. func (container *Container) start() error {
  306. container.cmd.Stdout = container.stdout
  307. container.cmd.Stderr = container.stderr
  308. if container.Config.OpenStdin {
  309. stdin, err := container.cmd.StdinPipe()
  310. if err != nil {
  311. return err
  312. }
  313. go func() {
  314. defer stdin.Close()
  315. utils.Debugf("Begin of stdin pipe [start]")
  316. io.Copy(stdin, container.stdin)
  317. utils.Debugf("End of stdin pipe [start]")
  318. }()
  319. }
  320. return container.cmd.Start()
  321. }
  322. func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error {
  323. var cStdout, cStderr io.ReadCloser
  324. var nJobs int
  325. errors := make(chan error, 3)
  326. if stdin != nil && container.Config.OpenStdin {
  327. nJobs += 1
  328. if cStdin, err := container.StdinPipe(); err != nil {
  329. errors <- err
  330. } else {
  331. go func() {
  332. utils.Debugf("[start] attach stdin\n")
  333. defer utils.Debugf("[end] attach stdin\n")
  334. // No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
  335. if container.Config.StdinOnce && !container.Config.Tty {
  336. defer cStdin.Close()
  337. } else {
  338. if cStdout != nil {
  339. defer cStdout.Close()
  340. }
  341. if cStderr != nil {
  342. defer cStderr.Close()
  343. }
  344. }
  345. if container.Config.Tty {
  346. _, err = utils.CopyEscapable(cStdin, stdin)
  347. } else {
  348. _, err = io.Copy(cStdin, stdin)
  349. }
  350. if err != nil {
  351. utils.Debugf("[error] attach stdin: %s\n", err)
  352. }
  353. // Discard error, expecting pipe error
  354. errors <- nil
  355. }()
  356. }
  357. }
  358. if stdout != nil {
  359. nJobs += 1
  360. if p, err := container.StdoutPipe(); err != nil {
  361. errors <- err
  362. } else {
  363. cStdout = p
  364. go func() {
  365. utils.Debugf("[start] attach stdout\n")
  366. defer utils.Debugf("[end] attach stdout\n")
  367. // If we are in StdinOnce mode, then close stdin
  368. if container.Config.StdinOnce {
  369. if stdin != nil {
  370. defer stdin.Close()
  371. }
  372. if stdinCloser != nil {
  373. defer stdinCloser.Close()
  374. }
  375. }
  376. _, err := io.Copy(stdout, cStdout)
  377. if err != nil {
  378. utils.Debugf("[error] attach stdout: %s\n", err)
  379. }
  380. errors <- err
  381. }()
  382. }
  383. } else {
  384. go func() {
  385. if stdinCloser != nil {
  386. defer stdinCloser.Close()
  387. }
  388. if cStdout, err := container.StdoutPipe(); err != nil {
  389. utils.Debugf("Error stdout pipe")
  390. } else {
  391. io.Copy(&utils.NopWriter{}, cStdout)
  392. }
  393. }()
  394. }
  395. if stderr != nil {
  396. nJobs += 1
  397. if p, err := container.StderrPipe(); err != nil {
  398. errors <- err
  399. } else {
  400. cStderr = p
  401. go func() {
  402. utils.Debugf("[start] attach stderr\n")
  403. defer utils.Debugf("[end] attach stderr\n")
  404. // If we are in StdinOnce mode, then close stdin
  405. if container.Config.StdinOnce {
  406. if stdin != nil {
  407. defer stdin.Close()
  408. }
  409. if stdinCloser != nil {
  410. defer stdinCloser.Close()
  411. }
  412. }
  413. _, err := io.Copy(stderr, cStderr)
  414. if err != nil {
  415. utils.Debugf("[error] attach stderr: %s\n", err)
  416. }
  417. errors <- err
  418. }()
  419. }
  420. } else {
  421. go func() {
  422. if stdinCloser != nil {
  423. defer stdinCloser.Close()
  424. }
  425. if cStderr, err := container.StderrPipe(); err != nil {
  426. utils.Debugf("Error stdout pipe")
  427. } else {
  428. io.Copy(&utils.NopWriter{}, cStderr)
  429. }
  430. }()
  431. }
  432. return utils.Go(func() error {
  433. if cStdout != nil {
  434. defer cStdout.Close()
  435. }
  436. if cStderr != nil {
  437. defer cStderr.Close()
  438. }
  439. // FIXME: how do clean up the stdin goroutine without the unwanted side effect
  440. // of closing the passed stdin? Add an intermediary io.Pipe?
  441. for i := 0; i < nJobs; i += 1 {
  442. utils.Debugf("Waiting for job %d/%d\n", i+1, nJobs)
  443. if err := <-errors; err != nil {
  444. utils.Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err)
  445. return err
  446. }
  447. utils.Debugf("Job %d completed successfully\n", i+1)
  448. }
  449. utils.Debugf("All jobs completed successfully\n")
  450. return nil
  451. })
  452. }
  453. func (container *Container) Start(hostConfig *HostConfig) error {
  454. container.State.Lock()
  455. defer container.State.Unlock()
  456. if len(hostConfig.Binds) == 0 {
  457. hostConfig, _ = container.ReadHostConfig()
  458. }
  459. if container.State.Running {
  460. return fmt.Errorf("The container %s is already running.", container.ID)
  461. }
  462. if err := container.EnsureMounted(); err != nil {
  463. return err
  464. }
  465. if container.runtime.networkManager.disabled {
  466. container.Config.NetworkDisabled = true
  467. } else {
  468. if err := container.allocateNetwork(); err != nil {
  469. return err
  470. }
  471. }
  472. // Make sure the config is compatible with the current kernel
  473. if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
  474. log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
  475. container.Config.Memory = 0
  476. }
  477. if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
  478. log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
  479. container.Config.MemorySwap = -1
  480. }
  481. // Create the requested bind mounts
  482. binds := make(map[string]BindMap)
  483. // Define illegal container destinations
  484. illegalDsts := []string{"/", "."}
  485. for _, bind := range hostConfig.Binds {
  486. // FIXME: factorize bind parsing in parseBind
  487. var src, dst, mode string
  488. arr := strings.Split(bind, ":")
  489. if len(arr) == 2 {
  490. src = arr[0]
  491. dst = arr[1]
  492. mode = "rw"
  493. } else if len(arr) == 3 {
  494. src = arr[0]
  495. dst = arr[1]
  496. mode = arr[2]
  497. } else {
  498. return fmt.Errorf("Invalid bind specification: %s", bind)
  499. }
  500. // Bail if trying to mount to an illegal destination
  501. for _, illegal := range illegalDsts {
  502. if dst == illegal {
  503. return fmt.Errorf("Illegal bind destination: %s", dst)
  504. }
  505. }
  506. bindMap := BindMap{
  507. SrcPath: src,
  508. DstPath: dst,
  509. Mode: mode,
  510. }
  511. binds[path.Clean(dst)] = bindMap
  512. }
  513. // FIXME: evaluate volumes-from before individual volumes, so that the latter can override the former.
  514. // Create the requested volumes volumes
  515. if container.Volumes == nil || len(container.Volumes) == 0 {
  516. container.Volumes = make(map[string]string)
  517. container.VolumesRW = make(map[string]bool)
  518. for volPath := range container.Config.Volumes {
  519. volPath = path.Clean(volPath)
  520. // If an external bind is defined for this volume, use that as a source
  521. if bindMap, exists := binds[volPath]; exists {
  522. container.Volumes[volPath] = bindMap.SrcPath
  523. if strings.ToLower(bindMap.Mode) == "rw" {
  524. container.VolumesRW[volPath] = true
  525. }
  526. // Otherwise create an directory in $ROOT/volumes/ and use that
  527. } else {
  528. c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
  529. if err != nil {
  530. return err
  531. }
  532. srcPath, err := c.layer()
  533. if err != nil {
  534. return err
  535. }
  536. container.Volumes[volPath] = srcPath
  537. container.VolumesRW[volPath] = true // RW by default
  538. }
  539. // Create the mountpoint
  540. if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
  541. return nil
  542. }
  543. }
  544. }
  545. if container.Config.VolumesFrom != "" {
  546. c := container.runtime.Get(container.Config.VolumesFrom)
  547. if c == nil {
  548. return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
  549. }
  550. for volPath, id := range c.Volumes {
  551. if _, exists := container.Volumes[volPath]; exists {
  552. return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID)
  553. }
  554. if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
  555. return nil
  556. }
  557. container.Volumes[volPath] = id
  558. if isRW, exists := c.VolumesRW[volPath]; exists {
  559. container.VolumesRW[volPath] = isRW
  560. }
  561. }
  562. }
  563. if err := container.generateLXCConfig(); err != nil {
  564. return err
  565. }
  566. params := []string{
  567. "-n", container.ID,
  568. "-f", container.lxcConfigPath(),
  569. "--",
  570. "/sbin/init",
  571. }
  572. // Networking
  573. if !container.Config.NetworkDisabled {
  574. params = append(params, "-g", container.network.Gateway.String())
  575. }
  576. // User
  577. if container.Config.User != "" {
  578. params = append(params, "-u", container.Config.User)
  579. }
  580. if container.Config.Tty {
  581. params = append(params, "-e", "TERM=xterm")
  582. }
  583. // Setup environment
  584. params = append(params,
  585. "-e", "HOME=/",
  586. "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  587. "-e", "container=lxc",
  588. )
  589. for _, elem := range container.Config.Env {
  590. params = append(params, "-e", elem)
  591. }
  592. // Program
  593. params = append(params, "--", container.Path)
  594. params = append(params, container.Args...)
  595. container.cmd = exec.Command("lxc-start", params...)
  596. // Setup logging of stdout and stderr to disk
  597. if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
  598. return err
  599. }
  600. if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
  601. return err
  602. }
  603. var err error
  604. if container.Config.Tty {
  605. err = container.startPty()
  606. } else {
  607. err = container.start()
  608. }
  609. if err != nil {
  610. return err
  611. }
  612. // FIXME: save state on disk *first*, then converge
  613. // this way disk state is used as a journal, eg. we can restore after crash etc.
  614. container.State.setRunning(container.cmd.Process.Pid)
  615. // Init the lock
  616. container.waitLock = make(chan struct{})
  617. container.ToDisk()
  618. container.SaveHostConfig(hostConfig)
  619. go container.monitor()
  620. return nil
  621. }
  622. func (container *Container) Run() error {
  623. hostConfig := &HostConfig{}
  624. if err := container.Start(hostConfig); err != nil {
  625. return err
  626. }
  627. container.Wait()
  628. return nil
  629. }
  630. func (container *Container) Output() (output []byte, err error) {
  631. pipe, err := container.StdoutPipe()
  632. if err != nil {
  633. return nil, err
  634. }
  635. defer pipe.Close()
  636. hostConfig := &HostConfig{}
  637. if err := container.Start(hostConfig); err != nil {
  638. return nil, err
  639. }
  640. output, err = ioutil.ReadAll(pipe)
  641. container.Wait()
  642. return output, err
  643. }
  644. // StdinPipe() returns a pipe connected to the standard input of the container's
  645. // active process.
  646. //
  647. func (container *Container) StdinPipe() (io.WriteCloser, error) {
  648. return container.stdinPipe, nil
  649. }
  650. func (container *Container) StdoutPipe() (io.ReadCloser, error) {
  651. reader, writer := io.Pipe()
  652. container.stdout.AddWriter(writer, "")
  653. return utils.NewBufReader(reader), nil
  654. }
  655. func (container *Container) StderrPipe() (io.ReadCloser, error) {
  656. reader, writer := io.Pipe()
  657. container.stderr.AddWriter(writer, "")
  658. return utils.NewBufReader(reader), nil
  659. }
  660. func (container *Container) allocateNetwork() error {
  661. if container.Config.NetworkDisabled {
  662. return nil
  663. }
  664. iface, err := container.runtime.networkManager.Allocate()
  665. if err != nil {
  666. return err
  667. }
  668. container.NetworkSettings.PortMapping = make(map[string]PortMapping)
  669. container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping)
  670. container.NetworkSettings.PortMapping["Udp"] = make(PortMapping)
  671. for _, spec := range container.Config.PortSpecs {
  672. nat, err := iface.AllocatePort(spec)
  673. if err != nil {
  674. iface.Release()
  675. return err
  676. }
  677. proto := strings.Title(nat.Proto)
  678. backend, frontend := strconv.Itoa(nat.Backend), strconv.Itoa(nat.Frontend)
  679. container.NetworkSettings.PortMapping[proto][backend] = frontend
  680. }
  681. container.network = iface
  682. container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
  683. container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
  684. container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
  685. container.NetworkSettings.Gateway = iface.Gateway.String()
  686. return nil
  687. }
  688. func (container *Container) releaseNetwork() {
  689. if container.Config.NetworkDisabled {
  690. return
  691. }
  692. container.network.Release()
  693. container.network = nil
  694. container.NetworkSettings = &NetworkSettings{}
  695. }
  696. // FIXME: replace this with a control socket within docker-init
  697. func (container *Container) waitLxc() error {
  698. for {
  699. output, err := exec.Command("lxc-info", "-n", container.ID).CombinedOutput()
  700. if err != nil {
  701. return err
  702. }
  703. if !strings.Contains(string(output), "RUNNING") {
  704. return nil
  705. }
  706. time.Sleep(500 * time.Millisecond)
  707. }
  708. }
  709. func (container *Container) monitor() {
  710. // Wait for the program to exit
  711. utils.Debugf("Waiting for process")
  712. // If the command does not exists, try to wait via lxc
  713. if container.cmd == nil {
  714. if err := container.waitLxc(); err != nil {
  715. utils.Debugf("%s: Process: %s", container.ID, err)
  716. }
  717. } else {
  718. if err := container.cmd.Wait(); err != nil {
  719. // Discard the error as any signals or non 0 returns will generate an error
  720. utils.Debugf("%s: Process: %s", container.ID, err)
  721. }
  722. }
  723. utils.Debugf("Process finished")
  724. if container.runtime != nil && container.runtime.srv != nil {
  725. container.runtime.srv.LogEvent("die", container.ShortID())
  726. }
  727. exitCode := -1
  728. if container.cmd != nil {
  729. exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
  730. }
  731. // Cleanup
  732. container.releaseNetwork()
  733. if container.Config.OpenStdin {
  734. if err := container.stdin.Close(); err != nil {
  735. utils.Debugf("%s: Error close stdin: %s", container.ID, err)
  736. }
  737. }
  738. if err := container.stdout.CloseWriters(); err != nil {
  739. utils.Debugf("%s: Error close stdout: %s", container.ID, err)
  740. }
  741. if err := container.stderr.CloseWriters(); err != nil {
  742. utils.Debugf("%s: Error close stderr: %s", container.ID, err)
  743. }
  744. if container.ptyMaster != nil {
  745. if err := container.ptyMaster.Close(); err != nil {
  746. utils.Debugf("%s: Error closing Pty master: %s", container.ID, err)
  747. }
  748. }
  749. if err := container.Unmount(); err != nil {
  750. log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
  751. }
  752. // Re-create a brand new stdin pipe once the container exited
  753. if container.Config.OpenStdin {
  754. container.stdin, container.stdinPipe = io.Pipe()
  755. }
  756. // Report status back
  757. container.State.setStopped(exitCode)
  758. // Release the lock
  759. close(container.waitLock)
  760. if err := container.ToDisk(); err != nil {
  761. // FIXME: there is a race condition here which causes this to fail during the unit tests.
  762. // If another goroutine was waiting for Wait() to return before removing the container's root
  763. // from the filesystem... At this point it may already have done so.
  764. // This is because State.setStopped() has already been called, and has caused Wait()
  765. // to return.
  766. // FIXME: why are we serializing running state to disk in the first place?
  767. //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
  768. }
  769. }
  770. func (container *Container) kill() error {
  771. if !container.State.Running {
  772. return nil
  773. }
  774. // Sending SIGKILL to the process via lxc
  775. output, err := exec.Command("lxc-kill", "-n", container.ID, "9").CombinedOutput()
  776. if err != nil {
  777. log.Printf("error killing container %s (%s, %s)", container.ID, output, err)
  778. }
  779. // 2. Wait for the process to die, in last resort, try to kill the process directly
  780. if err := container.WaitTimeout(10 * time.Second); err != nil {
  781. if container.cmd == nil {
  782. return fmt.Errorf("lxc-kill failed, impossible to kill the container %s", container.ID)
  783. }
  784. log.Printf("Container %s failed to exit within 10 seconds of lxc SIGKILL - trying direct SIGKILL", container.ID)
  785. if err := container.cmd.Process.Kill(); err != nil {
  786. return err
  787. }
  788. }
  789. // Wait for the container to be actually stopped
  790. container.Wait()
  791. return nil
  792. }
  793. func (container *Container) Kill() error {
  794. container.State.Lock()
  795. defer container.State.Unlock()
  796. if !container.State.Running {
  797. return nil
  798. }
  799. return container.kill()
  800. }
  801. func (container *Container) Stop(seconds int) error {
  802. container.State.Lock()
  803. defer container.State.Unlock()
  804. if !container.State.Running {
  805. return nil
  806. }
  807. // 1. Send a SIGTERM
  808. if output, err := exec.Command("lxc-kill", "-n", container.ID, "15").CombinedOutput(); err != nil {
  809. log.Print(string(output))
  810. log.Print("Failed to send SIGTERM to the process, force killing")
  811. if err := container.kill(); err != nil {
  812. return err
  813. }
  814. }
  815. // 2. Wait for the process to exit on its own
  816. if err := container.WaitTimeout(time.Duration(seconds) * time.Second); err != nil {
  817. log.Printf("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds)
  818. if err := container.kill(); err != nil {
  819. return err
  820. }
  821. }
  822. return nil
  823. }
  824. func (container *Container) Restart(seconds int) error {
  825. if err := container.Stop(seconds); err != nil {
  826. return err
  827. }
  828. hostConfig := &HostConfig{}
  829. if err := container.Start(hostConfig); err != nil {
  830. return err
  831. }
  832. return nil
  833. }
  834. // Wait blocks until the container stops running, then returns its exit code.
  835. func (container *Container) Wait() int {
  836. <-container.waitLock
  837. return container.State.ExitCode
  838. }
  839. func (container *Container) Resize(h, w int) error {
  840. pty, ok := container.ptyMaster.(*os.File)
  841. if !ok {
  842. return fmt.Errorf("ptyMaster does not have Fd() method")
  843. }
  844. return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)})
  845. }
  846. func (container *Container) ExportRw() (Archive, error) {
  847. return Tar(container.rwPath(), Uncompressed)
  848. }
  849. func (container *Container) RwChecksum() (string, error) {
  850. rwData, err := Tar(container.rwPath(), Xz)
  851. if err != nil {
  852. return "", err
  853. }
  854. return utils.HashData(rwData)
  855. }
  856. func (container *Container) Export() (Archive, error) {
  857. if err := container.EnsureMounted(); err != nil {
  858. return nil, err
  859. }
  860. return Tar(container.RootfsPath(), Uncompressed)
  861. }
  862. func (container *Container) WaitTimeout(timeout time.Duration) error {
  863. done := make(chan bool)
  864. go func() {
  865. container.Wait()
  866. done <- true
  867. }()
  868. select {
  869. case <-time.After(timeout):
  870. return fmt.Errorf("Timed Out")
  871. case <-done:
  872. return nil
  873. }
  874. }
  875. func (container *Container) EnsureMounted() error {
  876. if mounted, err := container.Mounted(); err != nil {
  877. return err
  878. } else if mounted {
  879. return nil
  880. }
  881. return container.Mount()
  882. }
  883. func (container *Container) Mount() error {
  884. image, err := container.GetImage()
  885. if err != nil {
  886. return err
  887. }
  888. return image.Mount(container.RootfsPath(), container.rwPath())
  889. }
  890. func (container *Container) Changes() ([]Change, error) {
  891. image, err := container.GetImage()
  892. if err != nil {
  893. return nil, err
  894. }
  895. return image.Changes(container.rwPath())
  896. }
  897. func (container *Container) GetImage() (*Image, error) {
  898. if container.runtime == nil {
  899. return nil, fmt.Errorf("Can't get image of unregistered container")
  900. }
  901. return container.runtime.graph.Get(container.Image)
  902. }
  903. func (container *Container) Mounted() (bool, error) {
  904. return Mounted(container.RootfsPath())
  905. }
  906. func (container *Container) Unmount() error {
  907. return Unmount(container.RootfsPath())
  908. }
  909. // ShortID returns a shorthand version of the container's id for convenience.
  910. // A collision with other container shorthands is very unlikely, but possible.
  911. // In case of a collision a lookup with Runtime.Get() will fail, and the caller
  912. // will need to use a langer prefix, or the full-length container Id.
  913. func (container *Container) ShortID() string {
  914. return utils.TruncateID(container.ID)
  915. }
  916. func (container *Container) logPath(name string) string {
  917. return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.ID, name))
  918. }
  919. func (container *Container) ReadLog(name string) (io.Reader, error) {
  920. return os.Open(container.logPath(name))
  921. }
  922. func (container *Container) hostConfigPath() string {
  923. return path.Join(container.root, "hostconfig.json")
  924. }
  925. func (container *Container) jsonPath() string {
  926. return path.Join(container.root, "config.json")
  927. }
  928. func (container *Container) lxcConfigPath() string {
  929. return path.Join(container.root, "config.lxc")
  930. }
  931. // This method must be exported to be used from the lxc template
  932. func (container *Container) RootfsPath() string {
  933. return path.Join(container.root, "rootfs")
  934. }
  935. func (container *Container) rwPath() string {
  936. return path.Join(container.root, "rw")
  937. }
  938. func validateID(id string) error {
  939. if id == "" {
  940. return fmt.Errorf("Invalid empty id")
  941. }
  942. return nil
  943. }
  944. // GetSize, return real size, virtual size
  945. func (container *Container) GetSize() (int64, int64) {
  946. var sizeRw, sizeRootfs int64
  947. filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
  948. if fileInfo != nil {
  949. sizeRw += fileInfo.Size()
  950. }
  951. return nil
  952. })
  953. _, err := os.Stat(container.RootfsPath())
  954. if err == nil {
  955. filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
  956. if fileInfo != nil {
  957. sizeRootfs += fileInfo.Size()
  958. }
  959. return nil
  960. })
  961. }
  962. return sizeRw, sizeRootfs
  963. }