142 lines
2.8 KiB
Go
142 lines
2.8 KiB
Go
|
package runtime
|
||
|
|
||
|
import (
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strconv"
|
||
|
"syscall"
|
||
|
|
||
|
"github.com/opencontainers/specs"
|
||
|
)
|
||
|
|
||
|
func newProcess(root, id string, c *container, s specs.Process) (*process, error) {
|
||
|
p := &process{
|
||
|
root: root,
|
||
|
id: id,
|
||
|
container: c,
|
||
|
spec: s,
|
||
|
}
|
||
|
// create fifo's for the process
|
||
|
for name, fd := range map[string]*string{
|
||
|
"stdin": &p.stdin,
|
||
|
"stdout": &p.stdout,
|
||
|
"stderr": &p.stderr,
|
||
|
} {
|
||
|
path := filepath.Join(root, name)
|
||
|
if err := syscall.Mkfifo(path, 0755); err != nil && !os.IsExist(err) {
|
||
|
return nil, err
|
||
|
}
|
||
|
*fd = path
|
||
|
}
|
||
|
exit, err := getExitPipe(filepath.Join(root, ExitFile))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
p.exitPipe = exit
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
func loadProcess(root, id string, c *container, s specs.Process) (*process, error) {
|
||
|
p := &process{
|
||
|
root: root,
|
||
|
id: id,
|
||
|
container: c,
|
||
|
spec: s,
|
||
|
stdin: filepath.Join(root, "stdin"),
|
||
|
stdout: filepath.Join(root, "stdout"),
|
||
|
stderr: filepath.Join(root, "stderr"),
|
||
|
}
|
||
|
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 nil, ErrProcessExited
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
type process struct {
|
||
|
root string
|
||
|
id string
|
||
|
// stdio fifos
|
||
|
stdin string
|
||
|
stdout string
|
||
|
stderr string
|
||
|
|
||
|
exitPipe *os.File
|
||
|
container *container
|
||
|
spec specs.Process
|
||
|
}
|
||
|
|
||
|
func (p *process) ID() string {
|
||
|
return p.id
|
||
|
}
|
||
|
|
||
|
func (p *process) Container() Container {
|
||
|
return p.container
|
||
|
}
|
||
|
|
||
|
// ExitFD returns the fd of the exit pipe
|
||
|
func (p *process) ExitFD() int {
|
||
|
return int(p.exitPipe.Fd())
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
i, err := strconv.Atoi(string(data))
|
||
|
if err != nil {
|
||
|
return -1, err
|
||
|
}
|
||
|
return i, nil
|
||
|
}
|
||
|
|
||
|
// Signal sends the provided signal to the process
|
||
|
func (p *process) Signal(s os.Signal) error {
|
||
|
return errNotImplemented
|
||
|
}
|
||
|
|
||
|
func (p *process) Spec() specs.Process {
|
||
|
return p.spec
|
||
|
}
|
||
|
|
||
|
func (p *process) Stdin() string {
|
||
|
return p.stdin
|
||
|
}
|
||
|
|
||
|
func (p *process) Stdout() string {
|
||
|
return p.stdout
|
||
|
}
|
||
|
|
||
|
func (p *process) Stderr() string {
|
||
|
return p.stderr
|
||
|
}
|
||
|
|
||
|
// Close closes any open files and/or resouces on the process
|
||
|
func (p *process) Close() error {
|
||
|
return p.exitPipe.Close()
|
||
|
}
|