github.com/gravitational/moby@v1.13.1/libcontainerd/client_unix.go (about) 1 // +build linux solaris 2 3 package libcontainerd 4 5 import ( 6 "encoding/json" 7 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 "sync" 12 13 "github.com/Sirupsen/logrus" 14 containerd "github.com/docker/containerd/api/grpc/types" 15 "github.com/docker/docker/pkg/idtools" 16 specs "github.com/opencontainers/runtime-spec/specs-go" 17 "golang.org/x/net/context" 18 ) 19 20 func (clnt *client) prepareBundleDir(uid, gid int) (string, error) { 21 root, err := filepath.Abs(clnt.remote.stateDir) 22 if err != nil { 23 return "", err 24 } 25 if uid == 0 && gid == 0 { 26 return root, nil 27 } 28 p := string(filepath.Separator) 29 for _, d := range strings.Split(root, string(filepath.Separator))[1:] { 30 p = filepath.Join(p, d) 31 fi, err := os.Stat(p) 32 if err != nil && !os.IsNotExist(err) { 33 return "", err 34 } 35 if os.IsNotExist(err) || fi.Mode()&1 == 0 { 36 p = fmt.Sprintf("%s.%d.%d", p, uid, gid) 37 if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) { 38 return "", err 39 } 40 } 41 } 42 return p, nil 43 } 44 45 func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) { 46 clnt.lock(containerID) 47 defer clnt.unlock(containerID) 48 49 if _, err := clnt.getContainer(containerID); err == nil { 50 return fmt.Errorf("Container %s is already active", containerID) 51 } 52 53 uid, gid, err := getRootIDs(specs.Spec(spec)) 54 if err != nil { 55 return err 56 } 57 dir, err := clnt.prepareBundleDir(uid, gid) 58 if err != nil { 59 return err 60 } 61 62 container := clnt.newContainer(filepath.Join(dir, containerID), options...) 63 if err := container.clean(); err != nil { 64 return err 65 } 66 67 defer func() { 68 if err != nil { 69 container.clean() 70 clnt.deleteContainer(containerID) 71 } 72 }() 73 74 if err := idtools.MkdirAllAs(container.dir, 0700, uid, gid); err != nil && !os.IsExist(err) { 75 return err 76 } 77 78 f, err := os.Create(filepath.Join(container.dir, configFilename)) 79 if err != nil { 80 return err 81 } 82 defer f.Close() 83 if err := json.NewEncoder(f).Encode(spec); err != nil { 84 return err 85 } 86 87 return container.start(checkpoint, checkpointDir, attachStdio) 88 } 89 90 func (clnt *client) Signal(containerID string, sig int) error { 91 clnt.lock(containerID) 92 defer clnt.unlock(containerID) 93 _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{ 94 Id: containerID, 95 Pid: InitFriendlyName, 96 Signal: uint32(sig), 97 }) 98 return err 99 } 100 101 func (clnt *client) newContainer(dir string, options ...CreateOption) *container { 102 container := &container{ 103 containerCommon: containerCommon{ 104 process: process{ 105 dir: dir, 106 processCommon: processCommon{ 107 containerID: filepath.Base(dir), 108 client: clnt, 109 friendlyName: InitFriendlyName, 110 }, 111 }, 112 processes: make(map[string]*process), 113 }, 114 } 115 for _, option := range options { 116 if err := option.Apply(container); err != nil { 117 logrus.Errorf("libcontainerd: newContainer(): %v", err) 118 } 119 } 120 return container 121 } 122 123 type exitNotifier struct { 124 id string 125 client *client 126 c chan struct{} 127 once sync.Once 128 } 129 130 func (en *exitNotifier) close() { 131 en.once.Do(func() { 132 close(en.c) 133 en.client.mapMutex.Lock() 134 if en == en.client.exitNotifiers[en.id] { 135 delete(en.client.exitNotifiers, en.id) 136 } 137 en.client.mapMutex.Unlock() 138 }) 139 } 140 func (en *exitNotifier) wait() <-chan struct{} { 141 return en.c 142 }