client_linux.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. package libcontainerd
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. "syscall"
  10. "github.com/Sirupsen/logrus"
  11. containerd "github.com/docker/containerd/api/grpc/types"
  12. "github.com/docker/docker/pkg/idtools"
  13. "github.com/docker/docker/pkg/mount"
  14. specs "github.com/opencontainers/specs/specs-go"
  15. "golang.org/x/net/context"
  16. )
  17. type client struct {
  18. clientCommon
  19. // Platform specific properties below here.
  20. remote *remote
  21. q queue
  22. exitNotifiers map[string]*exitNotifier
  23. }
  24. func (clnt *client) AddProcess(containerID, processFriendlyName string, specp Process) error {
  25. clnt.lock(containerID)
  26. defer clnt.unlock(containerID)
  27. container, err := clnt.getContainer(containerID)
  28. if err != nil {
  29. return err
  30. }
  31. spec, err := container.spec()
  32. if err != nil {
  33. return err
  34. }
  35. sp := spec.Process
  36. sp.Args = specp.Args
  37. sp.Terminal = specp.Terminal
  38. if specp.Env != nil {
  39. sp.Env = specp.Env
  40. }
  41. if specp.Cwd != nil {
  42. sp.Cwd = *specp.Cwd
  43. }
  44. if specp.User != nil {
  45. sp.User = specs.User{
  46. UID: specp.User.UID,
  47. GID: specp.User.GID,
  48. AdditionalGids: specp.User.AdditionalGids,
  49. }
  50. }
  51. if specp.Capabilities != nil {
  52. sp.Capabilities = specp.Capabilities
  53. }
  54. p := container.newProcess(processFriendlyName)
  55. r := &containerd.AddProcessRequest{
  56. Args: sp.Args,
  57. Cwd: sp.Cwd,
  58. Terminal: sp.Terminal,
  59. Id: containerID,
  60. Env: sp.Env,
  61. User: &containerd.User{
  62. Uid: sp.User.UID,
  63. Gid: sp.User.GID,
  64. AdditionalGids: sp.User.AdditionalGids,
  65. },
  66. Pid: processFriendlyName,
  67. Stdin: p.fifo(syscall.Stdin),
  68. Stdout: p.fifo(syscall.Stdout),
  69. Stderr: p.fifo(syscall.Stderr),
  70. Capabilities: sp.Capabilities,
  71. ApparmorProfile: sp.ApparmorProfile,
  72. SelinuxLabel: sp.SelinuxLabel,
  73. NoNewPrivileges: sp.NoNewPrivileges,
  74. Rlimits: convertRlimits(sp.Rlimits),
  75. }
  76. iopipe, err := p.openFifos(sp.Terminal)
  77. if err != nil {
  78. return err
  79. }
  80. if _, err := clnt.remote.apiClient.AddProcess(context.Background(), r); err != nil {
  81. p.closeFifos(iopipe)
  82. return err
  83. }
  84. container.processes[processFriendlyName] = p
  85. clnt.unlock(containerID)
  86. if err := clnt.backend.AttachStreams(processFriendlyName, *iopipe); err != nil {
  87. return err
  88. }
  89. clnt.lock(containerID)
  90. return nil
  91. }
  92. func (clnt *client) prepareBundleDir(uid, gid int) (string, error) {
  93. root, err := filepath.Abs(clnt.remote.stateDir)
  94. if err != nil {
  95. return "", err
  96. }
  97. if uid == 0 && gid == 0 {
  98. return root, nil
  99. }
  100. p := string(filepath.Separator)
  101. for _, d := range strings.Split(root, string(filepath.Separator))[1:] {
  102. p = filepath.Join(p, d)
  103. fi, err := os.Stat(p)
  104. if err != nil && !os.IsNotExist(err) {
  105. return "", err
  106. }
  107. if os.IsNotExist(err) || fi.Mode()&1 == 0 {
  108. p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
  109. if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) {
  110. return "", err
  111. }
  112. }
  113. }
  114. return p, nil
  115. }
  116. func (clnt *client) Create(containerID string, spec Spec, options ...CreateOption) (err error) {
  117. clnt.lock(containerID)
  118. defer clnt.unlock(containerID)
  119. if ctr, err := clnt.getContainer(containerID); err == nil {
  120. if ctr.restarting {
  121. ctr.restartManager.Cancel()
  122. ctr.clean()
  123. } else {
  124. return fmt.Errorf("Container %s is already active", containerID)
  125. }
  126. }
  127. uid, gid, err := getRootIDs(specs.Spec(spec))
  128. if err != nil {
  129. return err
  130. }
  131. dir, err := clnt.prepareBundleDir(uid, gid)
  132. if err != nil {
  133. return err
  134. }
  135. container := clnt.newContainer(filepath.Join(dir, containerID), options...)
  136. if err := container.clean(); err != nil {
  137. return err
  138. }
  139. defer func() {
  140. if err != nil {
  141. container.clean()
  142. clnt.deleteContainer(containerID)
  143. }
  144. }()
  145. if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) {
  146. return err
  147. }
  148. f, err := os.Create(filepath.Join(container.dir, configFilename))
  149. if err != nil {
  150. return err
  151. }
  152. defer f.Close()
  153. if err := json.NewEncoder(f).Encode(spec); err != nil {
  154. return err
  155. }
  156. return container.start()
  157. }
  158. func (clnt *client) Signal(containerID string, sig int) error {
  159. clnt.lock(containerID)
  160. defer clnt.unlock(containerID)
  161. _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{
  162. Id: containerID,
  163. Pid: InitFriendlyName,
  164. Signal: uint32(sig),
  165. })
  166. return err
  167. }
  168. func (clnt *client) SignalProcess(containerID string, pid string, sig int) error {
  169. clnt.lock(containerID)
  170. defer clnt.unlock(containerID)
  171. _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{
  172. Id: containerID,
  173. Pid: pid,
  174. Signal: uint32(sig),
  175. })
  176. return err
  177. }
  178. func (clnt *client) Resize(containerID, processFriendlyName string, width, height int) error {
  179. clnt.lock(containerID)
  180. defer clnt.unlock(containerID)
  181. if _, err := clnt.getContainer(containerID); err != nil {
  182. return err
  183. }
  184. _, err := clnt.remote.apiClient.UpdateProcess(context.Background(), &containerd.UpdateProcessRequest{
  185. Id: containerID,
  186. Pid: processFriendlyName,
  187. Width: uint32(width),
  188. Height: uint32(height),
  189. })
  190. return err
  191. }
  192. func (clnt *client) Pause(containerID string) error {
  193. return clnt.setState(containerID, StatePause)
  194. }
  195. func (clnt *client) setState(containerID, state string) error {
  196. clnt.lock(containerID)
  197. container, err := clnt.getContainer(containerID)
  198. if err != nil {
  199. clnt.unlock(containerID)
  200. return err
  201. }
  202. if container.systemPid == 0 {
  203. clnt.unlock(containerID)
  204. return fmt.Errorf("No active process for container %s", containerID)
  205. }
  206. st := "running"
  207. if state == StatePause {
  208. st = "paused"
  209. }
  210. chstate := make(chan struct{})
  211. _, err = clnt.remote.apiClient.UpdateContainer(context.Background(), &containerd.UpdateContainerRequest{
  212. Id: containerID,
  213. Pid: InitFriendlyName,
  214. Status: st,
  215. })
  216. if err != nil {
  217. clnt.unlock(containerID)
  218. return err
  219. }
  220. container.pauseMonitor.append(state, chstate)
  221. clnt.unlock(containerID)
  222. <-chstate
  223. return nil
  224. }
  225. func (clnt *client) Resume(containerID string) error {
  226. return clnt.setState(containerID, StateResume)
  227. }
  228. func (clnt *client) Stats(containerID string) (*Stats, error) {
  229. resp, err := clnt.remote.apiClient.Stats(context.Background(), &containerd.StatsRequest{containerID})
  230. if err != nil {
  231. return nil, err
  232. }
  233. return (*Stats)(resp), nil
  234. }
  235. // Take care of the old 1.11.0 behavior in case the version upgrade
  236. // happenned without a clean daemon shutdown
  237. func (clnt *client) cleanupOldRootfs(containerID string) {
  238. // Unmount and delete the bundle folder
  239. if mts, err := mount.GetMounts(); err == nil {
  240. for _, mts := range mts {
  241. if strings.HasSuffix(mts.Mountpoint, containerID+"/rootfs") {
  242. if err := syscall.Unmount(mts.Mountpoint, syscall.MNT_DETACH); err == nil {
  243. os.RemoveAll(strings.TrimSuffix(mts.Mountpoint, "/rootfs"))
  244. }
  245. break
  246. }
  247. }
  248. }
  249. }
  250. func (clnt *client) setExited(containerID string) error {
  251. clnt.lock(containerID)
  252. defer clnt.unlock(containerID)
  253. var exitCode uint32
  254. if event, ok := clnt.remote.pastEvents[containerID]; ok {
  255. exitCode = event.Status
  256. delete(clnt.remote.pastEvents, containerID)
  257. }
  258. err := clnt.backend.StateChanged(containerID, StateInfo{
  259. CommonStateInfo: CommonStateInfo{
  260. State: StateExit,
  261. ExitCode: exitCode,
  262. }})
  263. clnt.cleanupOldRootfs(containerID)
  264. return err
  265. }
  266. func (clnt *client) GetPidsForContainer(containerID string) ([]int, error) {
  267. cont, err := clnt.getContainerdContainer(containerID)
  268. if err != nil {
  269. return nil, err
  270. }
  271. pids := make([]int, len(cont.Pids))
  272. for i, p := range cont.Pids {
  273. pids[i] = int(p)
  274. }
  275. return pids, nil
  276. }
  277. // Summary returns a summary of the processes running in a container.
  278. // This is a no-op on Linux.
  279. func (clnt *client) Summary(containerID string) ([]Summary, error) {
  280. return nil, nil
  281. }
  282. func (clnt *client) getContainerdContainer(containerID string) (*containerd.Container, error) {
  283. resp, err := clnt.remote.apiClient.State(context.Background(), &containerd.StateRequest{Id: containerID})
  284. if err != nil {
  285. return nil, err
  286. }
  287. for _, cont := range resp.Containers {
  288. if cont.Id == containerID {
  289. return cont, nil
  290. }
  291. }
  292. return nil, fmt.Errorf("invalid state response")
  293. }
  294. func (clnt *client) newContainer(dir string, options ...CreateOption) *container {
  295. container := &container{
  296. containerCommon: containerCommon{
  297. process: process{
  298. dir: dir,
  299. processCommon: processCommon{
  300. containerID: filepath.Base(dir),
  301. client: clnt,
  302. friendlyName: InitFriendlyName,
  303. },
  304. },
  305. processes: make(map[string]*process),
  306. },
  307. }
  308. for _, option := range options {
  309. if err := option.Apply(container); err != nil {
  310. logrus.Error(err)
  311. }
  312. }
  313. return container
  314. }
  315. func (clnt *client) UpdateResources(containerID string, resources Resources) error {
  316. clnt.lock(containerID)
  317. defer clnt.unlock(containerID)
  318. container, err := clnt.getContainer(containerID)
  319. if err != nil {
  320. return err
  321. }
  322. if container.systemPid == 0 {
  323. return fmt.Errorf("No active process for container %s", containerID)
  324. }
  325. _, err = clnt.remote.apiClient.UpdateContainer(context.Background(), &containerd.UpdateContainerRequest{
  326. Id: containerID,
  327. Pid: InitFriendlyName,
  328. Resources: (*containerd.UpdateResource)(&resources),
  329. })
  330. if err != nil {
  331. return err
  332. }
  333. return nil
  334. }
  335. func (clnt *client) getExitNotifier(containerID string) *exitNotifier {
  336. clnt.mapMutex.RLock()
  337. defer clnt.mapMutex.RUnlock()
  338. return clnt.exitNotifiers[containerID]
  339. }
  340. func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier {
  341. clnt.mapMutex.Lock()
  342. w, ok := clnt.exitNotifiers[containerID]
  343. defer clnt.mapMutex.Unlock()
  344. if !ok {
  345. w = &exitNotifier{c: make(chan struct{}), client: clnt}
  346. clnt.exitNotifiers[containerID] = w
  347. }
  348. return w
  349. }
  350. func (clnt *client) restore(cont *containerd.Container, options ...CreateOption) (err error) {
  351. clnt.lock(cont.Id)
  352. defer clnt.unlock(cont.Id)
  353. logrus.Debugf("restore container %s state %s", cont.Id, cont.Status)
  354. containerID := cont.Id
  355. if _, err := clnt.getContainer(containerID); err == nil {
  356. return fmt.Errorf("container %s is already active", containerID)
  357. }
  358. defer func() {
  359. if err != nil {
  360. clnt.deleteContainer(cont.Id)
  361. }
  362. }()
  363. container := clnt.newContainer(cont.BundlePath, options...)
  364. container.systemPid = systemPid(cont)
  365. var terminal bool
  366. for _, p := range cont.Processes {
  367. if p.Pid == InitFriendlyName {
  368. terminal = p.Terminal
  369. }
  370. }
  371. iopipe, err := container.openFifos(terminal)
  372. if err != nil {
  373. return err
  374. }
  375. if err := clnt.backend.AttachStreams(containerID, *iopipe); err != nil {
  376. return err
  377. }
  378. clnt.appendContainer(container)
  379. err = clnt.backend.StateChanged(containerID, StateInfo{
  380. CommonStateInfo: CommonStateInfo{
  381. State: StateRestore,
  382. Pid: container.systemPid,
  383. }})
  384. if err != nil {
  385. return err
  386. }
  387. if event, ok := clnt.remote.pastEvents[containerID]; ok {
  388. // This should only be a pause or resume event
  389. if event.Type == StatePause || event.Type == StateResume {
  390. return clnt.backend.StateChanged(containerID, StateInfo{
  391. CommonStateInfo: CommonStateInfo{
  392. State: event.Type,
  393. Pid: container.systemPid,
  394. }})
  395. }
  396. logrus.Warnf("unexpected backlog event: %#v", event)
  397. }
  398. return nil
  399. }
  400. func (clnt *client) Restore(containerID string, options ...CreateOption) error {
  401. cont, err := clnt.getContainerdContainer(containerID)
  402. if err == nil && cont.Status != "stopped" {
  403. if err := clnt.restore(cont, options...); err != nil {
  404. logrus.Errorf("error restoring %s: %v", containerID, err)
  405. }
  406. return nil
  407. }
  408. return clnt.setExited(containerID)
  409. }
  410. type exitNotifier struct {
  411. id string
  412. client *client
  413. c chan struct{}
  414. once sync.Once
  415. }
  416. func (en *exitNotifier) close() {
  417. en.once.Do(func() {
  418. close(en.c)
  419. en.client.mapMutex.Lock()
  420. if en == en.client.exitNotifiers[en.id] {
  421. delete(en.client.exitNotifiers, en.id)
  422. }
  423. en.client.mapMutex.Unlock()
  424. })
  425. }
  426. func (en *exitNotifier) wait() <-chan struct{} {
  427. return en.c
  428. }