Merge pull request #861 from justincormack/go-runc-port
Portability fixes for containerd shim
This commit is contained in:
commit
47718b0930
@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@ -17,9 +16,7 @@ import (
|
||||
shimapi "github.com/containerd/containerd/api/services/shim"
|
||||
"github.com/containerd/containerd/linux/shim"
|
||||
"github.com/containerd/containerd/reaper"
|
||||
"github.com/containerd/containerd/sys"
|
||||
"github.com/containerd/containerd/version"
|
||||
runc "github.com/containerd/go-runc"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@ -81,21 +78,6 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// setupSignals creates a new signal handler for all signals and sets the shim as a
|
||||
// sub-reaper so that the container processes are reparented
|
||||
func setupSignals() (chan os.Signal, error) {
|
||||
signals := make(chan os.Signal, 2048)
|
||||
signal.Notify(signals)
|
||||
// make sure runc is setup to use the monitor
|
||||
// for waiting on processes
|
||||
runc.Monitor = reaper.Default
|
||||
// set the shim as the subreaper for all orphaned processes created by the container
|
||||
if err := sys.SetSubreaper(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signals, nil
|
||||
}
|
||||
|
||||
// serve serves the grpc API over a unix socket at the provided path
|
||||
// this function does not block
|
||||
func serve(server *grpc.Server, path string) error {
|
||||
@ -131,8 +113,3 @@ func handleSignals(signals chan os.Signal, server *grpc.Server) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupRoot sets up the root as the shim is started in its own mount namespace
|
||||
func setupRoot() error {
|
||||
return unix.Mount("", "/", "", unix.MS_SLAVE|unix.MS_REC, "")
|
||||
}
|
||||
|
32
cmd/containerd-shim/shim_linux.go
Normal file
32
cmd/containerd-shim/shim_linux.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/containerd/containerd/reaper"
|
||||
"github.com/containerd/containerd/sys"
|
||||
runc "github.com/containerd/go-runc"
|
||||
)
|
||||
|
||||
// setupSignals creates a new signal handler for all signals and sets the shim as a
|
||||
// sub-reaper so that the container processes are reparented
|
||||
func setupSignals() (chan os.Signal, error) {
|
||||
signals := make(chan os.Signal, 2048)
|
||||
signal.Notify(signals)
|
||||
// make sure runc is setup to use the monitor
|
||||
// for waiting on processes
|
||||
runc.Monitor = reaper.Default
|
||||
// set the shim as the subreaper for all orphaned processes created by the container
|
||||
if err := sys.SetSubreaper(1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signals, nil
|
||||
}
|
||||
|
||||
// setupRoot sets up the root as the shim is started in its own mount namespace
|
||||
func setupRoot() error {
|
||||
return unix.Mount("", "/", "", unix.MS_SLAVE|unix.MS_REC, "")
|
||||
}
|
27
cmd/containerd-shim/shim_unix.go
Normal file
27
cmd/containerd-shim/shim_unix.go
Normal file
@ -0,0 +1,27 @@
|
||||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/containerd/containerd/reaper"
|
||||
runc "github.com/containerd/go-runc"
|
||||
)
|
||||
|
||||
// setupSignals creates a new signal handler for all signals and sets the shim as a
|
||||
// sub-reaper so that the container processes are reparented
|
||||
func setupSignals() (chan os.Signal, error) {
|
||||
signals := make(chan os.Signal, 2048)
|
||||
signal.Notify(signals)
|
||||
// make sure runc is setup to use the monitor
|
||||
// for waiting on processes
|
||||
runc.Monitor = reaper.Default
|
||||
return signals, nil
|
||||
}
|
||||
|
||||
// setupRoot is a no op except on Linux
|
||||
func setupRoot() error {
|
||||
return nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build !windows
|
||||
|
||||
package shim
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build !windows
|
||||
|
||||
package shim
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build !windows
|
||||
|
||||
package shim
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build !windows
|
||||
|
||||
package shim
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// +build linux
|
||||
// +build !windows
|
||||
|
||||
package shim
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
|
||||
github.com/containerd/go-runc 5fe4d8cb7fdc0fae5f5a7f4f1d65a565032401b2
|
||||
github.com/containerd/go-runc 66d084de463f26efe84e6a5669e573fa6202e814
|
||||
github.com/containerd/console a3863895279f5104533fd999c1babf80faffd98c
|
||||
github.com/containerd/cgroups 7b2d1a0f50963678d5799e29d17a4d611f5a5dee
|
||||
github.com/docker/go-metrics 8fd5772bf1584597834c6f7961a530f06cbfbb87
|
||||
|
21
vendor/github.com/containerd/go-runc/command_linux.go
generated
vendored
Normal file
21
vendor/github.com/containerd/go-runc/command_linux.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
package runc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (r *Runc) command(context context.Context, args ...string) *exec.Cmd {
|
||||
command := r.Command
|
||||
if command == "" {
|
||||
command = DefaultCommand
|
||||
}
|
||||
cmd := exec.CommandContext(context, command, append(r.args(), args...)...)
|
||||
if r.PdeathSignal != 0 {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Pdeathsig: r.PdeathSignal,
|
||||
}
|
||||
}
|
||||
return cmd
|
||||
}
|
16
vendor/github.com/containerd/go-runc/command_other.go
generated
vendored
Normal file
16
vendor/github.com/containerd/go-runc/command_other.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// +build !linux
|
||||
|
||||
package runc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func (r *Runc) command(context context.Context, args ...string) *exec.Cmd {
|
||||
command := r.Command
|
||||
if command == "" {
|
||||
command = DefaultCommand
|
||||
}
|
||||
return exec.CommandContext(context, command, append(r.args(), args...)...)
|
||||
}
|
75
vendor/github.com/containerd/go-runc/console.go
generated
vendored
75
vendor/github.com/containerd/go-runc/console.go
generated
vendored
@ -10,7 +10,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// NewConsoleSocket creates a new unix socket at the provided path to accept a
|
||||
@ -20,13 +20,16 @@ func NewConsoleSocket(path string) (*Socket, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := net.Listen("unix", abs)
|
||||
addr, err := net.ResolveUnixAddr("unix", abs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := net.ListenUnix("unix", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Socket{
|
||||
l: l,
|
||||
path: abs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -41,27 +44,73 @@ func NewTempConsoleSocket() (*Socket, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := net.Listen("unix", abs)
|
||||
addr, err := net.ResolveUnixAddr("unix", abs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := net.ListenUnix("unix", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Socket{
|
||||
l: l,
|
||||
rmdir: true,
|
||||
path: abs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Socket is a unix socket that accepts the pty master created by runc
|
||||
type Socket struct {
|
||||
path string
|
||||
rmdir bool
|
||||
l net.Listener
|
||||
l *net.UnixListener
|
||||
}
|
||||
|
||||
// Path returns the path to the unix socket on disk
|
||||
func (c *Socket) Path() string {
|
||||
return c.path
|
||||
return c.l.Addr().String()
|
||||
}
|
||||
|
||||
// recvFd waits for a file descriptor to be sent over the given AF_UNIX
|
||||
// socket. The file name of the remote file descriptor will be recreated
|
||||
// locally (it is sent as non-auxiliary data in the same payload).
|
||||
func recvFd(socket *net.UnixConn) (*os.File, error) {
|
||||
const MaxNameLen = 4096
|
||||
var oobSpace = unix.CmsgSpace(4)
|
||||
|
||||
name := make([]byte, MaxNameLen)
|
||||
oob := make([]byte, oobSpace)
|
||||
|
||||
n, oobn, _, _, err := socket.ReadMsgUnix(name, oob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n >= MaxNameLen || oobn != oobSpace {
|
||||
return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
|
||||
}
|
||||
|
||||
// Truncate.
|
||||
name = name[:n]
|
||||
oob = oob[:oobn]
|
||||
|
||||
scms, err := unix.ParseSocketControlMessage(oob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
|
||||
}
|
||||
scm := scms[0]
|
||||
|
||||
fds, err := unix.ParseUnixRights(&scm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fds) != 1 {
|
||||
return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
|
||||
}
|
||||
fd := uintptr(fds[0])
|
||||
|
||||
return os.NewFile(fd, string(name)), nil
|
||||
}
|
||||
|
||||
// ReceiveMaster blocks until the socket receives the pty master
|
||||
@ -71,15 +120,11 @@ func (c *Socket) ReceiveMaster() (console.Console, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
unix, ok := conn.(*net.UnixConn)
|
||||
uc, ok := conn.(*net.UnixConn)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("received connection which was not a unix socket")
|
||||
}
|
||||
sock, err := unix.File()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := utils.RecvFd(sock)
|
||||
f, err := recvFd(uc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -90,7 +135,7 @@ func (c *Socket) ReceiveMaster() (console.Console, error) {
|
||||
func (c *Socket) Close() error {
|
||||
err := c.l.Close()
|
||||
if c.rmdir {
|
||||
if rerr := os.RemoveAll(filepath.Dir(c.path)); err == nil {
|
||||
if rerr := os.RemoveAll(filepath.Dir(c.Path())); err == nil {
|
||||
err = rerr
|
||||
}
|
||||
}
|
||||
|
14
vendor/github.com/containerd/go-runc/runc.go
generated
vendored
14
vendor/github.com/containerd/go-runc/runc.go
generated
vendored
@ -523,20 +523,6 @@ func (r *Runc) args() (out []string) {
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *Runc) command(context context.Context, args ...string) *exec.Cmd {
|
||||
command := r.Command
|
||||
if command == "" {
|
||||
command = DefaultCommand
|
||||
}
|
||||
cmd := exec.CommandContext(context, command, append(r.args(), args...)...)
|
||||
if r.PdeathSignal != 0 {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Pdeathsig: r.PdeathSignal,
|
||||
}
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runOrError will run the provided command. If an error is
|
||||
// encountered and neither Stdout or Stderr was set the error and the
|
||||
// stderr of the command will be returned in the format of <error>:
|
||||
|
95
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
95
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
@ -1,95 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package utils
|
||||
|
||||
/*
|
||||
* Copyright 2016, 2017 SUSE LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// MaxSendfdLen is the maximum length of the name of a file descriptor being
|
||||
// sent using SendFd. The name of the file handle returned by RecvFd will never
|
||||
// be larger than this value.
|
||||
const MaxNameLen = 4096
|
||||
|
||||
// oobSpace is the size of the oob slice required to store a single FD. Note
|
||||
// that unix.UnixRights appears to make the assumption that fd is always int32,
|
||||
// so sizeof(fd) = 4.
|
||||
var oobSpace = unix.CmsgSpace(4)
|
||||
|
||||
// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
|
||||
// socket. The file name of the remote file descriptor will be recreated
|
||||
// locally (it is sent as non-auxiliary data in the same payload).
|
||||
func RecvFd(socket *os.File) (*os.File, error) {
|
||||
// For some reason, unix.Recvmsg uses the length rather than the capacity
|
||||
// when passing the msg_controllen and other attributes to recvmsg. So we
|
||||
// have to actually set the length.
|
||||
name := make([]byte, MaxNameLen)
|
||||
oob := make([]byte, oobSpace)
|
||||
|
||||
sockfd := socket.Fd()
|
||||
n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n >= MaxNameLen || oobn != oobSpace {
|
||||
return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn)
|
||||
}
|
||||
|
||||
// Truncate.
|
||||
name = name[:n]
|
||||
oob = oob[:oobn]
|
||||
|
||||
scms, err := unix.ParseSocketControlMessage(oob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms))
|
||||
}
|
||||
scm := scms[0]
|
||||
|
||||
fds, err := unix.ParseUnixRights(&scm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fds) != 1 {
|
||||
return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds))
|
||||
}
|
||||
fd := uintptr(fds[0])
|
||||
|
||||
return os.NewFile(fd, string(name)), nil
|
||||
}
|
||||
|
||||
// SendFd sends a file descriptor over the given AF_UNIX socket. In
|
||||
// addition, the file.Name() of the given file will also be sent as
|
||||
// non-auxiliary data in the same payload (allowing to send contextual
|
||||
// information for a file descriptor).
|
||||
func SendFd(socket, file *os.File) error {
|
||||
name := []byte(file.Name())
|
||||
if len(name) >= MaxNameLen {
|
||||
return fmt.Errorf("sendfd: filename too long: %s", file.Name())
|
||||
}
|
||||
oob := unix.UnixRights(int(file.Fd()))
|
||||
|
||||
return unix.Sendmsg(int(socket.Fd()), name, oob, nil, 0)
|
||||
}
|
126
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
126
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
@ -1,126 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
exitSignalOffset = 128
|
||||
)
|
||||
|
||||
// GenerateRandomName returns a new name joined with a prefix. This size
|
||||
// specified is used to truncate the randomly generated value
|
||||
func GenerateRandomName(prefix string, size int) (string, error) {
|
||||
id := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if size > 64 {
|
||||
size = 64
|
||||
}
|
||||
return prefix + hex.EncodeToString(id)[:size], nil
|
||||
}
|
||||
|
||||
// ResolveRootfs ensures that the current working directory is
|
||||
// not a symlink and returns the absolute path to the rootfs
|
||||
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||
rootfs, err := filepath.Abs(uncleanRootfs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.EvalSymlinks(rootfs)
|
||||
}
|
||||
|
||||
// ExitStatus returns the correct exit status for a process based on if it
|
||||
// was signaled or exited cleanly
|
||||
func ExitStatus(status syscall.WaitStatus) int {
|
||||
if status.Signaled() {
|
||||
return exitSignalOffset + int(status.Signal())
|
||||
}
|
||||
return status.ExitStatus()
|
||||
}
|
||||
|
||||
// WriteJSON writes the provided struct v to w using standard json marshaling
|
||||
func WriteJSON(w io.Writer, v interface{}) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// CleanPath makes a path safe for use with filepath.Join. This is done by not
|
||||
// only cleaning the path, but also (if the path is relative) adding a leading
|
||||
// '/' and cleaning it (then removing the leading '/'). This ensures that a
|
||||
// path resulting from prepending another path will always resolve to lexically
|
||||
// be a subdirectory of the prefixed path. This is all done lexically, so paths
|
||||
// that include symlinks won't be safe as a result of using CleanPath.
|
||||
func CleanPath(path string) string {
|
||||
// Deal with empty strings nicely.
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Ensure that all paths are cleaned (especially problematic ones like
|
||||
// "/../../../../../" which can cause lots of issues).
|
||||
path = filepath.Clean(path)
|
||||
|
||||
// If the path isn't absolute, we need to do more processing to fix paths
|
||||
// such as "../../../../<etc>/some/path". We also shouldn't convert absolute
|
||||
// paths to relative ones.
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Clean(string(os.PathSeparator) + path)
|
||||
// This can't fail, as (by definition) all paths are relative to root.
|
||||
path, _ = filepath.Rel(string(os.PathSeparator), path)
|
||||
}
|
||||
|
||||
// Clean the path again for good measure.
|
||||
return filepath.Clean(path)
|
||||
}
|
||||
|
||||
// SearchLabels searches a list of key-value pairs for the provided key and
|
||||
// returns the corresponding value. The pairs must be separated with '='.
|
||||
func SearchLabels(labels []string, query string) string {
|
||||
for _, l := range labels {
|
||||
parts := strings.SplitN(l, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == query {
|
||||
return parts[1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Annotations returns the bundle path and user defined annotations from the
|
||||
// libcontainer state. We need to remove the bundle because that is a label
|
||||
// added by libcontainer.
|
||||
func Annotations(labels []string) (bundle string, userAnnotations map[string]string) {
|
||||
userAnnotations = make(map[string]string)
|
||||
for _, l := range labels {
|
||||
parts := strings.SplitN(l, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == "bundle" {
|
||||
bundle = parts[1]
|
||||
} else {
|
||||
userAnnotations[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetIntSize() int {
|
||||
return int(unsafe.Sizeof(1))
|
||||
}
|
43
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
43
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
@ -1,43 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func CloseExecFrom(minFd int) error {
|
||||
fdList, err := ioutil.ReadDir("/proc/self/fd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, fi := range fdList {
|
||||
fd, err := strconv.Atoi(fi.Name())
|
||||
if err != nil {
|
||||
// ignore non-numeric file names
|
||||
continue
|
||||
}
|
||||
|
||||
if fd < minFd {
|
||||
// ignore descriptors lower than our specified minimum
|
||||
continue
|
||||
}
|
||||
|
||||
// intentionally ignore errors from syscall.CloseOnExec
|
||||
syscall.CloseOnExec(fd)
|
||||
// the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSockPair returns a new unix socket pair
|
||||
func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
|
||||
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user