mirror of
https://github.com/rancher/os.git
synced 2025-09-01 14:48:55 +00:00
Update vendor/
This commit is contained in:
340
vendor/github.com/docker/containerd/runtime/process.go
generated
vendored
Normal file
340
vendor/github.com/docker/containerd/runtime/process.go
generated
vendored
Normal file
@@ -0,0 +1,340 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/specs"
|
||||
"github.com/docker/containerd/subreaper/exec"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type Process interface {
|
||||
io.Closer
|
||||
|
||||
// ID of the process.
|
||||
// This is either "init" when it is the container's init process or
|
||||
// it is a user provided id for the process similar to the container id
|
||||
ID() string
|
||||
CloseStdin() error
|
||||
Resize(int, int) error
|
||||
// ExitFD returns the fd the provides an event when the process exits
|
||||
ExitFD() int
|
||||
// ExitStatus returns the exit status of the process or an error if it
|
||||
// has not exited
|
||||
ExitStatus() (int, error)
|
||||
// Spec returns the process spec that created the process
|
||||
Spec() specs.ProcessSpec
|
||||
// Signal sends the provided signal to the process
|
||||
Signal(os.Signal) error
|
||||
// Container returns the container that the process belongs to
|
||||
Container() Container
|
||||
// Stdio of the container
|
||||
Stdio() Stdio
|
||||
// SystemPid is the pid on the system
|
||||
SystemPid() int
|
||||
// State returns if the process is running or not
|
||||
State() State
|
||||
// Start executes the process
|
||||
Start() error
|
||||
}
|
||||
|
||||
type processConfig struct {
|
||||
id string
|
||||
root string
|
||||
processSpec specs.ProcessSpec
|
||||
spec *specs.Spec
|
||||
c *container
|
||||
stdio Stdio
|
||||
exec bool
|
||||
checkpoint string
|
||||
}
|
||||
|
||||
func newProcess(config *processConfig) (*process, error) {
|
||||
p := &process{
|
||||
root: config.root,
|
||||
id: config.id,
|
||||
container: config.c,
|
||||
spec: config.processSpec,
|
||||
stdio: config.stdio,
|
||||
}
|
||||
uid, gid, err := getRootIDs(config.spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Create(filepath.Join(config.root, "process.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
ps := ProcessState{
|
||||
ProcessSpec: config.processSpec,
|
||||
Exec: config.exec,
|
||||
PlatformProcessState: PlatformProcessState{
|
||||
Checkpoint: config.checkpoint,
|
||||
RootUID: uid,
|
||||
RootGID: gid,
|
||||
},
|
||||
Stdin: config.stdio.Stdin,
|
||||
Stdout: config.stdio.Stdout,
|
||||
Stderr: config.stdio.Stderr,
|
||||
RuntimeArgs: config.c.runtimeArgs,
|
||||
NoPivotRoot: config.c.noPivotRoot,
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(f).Encode(ps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exit, err := getExitPipe(filepath.Join(config.root, ExitFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
control, err := getControlPipe(filepath.Join(config.root, ControlFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.exitPipe = exit
|
||||
p.controlPipe = control
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *process) Start() error {
|
||||
cmd := exec.Command(p.container.shim,
|
||||
p.container.id, p.container.bundle, p.container.runtime,
|
||||
)
|
||||
cmd.Dir = p.root
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
return p.startCmd(cmd)
|
||||
}
|
||||
|
||||
func loadProcess(root, id string, c *container, s *ProcessState) (*process, error) {
|
||||
p := &process{
|
||||
root: root,
|
||||
id: id,
|
||||
container: c,
|
||||
spec: s.ProcessSpec,
|
||||
stdio: Stdio{
|
||||
Stdin: s.Stdin,
|
||||
Stdout: s.Stdout,
|
||||
Stderr: s.Stderr,
|
||||
},
|
||||
}
|
||||
if _, err := p.getPidFromFile(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := p.ExitStatus(); err != nil {
|
||||
if err == ErrProcessNotExited {
|
||||
exit, err := getExitPipe(filepath.Join(root, ExitFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.exitPipe = exit
|
||||
return p, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type process struct {
|
||||
root string
|
||||
id string
|
||||
pid int
|
||||
exitPipe *os.File
|
||||
controlPipe *os.File
|
||||
container *container
|
||||
spec specs.ProcessSpec
|
||||
stdio Stdio
|
||||
}
|
||||
|
||||
func (p *process) ID() string {
|
||||
return p.id
|
||||
}
|
||||
|
||||
func (p *process) Container() Container {
|
||||
return p.container
|
||||
}
|
||||
|
||||
func (p *process) SystemPid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
// ExitFD returns the fd of the exit pipe
|
||||
func (p *process) ExitFD() int {
|
||||
return int(p.exitPipe.Fd())
|
||||
}
|
||||
|
||||
func (p *process) CloseStdin() error {
|
||||
_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 0, 0, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *process) Resize(w, h int) error {
|
||||
_, err := fmt.Fprintf(p.controlPipe, "%d %d %d\n", 1, w, h)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *process) ExitStatus() (int, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(p.root, ExitStatusFile))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return -1, ErrProcessNotExited
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return -1, ErrProcessNotExited
|
||||
}
|
||||
return strconv.Atoi(string(data))
|
||||
}
|
||||
|
||||
func (p *process) Spec() specs.ProcessSpec {
|
||||
return p.spec
|
||||
}
|
||||
|
||||
func (p *process) Stdio() Stdio {
|
||||
return p.stdio
|
||||
}
|
||||
|
||||
// Close closes any open files and/or resouces on the process
|
||||
func (p *process) Close() error {
|
||||
return p.exitPipe.Close()
|
||||
}
|
||||
|
||||
func (p *process) State() State {
|
||||
if p.pid == 0 {
|
||||
return Stopped
|
||||
}
|
||||
err := syscall.Kill(p.pid, 0)
|
||||
if err != nil && err == syscall.ESRCH {
|
||||
return Stopped
|
||||
}
|
||||
return Running
|
||||
}
|
||||
|
||||
func (p *process) getPidFromFile() (int, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(p.root, "pid"))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
i, err := strconv.Atoi(string(data))
|
||||
if err != nil {
|
||||
return -1, errInvalidPidInt
|
||||
}
|
||||
p.pid = i
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func getExitPipe(path string) (*os.File, error) {
|
||||
if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
// add NONBLOCK in case the other side has already closed or else
|
||||
// this function would never return
|
||||
return os.OpenFile(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
|
||||
}
|
||||
|
||||
func getControlPipe(path string) (*os.File, error) {
|
||||
if err := unix.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
return os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
|
||||
}
|
||||
|
||||
// Signal sends the provided signal to the process
|
||||
func (p *process) Signal(s os.Signal) error {
|
||||
return syscall.Kill(p.pid, s.(syscall.Signal))
|
||||
}
|
||||
|
||||
func (p *process) startCmd(cmd *exec.Cmd) error {
|
||||
if err := cmd.Start(); err != nil {
|
||||
if exErr, ok := err.(*exec.Error); ok {
|
||||
if exErr.Err == exec.ErrNotFound || exErr.Err == os.ErrNotExist {
|
||||
return fmt.Errorf("%s not installed on system", p.container.shim)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := p.waitForStart(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *process) waitForStart(cmd *exec.Cmd) error {
|
||||
wc := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
if _, err := p.getPidFromFile(); err != nil {
|
||||
if os.IsNotExist(err) || err == errInvalidPidInt {
|
||||
alive, err := isAlive(cmd)
|
||||
if err != nil {
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
if !alive {
|
||||
// runc could have failed to run the container so lets get the error
|
||||
// out of the logs or the shim could have encountered an error
|
||||
messages, err := readLogMessages(filepath.Join(p.root, "shim-log.json"))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
for _, m := range messages {
|
||||
if m.Level == "error" {
|
||||
wc <- fmt.Errorf("shim error: %v", m.Msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
// no errors reported back from shim, check for runc/runtime errors
|
||||
messages, err = readLogMessages(filepath.Join(p.root, "log.json"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrContainerNotStarted
|
||||
}
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
for _, m := range messages {
|
||||
if m.Level == "error" {
|
||||
wc <- fmt.Errorf("oci runtime error: %v", m.Msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
wc <- ErrContainerNotStarted
|
||||
return
|
||||
}
|
||||
time.Sleep(15 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
wc <- err
|
||||
return
|
||||
}
|
||||
// the pid file was read successfully
|
||||
wc <- nil
|
||||
return
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case err := <-wc:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case <-time.After(p.container.timeout):
|
||||
cmd.Process.Kill()
|
||||
cmd.Wait()
|
||||
return ErrContainerStartTimeout
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user