123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- package runtime
- import (
- "encoding/json"
- "fmt"
- "io"
- "os"
- "path"
- "path/filepath"
- "sync"
- "syscall"
- "github.com/docker/containerd/specs"
- "github.com/docker/containerd/subreaper"
- "github.com/docker/containerd/subreaper/exec"
- "github.com/docker/docker/pkg/term"
- "github.com/opencontainers/runc/libcontainer"
- )
- type directProcess struct {
- *process
- sync.WaitGroup
- io stdio
- console libcontainer.Console
- consolePath string
- exec bool
- checkpoint string
- specs *specs.Spec
- }
- func newDirectProcess(config *processConfig) (*directProcess, error) {
- lp, err := newProcess(config)
- if err != nil {
- return nil, err
- }
- return &directProcess{
- specs: config.spec,
- process: lp,
- exec: config.exec,
- checkpoint: config.checkpoint,
- }, nil
- }
- func (d *directProcess) CloseStdin() error {
- if d.io.stdin != nil {
- return d.io.stdin.Close()
- }
- return nil
- }
- func (d *directProcess) Resize(w, h int) error {
- if d.console == nil {
- return nil
- }
- ws := term.Winsize{
- Width: uint16(w),
- Height: uint16(h),
- }
- return term.SetWinsize(d.console.Fd(), &ws)
- }
- func (d *directProcess) openIO() (*os.File, *os.File, *os.File, error) {
- uid, gid, err := getRootIDs(d.specs)
- if err != nil {
- return nil, nil, nil, err
- }
- if d.spec.Terminal {
- console, err := libcontainer.NewConsole(uid, gid)
- if err != nil {
- return nil, nil, nil, err
- }
- d.console = console
- d.consolePath = console.Path()
- stdin, err := os.OpenFile(d.stdio.Stdin, syscall.O_RDONLY, 0)
- if err != nil {
- return nil, nil, nil, err
- }
- go io.Copy(console, stdin)
- stdout, err := os.OpenFile(d.stdio.Stdout, syscall.O_RDWR, 0)
- if err != nil {
- return nil, nil, nil, err
- }
- d.Add(1)
- go func() {
- io.Copy(stdout, console)
- console.Close()
- d.Done()
- }()
- d.io.stdin = stdin
- d.io.stdout = stdout
- d.io.stderr = stdout
- return nil, nil, nil, nil
- }
- stdin, err := os.OpenFile(d.stdio.Stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
- if err != nil {
- return nil, nil, nil, err
- }
- stdout, err := os.OpenFile(d.stdio.Stdout, syscall.O_RDWR, 0)
- if err != nil {
- return nil, nil, nil, err
- }
- stderr, err := os.OpenFile(d.stdio.Stderr, syscall.O_RDWR, 0)
- if err != nil {
- return nil, nil, nil, err
- }
- d.io.stdin = stdin
- d.io.stdout = stdout
- d.io.stderr = stderr
- return stdin, stdout, stderr, nil
- }
- func (d *directProcess) loadCheckpoint(bundle string) (*Checkpoint, error) {
- if d.checkpoint == "" {
- return nil, nil
- }
- f, err := os.Open(filepath.Join(bundle, "checkpoints", d.checkpoint, "config.json"))
- if err != nil {
- return nil, err
- }
- defer f.Close()
- var cpt Checkpoint
- if err := json.NewDecoder(f).Decode(&cpt); err != nil {
- return nil, err
- }
- return &cpt, nil
- }
- func (d *directProcess) Start() error {
- cwd, err := filepath.Abs(d.root)
- if err != nil {
- return err
- }
- stdin, stdout, stderr, err := d.openIO()
- if err != nil {
- return nil
- }
- checkpoint, err := d.loadCheckpoint(d.container.bundle)
- if err != nil {
- return err
- }
- logPath := filepath.Join(cwd, "log.json")
- args := append([]string{
- "--log", logPath,
- "--log-format", "json",
- }, d.container.runtimeArgs...)
- if d.exec {
- args = append(args, "exec",
- "--process", filepath.Join(cwd, "process.json"),
- "--console", d.consolePath,
- )
- } else if checkpoint != nil {
- args = append(args, "restore",
- "--image-path", filepath.Join(d.container.bundle, "checkpoints", checkpoint.Name),
- )
- add := func(flags ...string) {
- args = append(args, flags...)
- }
- if checkpoint.Shell {
- add("--shell-job")
- }
- if checkpoint.Tcp {
- add("--tcp-established")
- }
- if checkpoint.UnixSockets {
- add("--ext-unix-sk")
- }
- if d.container.noPivotRoot {
- add("--no-pivot")
- }
- } else {
- args = append(args, "start",
- "--bundle", d.container.bundle,
- "--console", d.consolePath,
- )
- if d.container.noPivotRoot {
- args = append(args, "--no-pivot")
- }
- }
- args = append(args,
- "-d",
- "--pid-file", filepath.Join(cwd, "pid"),
- d.container.id,
- )
- cmd := exec.Command(d.container.runtime, args...)
- cmd.Dir = d.container.bundle
- cmd.Stdin = stdin
- cmd.Stdout = stdout
- cmd.Stderr = stderr
- // set the parent death signal to SIGKILL so that if containerd dies the container
- // process also dies
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Pdeathsig: syscall.SIGKILL,
- }
- exitSubscription := subreaper.Subscribe()
- err = d.startCmd(cmd)
- if err != nil {
- subreaper.Unsubscribe(exitSubscription)
- d.delete()
- return err
- }
- go d.watch(cmd, exitSubscription)
- return nil
- }
- func (d *directProcess) watch(cmd *exec.Cmd, exitSubscription *subreaper.Subscription) {
- defer subreaper.Unsubscribe(exitSubscription)
- defer d.delete()
- f, err := os.OpenFile(path.Join(d.root, ExitFile), syscall.O_WRONLY, 0)
- if err == nil {
- defer f.Close()
- }
- exitCode := 0
- if err = cmd.Wait(); err != nil {
- if exitError, ok := err.(exec.ExitCodeError); ok {
- exitCode = exitError.Code
- }
- }
- if exitCode == 0 {
- pid, err := d.getPidFromFile()
- if err != nil {
- return
- }
- exitSubscription.SetPid(pid)
- exitCode = exitSubscription.Wait()
- }
- writeInt(path.Join(d.root, ExitStatusFile), exitCode)
- }
- func (d *directProcess) delete() {
- if d.console != nil {
- d.console.Close()
- }
- d.io.Close()
- d.Wait()
- if !d.exec {
- exec.Command(d.container.runtime, append(d.container.runtimeArgs, "delete", d.container.id)...).Run()
- }
- }
- func writeInt(path string, i int) error {
- f, err := os.Create(path)
- if err != nil {
- return err
- }
- defer f.Close()
- _, err = fmt.Fprintf(f, "%d", i)
- return err
- }
- type stdio struct {
- stdin *os.File
- stdout *os.File
- stderr *os.File
- }
- func (s stdio) Close() error {
- err := s.stdin.Close()
- if oerr := s.stdout.Close(); err == nil {
- err = oerr
- }
- if oerr := s.stderr.Close(); err == nil {
- err = oerr
- }
- return err
- }
|