1// +build linux 2 3/* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17*/ 18 19package linux 20 21import ( 22 "context" 23 "crypto/sha256" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 29 "github.com/containerd/containerd/events/exchange" 30 "github.com/containerd/containerd/runtime/linux/runctypes" 31 "github.com/containerd/containerd/runtime/v1/shim" 32 "github.com/containerd/containerd/runtime/v1/shim/client" 33 "github.com/pkg/errors" 34) 35 36// loadBundle loads an existing bundle from disk 37func loadBundle(id, path, workdir string) *bundle { 38 return &bundle{ 39 id: id, 40 path: path, 41 workDir: workdir, 42 } 43} 44 45// newBundle creates a new bundle on disk at the provided path for the given id 46func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) { 47 if err := os.MkdirAll(path, 0711); err != nil { 48 return nil, err 49 } 50 path = filepath.Join(path, id) 51 if err := os.Mkdir(path, 0711); err != nil { 52 return nil, err 53 } 54 defer func() { 55 if err != nil { 56 os.RemoveAll(path) 57 } 58 }() 59 workDir = filepath.Join(workDir, id) 60 if err := os.MkdirAll(workDir, 0711); err != nil { 61 return nil, err 62 } 63 defer func() { 64 if err != nil { 65 os.RemoveAll(workDir) 66 } 67 }() 68 err = ioutil.WriteFile(filepath.Join(path, configFilename), spec, 0666) 69 return &bundle{ 70 id: id, 71 path: path, 72 workDir: workDir, 73 }, err 74} 75 76type bundle struct { 77 id string 78 path string 79 workDir string 80} 81 82// ShimOpt specifies shim options for initialization and connection 83type ShimOpt func(*bundle, string, *runctypes.RuncOptions) (shim.Config, client.Opt) 84 85// ShimRemote is a ShimOpt for connecting and starting a remote shim 86func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) ShimOpt { 87 return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { 88 config := b.shimConfig(ns, c, ropts) 89 return config, 90 client.WithStart(c.Shim, b.shimAddress(ns), daemonAddress, cgroup, c.ShimDebug, exitHandler) 91 } 92} 93 94// ShimLocal is a ShimOpt for using an in process shim implementation 95func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt { 96 return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { 97 return b.shimConfig(ns, c, ropts), client.WithLocal(exchange) 98 } 99} 100 101// ShimConnect is a ShimOpt for connecting to an existing remote shim 102func ShimConnect(c *Config, onClose func()) ShimOpt { 103 return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { 104 return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose) 105 } 106} 107 108// NewShimClient connects to the shim managing the bundle and tasks creating it if needed 109func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts ShimOpt, runcOpts *runctypes.RuncOptions) (*client.Client, error) { 110 cfg, opt := getClientOpts(b, namespace, runcOpts) 111 return client.New(ctx, cfg, opt) 112} 113 114// Delete deletes the bundle from disk 115func (b *bundle) Delete() error { 116 err := atomicDelete(b.path) 117 if err == nil { 118 return atomicDelete(b.workDir) 119 } 120 // error removing the bundle path; still attempt removing work dir 121 err2 := atomicDelete(b.workDir) 122 if err2 == nil { 123 return err 124 } 125 return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2) 126} 127 128func (b *bundle) legacyShimAddress(namespace string) string { 129 return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock") 130} 131 132func (b *bundle) shimAddress(namespace string) string { 133 d := sha256.Sum256([]byte(filepath.Join(namespace, b.id))) 134 return filepath.Join(string(filepath.Separator), "containerd-shim", fmt.Sprintf("%x.sock", d)) 135} 136 137func (b *bundle) loadAddress() (string, error) { 138 addressPath := filepath.Join(b.path, "address") 139 data, err := ioutil.ReadFile(addressPath) 140 if err != nil { 141 return "", err 142 } 143 return string(data), nil 144} 145 146func (b *bundle) decideShimAddress(namespace string) string { 147 address, err := b.loadAddress() 148 if err != nil { 149 return b.legacyShimAddress(namespace) 150 } 151 return address 152} 153 154func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config { 155 var ( 156 criuPath string 157 runtimeRoot = c.RuntimeRoot 158 systemdCgroup bool 159 ) 160 if runcOptions != nil { 161 criuPath = runcOptions.CriuPath 162 systemdCgroup = runcOptions.SystemdCgroup 163 if runcOptions.RuntimeRoot != "" { 164 runtimeRoot = runcOptions.RuntimeRoot 165 } 166 } 167 return shim.Config{ 168 Path: b.path, 169 WorkDir: b.workDir, 170 Namespace: namespace, 171 Criu: criuPath, 172 RuntimeRoot: runtimeRoot, 173 SystemdCgroup: systemdCgroup, 174 } 175} 176 177// atomicDelete renames the path to a hidden file before removal 178func atomicDelete(path string) error { 179 // create a hidden dir for an atomic removal 180 atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) 181 if err := os.Rename(path, atomicPath); err != nil { 182 return err 183 } 184 return os.RemoveAll(atomicPath) 185} 186