| // +build linux | |
| package linux | |
| import ( | |
| "fmt" | |
| "io/ioutil" | |
| "log" | |
| "net" | |
| "os/exec" | |
| "path/filepath" | |
| "syscall" | |
| "time" | |
| "google.golang.org/grpc" | |
| "google.golang.org/grpc/grpclog" | |
| "github.com/containerd/containerd/api/services/shim" | |
| localShim "github.com/containerd/containerd/linux/shim" | |
| "github.com/containerd/containerd/reaper" | |
| "github.com/containerd/containerd/sys" | |
| "github.com/pkg/errors" | |
| ) | |
| func newShim(shimName string, path string, remote bool) (shim.ShimClient, error) { | |
| if !remote { | |
| return localShim.Client(path) | |
| } | |
| socket := filepath.Join(path, "shim.sock") | |
| l, err := sys.CreateUnixSocket(socket) | |
| if err != nil { | |
| return nil, err | |
| } | |
| cmd := exec.Command(shimName) | |
| cmd.Dir = path | |
| f, err := l.(*net.UnixListener).File() | |
| if err != nil { | |
| return nil, err | |
| } | |
| // close our side of the socket, do not close the listener as it will | |
| // remove the socket from disk | |
| defer f.Close() | |
| cmd.ExtraFiles = append(cmd.ExtraFiles, f) | |
| // make sure the shim can be re-parented to system init | |
| // and is cloned in a new mount namespace because the overlay/filesystems | |
| // will be mounted by the shim | |
| cmd.SysProcAttr = &syscall.SysProcAttr{ | |
| Cloneflags: syscall.CLONE_NEWNS, | |
| Setpgid: true, | |
| } | |
| if err := reaper.Default.Start(cmd); err != nil { | |
| return nil, errors.Wrapf(err, "failed to start shim") | |
| } | |
| if err := sys.SetOOMScore(cmd.Process.Pid, sys.OOMScoreMaxKillable); err != nil { | |
| return nil, err | |
| } | |
| return connectShim(socket) | |
| } | |
| func loadShim(path string, remote bool) (shim.ShimClient, error) { | |
| if !remote { | |
| return localShim.Client(path) | |
| } | |
| socket := filepath.Join(path, "shim.sock") | |
| return connectShim(socket) | |
| } | |
| func connectShim(socket string) (shim.ShimClient, error) { | |
| // reset the logger for grpc to log to dev/null so that it does not mess with our stdio | |
| grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) | |
| dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)} | |
| dialOpts = append(dialOpts, | |
| grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { | |
| return net.DialTimeout("unix", socket, timeout) | |
| }), | |
| grpc.WithBlock(), | |
| grpc.WithTimeout(2*time.Second), | |
| ) | |
| conn, err := grpc.Dial(fmt.Sprintf("unix://%s", socket), dialOpts...) | |
| if err != nil { | |
| return nil, errors.Wrapf(err, "failed to connect to shim via \"%s\"", fmt.Sprintf("unix://%s", socket)) | |
| } | |
| return shim.NewShimClient(conn), nil | |
| } |