2016-01-06 21:32:46 +00:00
|
|
|
package runtime
|
|
|
|
|
|
|
|
import (
|
2016-02-01 19:02:41 +00:00
|
|
|
"encoding/json"
|
2016-02-02 22:21:25 +00:00
|
|
|
"fmt"
|
2016-02-01 19:02:41 +00:00
|
|
|
"io"
|
2016-01-06 21:32:46 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"syscall"
|
2016-02-02 22:21:25 +00:00
|
|
|
"time"
|
2016-01-06 21:32:46 +00:00
|
|
|
|
|
|
|
"github.com/opencontainers/specs"
|
|
|
|
)
|
|
|
|
|
2016-02-01 19:02:41 +00:00
|
|
|
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
|
2016-02-02 22:21:25 +00:00
|
|
|
CloseStdin() error
|
|
|
|
Resize(int, int) error
|
2016-02-01 19:02:41 +00:00
|
|
|
// 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)
|
2016-02-03 21:56:15 +00:00
|
|
|
// Spec returns the process spec that created the process
|
2016-02-01 19:02:41 +00:00
|
|
|
Spec() specs.Process
|
|
|
|
// Signal sends the provided signal to the process
|
|
|
|
Signal(os.Signal) error
|
|
|
|
// Container returns the container that the process belongs to
|
|
|
|
Container() Container
|
2016-02-03 22:30:45 +00:00
|
|
|
// Stdio of the container
|
|
|
|
Stdio() Stdio
|
|
|
|
// SystemPid is the pid on the system
|
|
|
|
SystemPid() int
|
2016-02-01 19:02:41 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 19:23:35 +00:00
|
|
|
type processConfig struct {
|
|
|
|
id string
|
|
|
|
root string
|
|
|
|
processSpec specs.Process
|
|
|
|
spec *specs.LinuxSpec
|
|
|
|
c *container
|
|
|
|
stdio Stdio
|
|
|
|
}
|
|
|
|
|
|
|
|
func newProcess(config *processConfig) (*process, error) {
|
2016-01-06 21:32:46 +00:00
|
|
|
p := &process{
|
2016-02-11 19:23:35 +00:00
|
|
|
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
|
2016-01-06 21:32:46 +00:00
|
|
|
}
|
2016-02-11 19:23:35 +00:00
|
|
|
f, err := os.Create(filepath.Join(config.root, "process.json"))
|
2016-02-01 19:02:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2016-02-03 21:56:15 +00:00
|
|
|
if err := json.NewEncoder(f).Encode(ProcessState{
|
2016-02-11 19:23:35 +00:00
|
|
|
Process: config.processSpec,
|
|
|
|
RootUID: uid,
|
|
|
|
RootGID: gid,
|
|
|
|
Stdin: config.stdio.Stdin,
|
|
|
|
Stdout: config.stdio.Stdout,
|
|
|
|
Stderr: config.stdio.Stderr,
|
2016-02-03 21:56:15 +00:00
|
|
|
}); err != nil {
|
2016-02-01 19:02:41 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-11 19:23:35 +00:00
|
|
|
exit, err := getExitPipe(filepath.Join(config.root, ExitFile))
|
2016-01-06 21:32:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-11 19:23:35 +00:00
|
|
|
control, err := getControlPipe(filepath.Join(config.root, ControlFile))
|
2016-02-02 22:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-01-06 21:32:46 +00:00
|
|
|
p.exitPipe = exit
|
2016-02-02 22:21:25 +00:00
|
|
|
p.controlPipe = control
|
2016-01-06 21:32:46 +00:00
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2016-02-03 21:56:15 +00:00
|
|
|
func loadProcess(root, id string, c *container, s *ProcessState) (*process, error) {
|
2016-01-06 21:32:46 +00:00
|
|
|
p := &process{
|
|
|
|
root: root,
|
|
|
|
id: id,
|
|
|
|
container: c,
|
2016-02-03 21:56:15 +00:00
|
|
|
spec: s.Process,
|
|
|
|
stdio: Stdio{
|
|
|
|
Stdin: s.Stdin,
|
|
|
|
Stdout: s.Stdout,
|
|
|
|
Stderr: s.Stderr,
|
|
|
|
},
|
2016-01-06 21:32:46 +00:00
|
|
|
}
|
2016-02-03 22:30:45 +00:00
|
|
|
if _, err := p.getPid(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-01-06 21:32:46 +00:00
|
|
|
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
|
|
|
|
}
|
2016-02-01 19:02:41 +00:00
|
|
|
return p, nil
|
2016-01-06 21:32:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getExitPipe(path string) (*os.File, error) {
|
|
|
|
if err := syscall.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)
|
|
|
|
}
|
|
|
|
|
2016-02-02 22:21:25 +00:00
|
|
|
func getControlPipe(path string) (*os.File, error) {
|
|
|
|
if err := syscall.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return os.OpenFile(path, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:32:46 +00:00
|
|
|
type process struct {
|
2016-02-03 21:56:15 +00:00
|
|
|
root string
|
|
|
|
id string
|
|
|
|
pid int
|
2016-02-02 22:21:25 +00:00
|
|
|
exitPipe *os.File
|
|
|
|
controlPipe *os.File
|
|
|
|
container *container
|
|
|
|
spec specs.Process
|
2016-02-03 21:56:15 +00:00
|
|
|
stdio Stdio
|
2016-01-06 21:32:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *process) ID() string {
|
|
|
|
return p.id
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *process) Container() Container {
|
|
|
|
return p.container
|
|
|
|
}
|
|
|
|
|
2016-02-03 22:30:45 +00:00
|
|
|
func (p *process) SystemPid() int {
|
|
|
|
return p.pid
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:32:46 +00:00
|
|
|
// ExitFD returns the fd of the exit pipe
|
|
|
|
func (p *process) ExitFD() int {
|
|
|
|
return int(p.exitPipe.Fd())
|
|
|
|
}
|
|
|
|
|
2016-02-02 22:21:25 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:32:46 +00:00
|
|
|
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
|
|
|
|
}
|
2016-02-02 22:21:25 +00:00
|
|
|
return strconv.Atoi(string(data))
|
2016-01-06 21:32:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Signal sends the provided signal to the process
|
|
|
|
func (p *process) Signal(s os.Signal) error {
|
2016-02-02 22:21:25 +00:00
|
|
|
return syscall.Kill(p.pid, s.(syscall.Signal))
|
2016-01-06 21:32:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *process) Spec() specs.Process {
|
|
|
|
return p.spec
|
|
|
|
}
|
|
|
|
|
2016-02-03 22:30:45 +00:00
|
|
|
func (p *process) Stdio() Stdio {
|
|
|
|
return p.stdio
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:32:46 +00:00
|
|
|
// Close closes any open files and/or resouces on the process
|
|
|
|
func (p *process) Close() error {
|
|
|
|
return p.exitPipe.Close()
|
|
|
|
}
|
2016-02-02 22:21:25 +00:00
|
|
|
|
|
|
|
func (p *process) getPid() (int, error) {
|
|
|
|
for i := 0; i < 20; i++ {
|
|
|
|
data, err := ioutil.ReadFile(filepath.Join(p.root, "pid"))
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
i, err := strconv.Atoi(string(data))
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
p.pid = i
|
|
|
|
return i, nil
|
|
|
|
}
|
|
|
|
return -1, fmt.Errorf("containerd: cannot read pid file")
|
|
|
|
}
|