mirror of
https://github.com/rancher/os.git
synced 2025-07-05 02:56:13 +00:00
284 lines
5.7 KiB
Go
284 lines
5.7 KiB
Go
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
|
|
}
|