exec.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // +build !windows
  2. /*
  3. Copyright The containerd Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package proc
  15. import (
  16. "context"
  17. "fmt"
  18. "io"
  19. "os"
  20. "path/filepath"
  21. "sync"
  22. "syscall"
  23. "time"
  24. "golang.org/x/sys/unix"
  25. "github.com/containerd/console"
  26. "github.com/containerd/containerd/runtime/proc"
  27. "github.com/containerd/fifo"
  28. runc "github.com/containerd/go-runc"
  29. specs "github.com/opencontainers/runtime-spec/specs-go"
  30. "github.com/pkg/errors"
  31. )
  32. type execProcess struct {
  33. wg sync.WaitGroup
  34. proc.State
  35. mu sync.Mutex
  36. id string
  37. console console.Console
  38. io runc.IO
  39. status int
  40. exited time.Time
  41. pid int
  42. closers []io.Closer
  43. stdin io.Closer
  44. stdio proc.Stdio
  45. path string
  46. spec specs.Process
  47. parent *Init
  48. waitBlock chan struct{}
  49. }
  50. func (e *execProcess) Wait() {
  51. <-e.waitBlock
  52. }
  53. func (e *execProcess) ID() string {
  54. return e.id
  55. }
  56. func (e *execProcess) Pid() int {
  57. e.mu.Lock()
  58. defer e.mu.Unlock()
  59. return e.pid
  60. }
  61. func (e *execProcess) ExitStatus() int {
  62. e.mu.Lock()
  63. defer e.mu.Unlock()
  64. return e.status
  65. }
  66. func (e *execProcess) ExitedAt() time.Time {
  67. e.mu.Lock()
  68. defer e.mu.Unlock()
  69. return e.exited
  70. }
  71. func (e *execProcess) setExited(status int) {
  72. e.status = status
  73. e.exited = time.Now()
  74. e.parent.Platform.ShutdownConsole(context.Background(), e.console)
  75. close(e.waitBlock)
  76. }
  77. func (e *execProcess) delete(ctx context.Context) error {
  78. e.wg.Wait()
  79. if e.io != nil {
  80. for _, c := range e.closers {
  81. c.Close()
  82. }
  83. e.io.Close()
  84. }
  85. pidfile := filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id))
  86. // silently ignore error
  87. os.Remove(pidfile)
  88. return nil
  89. }
  90. func (e *execProcess) resize(ws console.WinSize) error {
  91. if e.console == nil {
  92. return nil
  93. }
  94. return e.console.Resize(ws)
  95. }
  96. func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error {
  97. pid := e.pid
  98. if pid != 0 {
  99. if err := unix.Kill(pid, syscall.Signal(sig)); err != nil {
  100. return errors.Wrapf(checkKillError(err), "exec kill error")
  101. }
  102. }
  103. return nil
  104. }
  105. func (e *execProcess) Stdin() io.Closer {
  106. return e.stdin
  107. }
  108. func (e *execProcess) Stdio() proc.Stdio {
  109. return e.stdio
  110. }
  111. func (e *execProcess) start(ctx context.Context) (err error) {
  112. var (
  113. socket *runc.Socket
  114. pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id))
  115. )
  116. if e.stdio.Terminal {
  117. if socket, err = runc.NewTempConsoleSocket(); err != nil {
  118. return errors.Wrap(err, "failed to create runc console socket")
  119. }
  120. defer socket.Close()
  121. } else if e.stdio.IsNull() {
  122. if e.io, err = runc.NewNullIO(); err != nil {
  123. return errors.Wrap(err, "creating new NULL IO")
  124. }
  125. } else {
  126. if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil {
  127. return errors.Wrap(err, "failed to create runc io pipes")
  128. }
  129. }
  130. opts := &runc.ExecOpts{
  131. PidFile: pidfile,
  132. IO: e.io,
  133. Detach: true,
  134. }
  135. if socket != nil {
  136. opts.ConsoleSocket = socket
  137. }
  138. if err := e.parent.runtime.Exec(ctx, e.parent.id, e.spec, opts); err != nil {
  139. close(e.waitBlock)
  140. return e.parent.runtimeError(err, "OCI runtime exec failed")
  141. }
  142. if e.stdio.Stdin != "" {
  143. sc, err := fifo.OpenFifo(ctx, e.stdio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
  144. if err != nil {
  145. return errors.Wrapf(err, "failed to open stdin fifo %s", e.stdio.Stdin)
  146. }
  147. e.closers = append(e.closers, sc)
  148. e.stdin = sc
  149. }
  150. var copyWaitGroup sync.WaitGroup
  151. if socket != nil {
  152. console, err := socket.ReceiveMaster()
  153. if err != nil {
  154. return errors.Wrap(err, "failed to retrieve console master")
  155. }
  156. if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
  157. return errors.Wrap(err, "failed to start console copy")
  158. }
  159. } else if !e.stdio.IsNull() {
  160. if err := copyPipes(ctx, e.io, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
  161. return errors.Wrap(err, "failed to start io pipe copy")
  162. }
  163. }
  164. copyWaitGroup.Wait()
  165. pid, err := runc.ReadPidFile(opts.PidFile)
  166. if err != nil {
  167. return errors.Wrap(err, "failed to retrieve OCI runtime exec pid")
  168. }
  169. e.pid = pid
  170. return nil
  171. }
  172. func (e *execProcess) Status(ctx context.Context) (string, error) {
  173. s, err := e.parent.Status(ctx)
  174. if err != nil {
  175. return "", err
  176. }
  177. // if the container as a whole is in the pausing/paused state, so are all
  178. // other processes inside the container, use container state here
  179. switch s {
  180. case "paused", "pausing":
  181. return s, nil
  182. }
  183. e.mu.Lock()
  184. defer e.mu.Unlock()
  185. // if we don't have a pid then the exec process has just been created
  186. if e.pid == 0 {
  187. return "created", nil
  188. }
  189. // if we have a pid and it can be signaled, the process is running
  190. if err := unix.Kill(e.pid, 0); err == nil {
  191. return "running", nil
  192. }
  193. // else if we have a pid but it can nolonger be signaled, it has stopped
  194. return "stopped", nil
  195. }