exec.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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 process
  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/errdefs"
  27. "github.com/containerd/containerd/pkg/stdio"
  28. "github.com/containerd/fifo"
  29. runc "github.com/containerd/go-runc"
  30. specs "github.com/opencontainers/runtime-spec/specs-go"
  31. "github.com/pkg/errors"
  32. )
  33. type execProcess struct {
  34. wg sync.WaitGroup
  35. execState execState
  36. mu sync.Mutex
  37. id string
  38. console console.Console
  39. io *processIO
  40. status int
  41. exited time.Time
  42. pid safePid
  43. closers []io.Closer
  44. stdin io.Closer
  45. stdio stdio.Stdio
  46. path string
  47. spec specs.Process
  48. parent *Init
  49. waitBlock chan struct{}
  50. }
  51. func (e *execProcess) Wait() {
  52. <-e.waitBlock
  53. }
  54. func (e *execProcess) ID() string {
  55. return e.id
  56. }
  57. func (e *execProcess) Pid() int {
  58. return e.pid.get()
  59. }
  60. func (e *execProcess) ExitStatus() int {
  61. e.mu.Lock()
  62. defer e.mu.Unlock()
  63. return e.status
  64. }
  65. func (e *execProcess) ExitedAt() time.Time {
  66. e.mu.Lock()
  67. defer e.mu.Unlock()
  68. return e.exited
  69. }
  70. func (e *execProcess) SetExited(status int) {
  71. e.mu.Lock()
  72. defer e.mu.Unlock()
  73. e.execState.SetExited(status)
  74. }
  75. func (e *execProcess) setExited(status int) {
  76. e.status = status
  77. e.exited = time.Now()
  78. e.parent.Platform.ShutdownConsole(context.Background(), e.console)
  79. close(e.waitBlock)
  80. }
  81. func (e *execProcess) Delete(ctx context.Context) error {
  82. e.mu.Lock()
  83. defer e.mu.Unlock()
  84. return e.execState.Delete(ctx)
  85. }
  86. func (e *execProcess) delete(ctx context.Context) error {
  87. waitTimeout(ctx, &e.wg, 2*time.Second)
  88. if e.io != nil {
  89. for _, c := range e.closers {
  90. c.Close()
  91. }
  92. e.io.Close()
  93. }
  94. pidfile := filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id))
  95. // silently ignore error
  96. os.Remove(pidfile)
  97. return nil
  98. }
  99. func (e *execProcess) Resize(ws console.WinSize) error {
  100. e.mu.Lock()
  101. defer e.mu.Unlock()
  102. return e.execState.Resize(ws)
  103. }
  104. func (e *execProcess) resize(ws console.WinSize) error {
  105. if e.console == nil {
  106. return nil
  107. }
  108. return e.console.Resize(ws)
  109. }
  110. func (e *execProcess) Kill(ctx context.Context, sig uint32, _ bool) error {
  111. e.mu.Lock()
  112. defer e.mu.Unlock()
  113. return e.execState.Kill(ctx, sig, false)
  114. }
  115. func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error {
  116. pid := e.pid.get()
  117. switch {
  118. case pid == 0:
  119. return errors.Wrap(errdefs.ErrFailedPrecondition, "process not created")
  120. case !e.exited.IsZero():
  121. return errors.Wrapf(errdefs.ErrNotFound, "process already finished")
  122. default:
  123. if err := unix.Kill(pid, syscall.Signal(sig)); err != nil {
  124. return errors.Wrapf(checkKillError(err), "exec kill error")
  125. }
  126. }
  127. return nil
  128. }
  129. func (e *execProcess) Stdin() io.Closer {
  130. return e.stdin
  131. }
  132. func (e *execProcess) Stdio() stdio.Stdio {
  133. return e.stdio
  134. }
  135. func (e *execProcess) Start(ctx context.Context) error {
  136. e.mu.Lock()
  137. defer e.mu.Unlock()
  138. return e.execState.Start(ctx)
  139. }
  140. func (e *execProcess) start(ctx context.Context) (err error) {
  141. // The reaper may receive exit signal right after
  142. // the container is started, before the e.pid is updated.
  143. // In that case, we want to block the signal handler to
  144. // access e.pid until it is updated.
  145. e.pid.Lock()
  146. defer e.pid.Unlock()
  147. var (
  148. socket *runc.Socket
  149. pio *processIO
  150. pidFile = newExecPidFile(e.path, e.id)
  151. )
  152. if e.stdio.Terminal {
  153. if socket, err = runc.NewTempConsoleSocket(); err != nil {
  154. return errors.Wrap(err, "failed to create runc console socket")
  155. }
  156. defer socket.Close()
  157. } else {
  158. if pio, err = createIO(ctx, e.id, e.parent.IoUID, e.parent.IoGID, e.stdio); err != nil {
  159. return errors.Wrap(err, "failed to create init process I/O")
  160. }
  161. e.io = pio
  162. }
  163. opts := &runc.ExecOpts{
  164. PidFile: pidFile.Path(),
  165. Detach: true,
  166. }
  167. if pio != nil {
  168. opts.IO = pio.IO()
  169. }
  170. if socket != nil {
  171. opts.ConsoleSocket = socket
  172. }
  173. if err := e.parent.runtime.Exec(ctx, e.parent.id, e.spec, opts); err != nil {
  174. close(e.waitBlock)
  175. return e.parent.runtimeError(err, "OCI runtime exec failed")
  176. }
  177. if e.stdio.Stdin != "" {
  178. if err := e.openStdin(e.stdio.Stdin); err != nil {
  179. return err
  180. }
  181. }
  182. ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
  183. defer cancel()
  184. if socket != nil {
  185. console, err := socket.ReceiveMaster()
  186. if err != nil {
  187. return errors.Wrap(err, "failed to retrieve console master")
  188. }
  189. if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg); err != nil {
  190. return errors.Wrap(err, "failed to start console copy")
  191. }
  192. } else {
  193. if err := pio.Copy(ctx, &e.wg); err != nil {
  194. return errors.Wrap(err, "failed to start io pipe copy")
  195. }
  196. }
  197. pid, err := pidFile.Read()
  198. if err != nil {
  199. return errors.Wrap(err, "failed to retrieve OCI runtime exec pid")
  200. }
  201. e.pid.pid = pid
  202. return nil
  203. }
  204. func (e *execProcess) openStdin(path string) error {
  205. sc, err := fifo.OpenFifo(context.Background(), path, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
  206. if err != nil {
  207. return errors.Wrapf(err, "failed to open stdin fifo %s", path)
  208. }
  209. e.stdin = sc
  210. e.closers = append(e.closers, sc)
  211. return nil
  212. }
  213. func (e *execProcess) Status(ctx context.Context) (string, error) {
  214. s, err := e.parent.Status(ctx)
  215. if err != nil {
  216. return "", err
  217. }
  218. // if the container as a whole is in the pausing/paused state, so are all
  219. // other processes inside the container, use container state here
  220. switch s {
  221. case "paused", "pausing":
  222. return s, nil
  223. }
  224. e.mu.Lock()
  225. defer e.mu.Unlock()
  226. return e.execState.Status(ctx)
  227. }