libcontainerd/client_unix.go
934328d8
 // +build linux solaris
 
 package libcontainerd
 
 import (
 	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
 	"strings"
 	"sync"
 
 	"github.com/Sirupsen/logrus"
 	containerd "github.com/docker/containerd/api/grpc/types"
 	"github.com/docker/docker/pkg/idtools"
 	specs "github.com/opencontainers/runtime-spec/specs-go"
 	"golang.org/x/net/context"
 )
 
 func (clnt *client) prepareBundleDir(uid, gid int) (string, error) {
 	root, err := filepath.Abs(clnt.remote.stateDir)
 	if err != nil {
 		return "", err
 	}
 	if uid == 0 && gid == 0 {
 		return root, nil
 	}
 	p := string(filepath.Separator)
 	for _, d := range strings.Split(root, string(filepath.Separator))[1:] {
 		p = filepath.Join(p, d)
 		fi, err := os.Stat(p)
 		if err != nil && !os.IsNotExist(err) {
 			return "", err
 		}
 		if os.IsNotExist(err) || fi.Mode()&1 == 0 {
 			p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
 			if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) {
 				return "", err
 			}
 		}
 	}
 	return p, nil
 }
 
 func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) {
 	clnt.lock(containerID)
 	defer clnt.unlock(containerID)
 
 	if _, err := clnt.getContainer(containerID); err == nil {
 		return fmt.Errorf("Container %s is already active", containerID)
 	}
 
 	uid, gid, err := getRootIDs(specs.Spec(spec))
 	if err != nil {
 		return err
 	}
 	dir, err := clnt.prepareBundleDir(uid, gid)
 	if err != nil {
 		return err
 	}
 
 	container := clnt.newContainer(filepath.Join(dir, containerID), options...)
 	if err := container.clean(); err != nil {
 		return err
 	}
 
 	defer func() {
 		if err != nil {
 			container.clean()
 			clnt.deleteContainer(containerID)
 		}
 	}()
 
 	if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) {
 		return err
 	}
 
 	f, err := os.Create(filepath.Join(container.dir, configFilename))
 	if err != nil {
 		return err
 	}
 	defer f.Close()
 	if err := json.NewEncoder(f).Encode(spec); err != nil {
 		return err
 	}
 
 	return container.start(checkpoint, checkpointDir, attachStdio)
 }
 
 func (clnt *client) Signal(containerID string, sig int) error {
 	clnt.lock(containerID)
 	defer clnt.unlock(containerID)
 	_, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{
 		Id:     containerID,
 		Pid:    InitFriendlyName,
 		Signal: uint32(sig),
 	})
 	return err
 }
 
 func (clnt *client) newContainer(dir string, options ...CreateOption) *container {
 	container := &container{
 		containerCommon: containerCommon{
 			process: process{
 				dir: dir,
 				processCommon: processCommon{
 					containerID:  filepath.Base(dir),
 					client:       clnt,
 					friendlyName: InitFriendlyName,
 				},
 			},
 			processes: make(map[string]*process),
 		},
 	}
 	for _, option := range options {
 		if err := option.Apply(container); err != nil {
 			logrus.Errorf("libcontainerd: newContainer(): %v", err)
 		}
 	}
 	return container
 }
 
 type exitNotifier struct {
 	id     string
 	client *client
 	c      chan struct{}
 	once   sync.Once
 }
 
 func (en *exitNotifier) close() {
 	en.once.Do(func() {
 		close(en.c)
 		en.client.mapMutex.Lock()
 		if en == en.client.exitNotifiers[en.id] {
 			delete(en.client.exitNotifiers, en.id)
 		}
 		en.client.mapMutex.Unlock()
 	})
 }
 func (en *exitNotifier) wait() <-chan struct{} {
 	return en.c
 }