934328d8 |
// +build linux solaris
|
9c4570a9 |
package libcontainerd
import (
"encoding/json" |
d705dab1 |
"io" |
9c4570a9 |
"io/ioutil"
"os"
"path/filepath" |
6f2658fb |
"sync" |
6d264645 |
"time" |
9c4570a9 |
"github.com/Sirupsen/logrus" |
0ea0b2be |
containerd "github.com/containerd/containerd/api/grpc/types" |
02d19342 |
"github.com/docker/docker/pkg/ioutils" |
ee9d28bd |
specs "github.com/opencontainers/runtime-spec/specs-go" |
6d264645 |
"github.com/tonistiigi/fifo" |
9c4570a9 |
"golang.org/x/net/context" |
069fdc8a |
"golang.org/x/sys/unix" |
9c4570a9 |
)
type container struct {
containerCommon
// Platform specific fields are below here.
pauseMonitor |
7b2e5216 |
oom bool
runtime string
runtimeArgs []string
}
type runtime struct {
path string
args []string
}
// WithRuntime sets the runtime to be used for the created container
func WithRuntime(path string, args []string) CreateOption {
return runtime{path, args}
}
func (rt runtime) Apply(p interface{}) error {
if pr, ok := p.(*container); ok {
pr.runtime = rt.path
pr.runtimeArgs = rt.args
}
return nil |
9c4570a9 |
}
func (ctr *container) clean() error { |
7bf07737 |
if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" {
return nil
} |
9c4570a9 |
if _, err := os.Lstat(ctr.dir); err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
if err := os.RemoveAll(ctr.dir); err != nil {
return err
}
return nil
}
|
84d17012 |
// cleanProcess removes the fifos used by an additional process.
// Caller needs to lock container ID before calling this method.
func (ctr *container) cleanProcess(id string) {
if p, ok := ctr.processes[id]; ok { |
069fdc8a |
for _, i := range []int{unix.Stdin, unix.Stdout, unix.Stderr} { |
ee9d28bd |
if err := os.Remove(p.fifo(i)); err != nil && !os.IsNotExist(err) { |
5231c553 |
logrus.Warnf("libcontainerd: failed to remove %v for process %v: %v", p.fifo(i), id, err) |
84d17012 |
}
}
}
delete(ctr.processes, id)
}
|
9c4570a9 |
func (ctr *container) spec() (*specs.Spec, error) {
var spec specs.Spec
dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename))
if err != nil {
return nil, err
}
if err := json.Unmarshal(dt, &spec); err != nil {
return nil, err
}
return &spec, nil
}
|
8d588d9c |
func (ctr *container) start(spec *specs.Spec, checkpoint, checkpointDir string, attachStdio StdioCallback) (err error) { |
6f2658fb |
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ready := make(chan struct{})
|
c178700a |
fifoCtx, cancel := context.WithCancel(context.Background())
defer func() {
if err != nil {
cancel()
}
}()
iopipe, err := ctr.openFifos(fifoCtx, spec.Process.Terminal) |
9c4570a9 |
if err != nil {
return err
}
|
6f2658fb |
var stdinOnce sync.Once
|
02d19342 |
// we need to delay stdin closure after container start or else "stdin close"
// event will be rejected by containerd. |
37a3be24 |
// stdin closure happens in attachStdio |
02d19342 |
stdin := iopipe.Stdin
iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error { |
6f2658fb |
var err error
stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed
err = stdin.Close()
go func() {
select {
case <-ready: |
4e262f63 |
case <-ctx.Done():
}
select {
case <-ready: |
6f2658fb |
if err := ctr.sendCloseStdin(); err != nil { |
aa01ee4a |
logrus.Warnf("failed to close stdin: %+v", err) |
6f2658fb |
} |
4e262f63 |
default: |
6f2658fb |
}
}()
})
return err |
02d19342 |
})
|
9c4570a9 |
r := &containerd.CreateContainerRequest{ |
d8fef66b |
Id: ctr.containerID,
BundlePath: ctr.dir, |
069fdc8a |
Stdin: ctr.fifo(unix.Stdin),
Stdout: ctr.fifo(unix.Stdout),
Stderr: ctr.fifo(unix.Stderr), |
d8fef66b |
Checkpoint: checkpoint,
CheckpointDir: checkpointDir, |
8a4225cd |
// check to see if we are running in ramdisk to disable pivot root
NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", |
7b2e5216 |
Runtime: ctr.runtime,
RuntimeArgs: ctr.runtimeArgs, |
9c4570a9 |
}
ctr.client.appendContainer(ctr)
|
37a3be24 |
if err := attachStdio(*iopipe); err != nil { |
9c4570a9 |
ctr.closeFifos(iopipe)
return err
}
|
02d19342 |
resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
if err != nil {
ctr.closeFifos(iopipe) |
9c4570a9 |
return err
}
ctr.systemPid = systemPid(resp.Container) |
6f2658fb |
close(ready) |
9c4570a9 |
return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ |
818a5198 |
CommonStateInfo: CommonStateInfo{
State: StateStart,
Pid: ctr.systemPid,
}}) |
8d588d9c |
|
9c4570a9 |
}
func (ctr *container) newProcess(friendlyName string) *process {
return &process{
dir: ctr.dir,
processCommon: processCommon{
containerID: ctr.containerID,
friendlyName: friendlyName,
client: ctr.client,
},
}
}
func (ctr *container) handleEvent(e *containerd.Event) error {
ctr.client.lock(ctr.containerID)
defer ctr.client.unlock(ctr.containerID)
switch e.Type {
case StateExit, StatePause, StateResume, StateOOM:
st := StateInfo{ |
818a5198 |
CommonStateInfo: CommonStateInfo{
State: e.Type,
ExitCode: e.Status,
}, |
9c4570a9 |
OOMKilled: e.Type == StateExit && ctr.oom,
}
if e.Type == StateOOM {
ctr.oom = true
}
if e.Type == StateExit && e.Pid != InitFriendlyName {
st.ProcessID = e.Pid
st.State = StateExitProcess
} |
495448b2 |
// Remove process from list if we have exited
switch st.State {
case StateExit:
ctr.clean()
ctr.client.deleteContainer(e.Id)
case StateExitProcess:
ctr.cleanProcess(st.ProcessID)
}
ctr.client.q.append(e.Id, func() {
if err := ctr.client.backend.StateChanged(e.Id, st); err != nil {
logrus.Errorf("libcontainerd: backend.StateChanged(): %v", err)
} |
9c4570a9 |
if e.Type == StatePause || e.Type == StateResume {
ctr.pauseMonitor.handle(e.Type)
}
if e.Type == StateExit {
if en := ctr.client.getExitNotifier(e.Id); en != nil {
en.close()
}
}
})
default: |
5231c553 |
logrus.Debugf("libcontainerd: event unhandled: %+v", e) |
9c4570a9 |
}
return nil
} |
d705dab1 |
// discardFifos attempts to fully read the container fifos to unblock processes
// that may be blocked on the writer side.
func (ctr *container) discardFifos() { |
6d264645 |
ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) |
069fdc8a |
for _, i := range []int{unix.Stdout, unix.Stderr} {
f, err := fifo.OpenFifo(ctx, ctr.fifo(i), unix.O_RDONLY|unix.O_NONBLOCK, 0) |
6d264645 |
if err != nil {
logrus.Warnf("error opening fifo %v for discarding: %+v", f, err)
continue
} |
d705dab1 |
go func() { |
6d264645 |
io.Copy(ioutil.Discard, f) |
d705dab1 |
}()
}
} |