exec.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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); 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. fifoCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
  144. defer cancel()
  145. sc, err := fifo.OpenFifo(fifoCtx, e.stdio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
  146. if err != nil {
  147. return errors.Wrapf(err, "failed to open stdin fifo %s", e.stdio.Stdin)
  148. }
  149. e.closers = append(e.closers, sc)
  150. e.stdin = sc
  151. }
  152. var copyWaitGroup sync.WaitGroup
  153. if socket != nil {
  154. console, err := socket.ReceiveMaster()
  155. if err != nil {
  156. return errors.Wrap(err, "failed to retrieve console master")
  157. }
  158. if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
  159. return errors.Wrap(err, "failed to start console copy")
  160. }
  161. } else if !e.stdio.IsNull() {
  162. fifoCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
  163. defer cancel()
  164. if err := copyPipes(fifoCtx, e.io, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
  165. return errors.Wrap(err, "failed to start io pipe copy")
  166. }
  167. }
  168. copyWaitGroup.Wait()
  169. pid, err := runc.ReadPidFile(opts.PidFile)
  170. if err != nil {
  171. return errors.Wrap(err, "failed to retrieve OCI runtime exec pid")
  172. }
  173. e.pid = pid
  174. return nil
  175. }
  176. func (e *execProcess) Status(ctx context.Context) (string, error) {
  177. s, err := e.parent.Status(ctx)
  178. if err != nil {
  179. return "", err
  180. }
  181. // if the container as a whole is in the pausing/paused state, so are all
  182. // other processes inside the container, use container state here
  183. switch s {
  184. case "paused", "pausing":
  185. return s, nil
  186. }
  187. e.mu.Lock()
  188. defer e.mu.Unlock()
  189. // if we don't have a pid then the exec process has just been created
  190. if e.pid == 0 {
  191. return "created", nil
  192. }
  193. // if we have a pid and it can be signaled, the process is running
  194. if err := unix.Kill(e.pid, 0); err == nil {
  195. return "running", nil
  196. }
  197. // else if we have a pid but it can nolonger be signaled, it has stopped
  198. return "stopped", nil
  199. }