Getting FreeBSD Compiling
This commit is contained in:
parent
2c1089e6ec
commit
ca2e851d87
13
Makefile
13
Makefile
|
@ -5,7 +5,18 @@ PROJECT=github.com/containerd/containerd
|
|||
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null || true)
|
||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null)
|
||||
|
||||
LDFLAGS := -X github.com/containerd/containerd.GitCommit=${GIT_COMMIT} ${LDFLAGS}
|
||||
# --------------------------------------------------------------------
|
||||
#
|
||||
# epoll library flags for epoll_freebsd.go
|
||||
# @(kris-nova)
|
||||
#
|
||||
#
|
||||
CFLAGS=-I /usr/local/include/libepoll-shim ${CFLAGS}
|
||||
LDFLAGS := -I ld -X github.com/containerd/containerd.GitCommit=${GIT_COMMIT} ${LDFLAGS}
|
||||
#
|
||||
#
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
TEST_TIMEOUT ?= 5m
|
||||
TEST_SUITE_TIMEOUT ?= 10m
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package server
|
||||
|
||||
// +build freebsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/containerd/api/grpc/types"
|
||||
"github.com/containerd/containerd/specs"
|
||||
"github.com/containerd/containerd/supervisor"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var clockTicksPerSecond uint64
|
||||
|
||||
func (s *apiServer) AddProcess(ctx context.Context, r *types.AddProcessRequest) (*types.AddProcessResponse, error) {
|
||||
process := &specs.ProcessSpec{
|
||||
Terminal: r.Terminal,
|
||||
Args: r.Args,
|
||||
Env: r.Env,
|
||||
Cwd: r.Cwd,
|
||||
}
|
||||
if r.Id == "" {
|
||||
return nil, fmt.Errorf("container id cannot be empty")
|
||||
}
|
||||
if r.Pid == "" {
|
||||
return nil, fmt.Errorf("process id cannot be empty")
|
||||
}
|
||||
e := &supervisor.AddProcessTask{}
|
||||
e.ID = r.Id
|
||||
e.PID = r.Pid
|
||||
e.ProcessSpec = process
|
||||
e.Stdin = r.Stdin
|
||||
e.Stdout = r.Stdout
|
||||
e.Stderr = r.Stderr
|
||||
e.StartResponse = make(chan supervisor.StartResponse, 1)
|
||||
s.sv.SendTask(e)
|
||||
if err := <-e.ErrorCh(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
<-e.StartResponse
|
||||
return &types.AddProcessResponse{}, nil
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
// +build freebsd
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
//
|
||||
// (@kris-nova)
|
||||
//
|
||||
// Probably most of my god awful hacking is done in this file, most of this is in place to
|
||||
// hack around the Go standard library since we are using a Linux package in FreeBSD.. we
|
||||
// need to have certain constants defined that usually are only found based on $GOOS,.
|
||||
//
|
||||
// For more information or just to yell at me shoot me a line at kris@nivenly.com
|
||||
//
|
||||
|
||||
package archutils
|
||||
|
||||
// #cgo CFLAGS: -I/usr/local/include/libepoll-shim
|
||||
// #cgo LDFLAGS: -L/usr/local/lib -Ild -lepoll-shim -lrt
|
||||
// #include <sys/epoll.h>
|
||||
/*
|
||||
int EpollCreate1(int flag) {
|
||||
return epoll_create1(flag);
|
||||
}
|
||||
|
||||
int EpollCtl(int efd, int op,int sfd, int events, int fd) {
|
||||
struct epoll_event event;
|
||||
event.events = events;
|
||||
event.data.fd = fd;
|
||||
|
||||
return epoll_ctl(efd, op, sfd, &event);
|
||||
}
|
||||
|
||||
struct event_t {
|
||||
uint32_t events;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct epoll_event events[128];
|
||||
int run_epoll_wait(int fd, struct event_t *event) {
|
||||
int n, i;
|
||||
n = epoll_wait(fd, events, 128, -1);
|
||||
for (i = 0; i < n; i++) {
|
||||
event[i].events = events[i].events;
|
||||
event[i].fd = events[i].data.fd;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// EpollCreate1 calls a C implementation
|
||||
func EpollCreate1(flag int) (int, error) {
|
||||
fd := int(C.EpollCreate1(C.int(flag)))
|
||||
if fd < 0 {
|
||||
return fd, fmt.Errorf("failed to create epoll, errno is %d", fd)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
type FreeBSDEpollEventInterface interface {
|
||||
}
|
||||
|
||||
type FreeBSDEpollEvent struct {
|
||||
Events uint32
|
||||
Fd int32
|
||||
Pad int32
|
||||
}
|
||||
|
||||
const (
|
||||
// -----------------------------------------------------
|
||||
// Hacking in control constants for FreeBSD Epoll port
|
||||
// Note: these are not defined in the Go standard library
|
||||
// so we define them here manually. Once the constants make it
|
||||
// to the epoll.h file, the declaration in Go shouldn't matter.
|
||||
//
|
||||
// (@kris-nova)
|
||||
//
|
||||
FREEBSD_EPOLL_CTL_ADD = 0x1
|
||||
FREEBSD_EPOLL_CLOEXEC = 0x80000
|
||||
FREEBSD_EPOLL_CTL_DEL = 0x2
|
||||
FREEBSD_EPOLLHUP = 0x10
|
||||
FREEBSD_EPOLLIN = 0x1
|
||||
FREEBSD_SYS_EPOLL_CTL = 233
|
||||
)
|
||||
|
||||
// EpollCtl is a another hack to get the sys call running without having a dependency on linux
|
||||
//func EpollCtl(epfd int, op int, fd int, eventInterface FreeBSDEpollEventInterface) (err error) {
|
||||
// event := eventInterface.(*FreeBSDEpollEvent)
|
||||
// _, _, e1 := syscall.RawSyscall6(FREEBSD_SYS_EPOLL_CTL, uintptr(epfd), uintptr(op), uintptr(fd), uintptr(unsafe.Pointer(event)), 0, 0)
|
||||
// if e1 != 0 {=
|
||||
// err = e1
|
||||
// }
|
||||
// return
|
||||
//}
|
||||
|
||||
// EpollCtl calls a C implementation
|
||||
func EpollCtl(epfd int, op int, fd int, eventInterface FreeBSDEpollEventInterface) error {
|
||||
event := eventInterface.(*FreeBSDEpollEvent)
|
||||
errno := C.EpollCtl(C.int(epfd), C.int(FREEBSD_EPOLL_CTL_ADD), C.int(fd), C.int(event.Events), C.int(event.Fd))
|
||||
if errno < 0 {
|
||||
return fmt.Errorf("Failed to ctl epoll")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EpollWait calls a C implementation
|
||||
func EpollWait(epfd int, events []FreeBSDEpollEvent, msec int) (int, error) {
|
||||
//var events []FreeBSDEpollEvent
|
||||
//for _, e := range eventInterfaces {
|
||||
// events = append(events, e.(FreeBSDEpollEvent))
|
||||
//}
|
||||
var c_events [128]C.struct_event_t
|
||||
n := int(C.run_epoll_wait(C.int(epfd), (*C.struct_event_t)(unsafe.Pointer(&c_events))))
|
||||
if n < 0 {
|
||||
return int(n), fmt.Errorf("Failed to wait epoll")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
events[i].Fd = int32(c_events[i].fd)
|
||||
events[i].Events = uint32(c_events[i].events)
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
|
@ -39,11 +39,15 @@ func ioctl(fd uintptr, flag, data uintptr) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
FREEBSD_TIOCSPTLCK = 0x40045431
|
||||
)
|
||||
|
||||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
func unlockpt(f *os.File) error {
|
||||
var u int32
|
||||
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||
return ioctl(f.Fd(), FREEBSD_TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
// +build freebsd
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/containerd/osutils"
|
||||
runc "github.com/containerd/go-runc"
|
||||
"github.com/tonistiigi/fifo"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// openIO opens the pre-created fifo's for use with the container
|
||||
// in RDWR so that they remain open if the other side stops listening
|
||||
func (p *process) openIO() error {
|
||||
p.stdio = &stdio{}
|
||||
var (
|
||||
uid = p.state.RootUID
|
||||
gid = p.state.RootGID
|
||||
)
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
|
||||
stdinCloser, err := fifo.OpenFifo(ctx, p.state.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.stdinCloser = stdinCloser
|
||||
|
||||
if p.state.Terminal {
|
||||
socket, err := runc.NewTempConsoleSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.socket = socket
|
||||
consoleCh := p.waitConsole(socket)
|
||||
|
||||
stdin, err := fifo.OpenFifo(ctx, p.state.Stdin, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdoutw, err := fifo.OpenFifo(ctx, p.state.Stdout, syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stdoutr, err := fifo.OpenFifo(ctx, p.state.Stdout, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// open the fifos but wait until we receive the console before we start
|
||||
// copying data back and forth between the two
|
||||
go p.setConsole(consoleCh, stdin, stdoutw)
|
||||
|
||||
p.Add(1)
|
||||
p.ioCleanupFn = func() {
|
||||
stdoutr.Close()
|
||||
stdoutw.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
close(p.consoleErrCh)
|
||||
i, err := p.initializeIO(uid, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.shimIO = i
|
||||
// non-tty
|
||||
ioClosers := []io.Closer{}
|
||||
for _, pair := range []struct {
|
||||
name string
|
||||
dest func(wc io.WriteCloser, rc io.Closer)
|
||||
}{
|
||||
{
|
||||
p.state.Stdout,
|
||||
func(wc io.WriteCloser, rc io.Closer) {
|
||||
p.Add(1)
|
||||
go func() {
|
||||
io.Copy(wc, i.Stdout)
|
||||
p.Done()
|
||||
}()
|
||||
},
|
||||
},
|
||||
{
|
||||
p.state.Stderr,
|
||||
func(wc io.WriteCloser, rc io.Closer) {
|
||||
p.Add(1)
|
||||
go func() {
|
||||
io.Copy(wc, i.Stderr)
|
||||
p.Done()
|
||||
}()
|
||||
},
|
||||
},
|
||||
} {
|
||||
fw, err := fifo.OpenFifo(ctx, pair.name, syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("containerd-shim: opening %s failed: %s", pair.name, err)
|
||||
}
|
||||
fr, err := fifo.OpenFifo(ctx, pair.name, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("containerd-shim: opening %s failed: %s", pair.name, err)
|
||||
}
|
||||
pair.dest(fw, fr)
|
||||
ioClosers = append(ioClosers, fw, fr)
|
||||
}
|
||||
|
||||
f, err := fifo.OpenFifo(ctx, p.state.Stdin, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("containerd-shim: opening %s failed: %s", p.state.Stdin, err)
|
||||
}
|
||||
p.ioCleanupFn = func() {
|
||||
for _, c := range ioClosers {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
io.Copy(i.Stdin, f)
|
||||
i.Stdin.Close()
|
||||
f.Close()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *process) Wait() {
|
||||
p.WaitGroup.Wait()
|
||||
if p.ioCleanupFn != nil {
|
||||
p.ioCleanupFn()
|
||||
}
|
||||
if p.console != nil {
|
||||
p.console.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *process) killAll() error {
|
||||
if !p.state.Exec {
|
||||
cmd := exec.Command(p.runtime, append(p.state.RuntimeArgs, "kill", "--all", p.id, "SIGKILL")...)
|
||||
cmd.SysProcAttr = osutils.SetPDeathSig()
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", out, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *process) setConsole(c <-chan *consoleR, stdin io.Reader, stdout io.Writer) {
|
||||
r := <-c
|
||||
if r.err != nil {
|
||||
p.consoleErrCh <- r.err
|
||||
return
|
||||
}
|
||||
close(p.consoleErrCh)
|
||||
p.console = r.c
|
||||
// copy from the console into the provided fifos
|
||||
go io.Copy(r.c, stdin)
|
||||
go func() {
|
||||
io.Copy(stdout, r.c)
|
||||
p.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
type consoleR struct {
|
||||
c console.Console
|
||||
err error
|
||||
}
|
||||
|
||||
func (p *process) waitConsole(socket *runc.Socket) <-chan *consoleR {
|
||||
c := make(chan *consoleR, 1)
|
||||
go func() {
|
||||
master, err := socket.ReceiveMaster()
|
||||
socket.Close()
|
||||
if err != nil {
|
||||
c <- &consoleR{
|
||||
err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
c <- &consoleR{
|
||||
c: master,
|
||||
}
|
||||
}()
|
||||
return c
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package main
|
||||
|
||||
// +build freebsd
|
||||
|
||||
func processMetrics() {
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// +build freebsd
|
||||
|
||||
package osutils
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// SetPDeathSig sets the parent death signal to SIGKILL so that if the
|
||||
// shim dies the container process also dies.
|
||||
func SetPDeathSig() *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{
|
||||
//Pdeathsig: syscall.SIGKILL,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// +build freebsd
|
||||
|
||||
package osutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// GetSubreaper returns the subreaper setting for the calling process
|
||||
func GetSubreaper() (int, error) {
|
||||
return 0, errors.New("osutils GetSubreaper not implemented on FreeBSD")
|
||||
}
|
||||
|
||||
// SetSubreaper sets the value i as the subreaper setting for the calling process
|
||||
func SetSubreaper(i int) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
package runtime
|
||||
|
||||
// +build freebsd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/specs"
|
||||
ocs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func findCgroupMountpointAndRoot(pid int, subsystem string) (string, string, error) {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
fields := strings.Split(txt, " ")
|
||||
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||
if opt == subsystem {
|
||||
return fields[4], fields[3], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("cgroup path for %s not found", subsystem)
|
||||
}
|
||||
|
||||
func parseCgroupFile(path string) (map[string]string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
cgroups := make(map[string]string)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := s.Text()
|
||||
parts := strings.Split(text, ":")
|
||||
|
||||
for _, subs := range strings.Split(parts[1], ",") {
|
||||
cgroups[subs] = parts[2]
|
||||
}
|
||||
}
|
||||
return cgroups, nil
|
||||
}
|
||||
|
||||
func (c *container) OOM() (OOM, error) {
|
||||
p := c.processes[InitProcessID]
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("no init process found")
|
||||
}
|
||||
|
||||
mountpoint, hostRoot, err := findCgroupMountpointAndRoot(os.Getpid(), "memory")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cgroups, err := parseCgroupFile(fmt.Sprintf("/proc/%d/cgroup", p.pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root, ok := cgroups["memory"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no memory cgroup for container %s", c.ID())
|
||||
}
|
||||
|
||||
// Take care of the case were we're running inside a container
|
||||
// ourself
|
||||
root = strings.TrimPrefix(root, hostRoot)
|
||||
|
||||
return c.getMemoryEventFD(filepath.Join(mountpoint, root))
|
||||
}
|
||||
|
||||
func (c *container) Pids() ([]int, error) {
|
||||
var pids []int
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "ps", "--format=json", c.id)
|
||||
out, err := exec.Command(c.runtime, args...).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %q", err.Error(), out)
|
||||
}
|
||||
if err := json.Unmarshal(out, &pids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pids, nil
|
||||
}
|
||||
|
||||
func u64Ptr(i uint64) *uint64 { return &i }
|
||||
func i64Ptr(i int64) *int64 { return &i }
|
||||
|
||||
func (c *container) UpdateResources(r *Resource) error {
|
||||
sr := ocs.LinuxResources{
|
||||
Memory: &ocs.LinuxMemory{
|
||||
Limit: u64Ptr(uint64(r.Memory)),
|
||||
Reservation: u64Ptr(uint64(r.MemoryReservation)),
|
||||
Swap: u64Ptr(uint64(r.MemorySwap)),
|
||||
Kernel: u64Ptr(uint64(r.KernelMemory)),
|
||||
KernelTCP: u64Ptr(uint64(r.KernelTCPMemory)),
|
||||
},
|
||||
CPU: &ocs.LinuxCPU{
|
||||
Shares: u64Ptr(uint64(r.CPUShares)),
|
||||
Quota: i64Ptr(int64(r.CPUQuota)),
|
||||
Period: u64Ptr(uint64(r.CPUPeriod)),
|
||||
Cpus: r.CpusetCpus,
|
||||
Mems: r.CpusetMems,
|
||||
},
|
||||
BlockIO: &ocs.LinuxBlockIO{
|
||||
Weight: &r.BlkioWeight,
|
||||
},
|
||||
Pids: &ocs.LinuxPids{
|
||||
Limit: r.PidsLimit,
|
||||
},
|
||||
}
|
||||
|
||||
srStr := bytes.NewBuffer(nil)
|
||||
if err := json.NewEncoder(srStr).Encode(&sr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := c.runtimeArgs
|
||||
args = append(args, "update", "-r", "-", c.id)
|
||||
cmd := exec.Command(c.runtime, args...)
|
||||
cmd.Stdin = srStr
|
||||
b, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf(string(b))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRootIDs(s *specs.Spec) (int, int, error) {
|
||||
if s == nil {
|
||||
return 0, 0, nil
|
||||
}
|
||||
var hasUserns bool
|
||||
for _, ns := range s.Linux.Namespaces {
|
||||
if ns.Type == ocs.UserNamespace {
|
||||
hasUserns = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasUserns {
|
||||
return 0, 0, nil
|
||||
}
|
||||
uid := hostIDFromMap(0, s.Linux.UIDMappings)
|
||||
gid := hostIDFromMap(0, s.Linux.GIDMappings)
|
||||
return uid, gid, nil
|
||||
}
|
||||
|
||||
func (c *container) getMemoryEventFD(root string) (*oom, error) {
|
||||
f, err := os.Open(filepath.Join(root, "memory.oom_control"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
fd, _, serr := syscall.RawSyscall(syscall.SYS_KEVENT, 0, syscall.FD_CLOEXEC, 0)
|
||||
if serr != 0 {
|
||||
return nil, serr
|
||||
}
|
||||
if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil {
|
||||
syscall.Close(int(fd))
|
||||
return nil, err
|
||||
}
|
||||
return &oom{
|
||||
root: root,
|
||||
id: c.id,
|
||||
eventfd: int(fd),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// +build freebsd
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (p *process) getPidFromFile() (int, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(p.root, "pid"))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
i, err := strconv.Atoi(string(data))
|
||||
if err != nil {
|
||||
return -1, errInvalidPidInt
|
||||
}
|
||||
p.pid = i
|
||||
return i, nil
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
// +build freebsd
|
||||
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/containerd/containerd/archutils"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
)
|
||||
|
||||
// NewMonitor starts a new process monitor and returns it
|
||||
func NewMonitor() (*Monitor, error) {
|
||||
m := &Monitor{
|
||||
receivers: make(map[int]interface{}),
|
||||
exits: make(chan runtime.Process, 1024),
|
||||
ooms: make(chan string, 1024),
|
||||
}
|
||||
fd, err := archutils.EpollCreate1(archutils.FREEBSD_EPOLL_CLOEXEC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.epollFd = fd
|
||||
go m.start()
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Monitor represents a runtime.Process monitor
|
||||
type Monitor struct {
|
||||
m sync.Mutex
|
||||
receivers map[int]interface{}
|
||||
exits chan runtime.Process
|
||||
ooms chan string
|
||||
epollFd int
|
||||
}
|
||||
|
||||
// Exits returns the channel used to notify of a process exit
|
||||
func (m *Monitor) Exits() chan runtime.Process {
|
||||
return m.exits
|
||||
}
|
||||
|
||||
// OOMs returns the channel used to notify of a container exit due to OOM
|
||||
func (m *Monitor) OOMs() chan string {
|
||||
return m.ooms
|
||||
}
|
||||
|
||||
// Monitor adds a process to the list of the one being monitored
|
||||
func (m *Monitor) Monitor(p runtime.Process) error {
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
fd := p.ExitFD()
|
||||
event := archutils.FreeBSDEpollEvent{
|
||||
Fd: int32(fd),
|
||||
Events: archutils.FREEBSD_EPOLLHUP,
|
||||
}
|
||||
if err := archutils.EpollCtl(m.epollFd, archutils.FREEBSD_EPOLL_CTL_ADD, fd, &event); err != nil {
|
||||
return err
|
||||
}
|
||||
EpollFdCounter.Inc(1)
|
||||
m.receivers[fd] = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// MonitorOOM adds a container to the list of the ones monitored for OOM
|
||||
func (m *Monitor) MonitorOOM(c runtime.Container) error {
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
o, err := c.OOM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd := o.FD()
|
||||
event := archutils.FreeBSDEpollEvent{
|
||||
Fd: int32(fd),
|
||||
Events: archutils.FREEBSD_EPOLLHUP | archutils.FREEBSD_EPOLLIN,
|
||||
}
|
||||
if err := archutils.EpollCtl(m.epollFd, archutils.FREEBSD_EPOLL_CTL_ADD, fd, &event); err != nil {
|
||||
return err
|
||||
}
|
||||
EpollFdCounter.Inc(1)
|
||||
m.receivers[fd] = o
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close cleans up resources allocated by NewMonitor()
|
||||
func (m *Monitor) Close() error {
|
||||
return syscall.Close(m.epollFd)
|
||||
}
|
||||
|
||||
func (m *Monitor) processEvent(fd int, event uint32) {
|
||||
m.m.Lock()
|
||||
r := m.receivers[fd]
|
||||
switch t := r.(type) {
|
||||
case runtime.Process:
|
||||
if event == archutils.FREEBSD_EPOLLHUP {
|
||||
delete(m.receivers, fd)
|
||||
if err := archutils.EpollCtl(m.epollFd, archutils.FREEBSD_EPOLL_CTL_DEL, fd, &archutils.FreeBSDEpollEvent{
|
||||
Events: archutils.FREEBSD_EPOLLHUP,
|
||||
Fd: int32(fd),
|
||||
}); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: epoll remove fd")
|
||||
}
|
||||
if err := t.Close(); err != nil {
|
||||
logrus.WithField("error", err).Error("containerd: close process IO")
|
||||
}
|
||||
EpollFdCounter.Dec(1)
|
||||
// defer until lock is released
|
||||
defer func() {
|
||||
m.exits <- t
|
||||
}()
|
||||
}
|
||||
case runtime.OOM:
|
||||
// always flush the event fd
|
||||
t.Flush()
|
||||
if t.Removed() {
|
||||
delete(m.receivers, fd)
|
||||
// epoll will remove the fd from its set after it has been closed
|
||||
t.Close()
|
||||
EpollFdCounter.Dec(1)
|
||||
} else {
|
||||
// defer until lock is released
|
||||
defer func() {
|
||||
m.ooms <- t.ContainerID()
|
||||
}()
|
||||
}
|
||||
}
|
||||
// This cannot be a defer to avoid a deadlock in case the channels
|
||||
// above get full
|
||||
m.m.Unlock()
|
||||
}
|
||||
|
||||
func (m *Monitor) start() {
|
||||
var events [128]archutils.FreeBSDEpollEvent
|
||||
for {
|
||||
n, err := archutils.EpollWait(m.epollFd, events[:], -1)
|
||||
if err != nil {
|
||||
if err == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
logrus.WithField("error", err).Fatal("containerd: epoll wait")
|
||||
}
|
||||
// process events
|
||||
for i := 0; i < n; i++ {
|
||||
m.processEvent(int(events[i].Fd), events[i].Events)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// -------------------------------------------------------
|
||||
// FreeBSD stub for compiling sigar
|
||||
// @(kris-nova)
|
||||
//
|
||||
// Adding out stubs here to get sigar compiling without an
|
||||
// implementation, this patch should handle getting docker
|
||||
// working, and the Go compiler happy and NOTHING more.
|
||||
|
||||
// +build freebsd
|
||||
|
||||
package sigar
|
||||
|
||||
import "syscall"
|
||||
|
||||
func (self *FileSystemUsage) Get(path string) error {
|
||||
stat := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(path, &stat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bsize := stat.Bsize / 512
|
||||
|
||||
self.Total = (uint64(stat.Blocks) * uint64(bsize)) >> 1
|
||||
self.Free = (uint64(stat.Bfree) * uint64(bsize)) >> 1
|
||||
self.Avail = (uint64(stat.Bavail) * uint64(bsize)) >> 1
|
||||
self.Used = self.Total - self.Free
|
||||
self.Files = stat.Files
|
||||
self.FreeFiles = uint64(stat.Ffree)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Cpu) Get() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mem) Get() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Swap) Get() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *LoadAverage) Get() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CpuList) Get() error {
|
||||
return nil
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) 2012 VMware, Inc.
|
||||
|
||||
// +build darwin freebsd linux netbsd openbsd
|
||||
// +build darwin linux netbsd openbsd
|
||||
|
||||
package sigar
|
||||
|
||||
|
@ -23,4 +23,4 @@ func (self *FileSystemUsage) Get(path string) error {
|
|||
self.FreeFiles = stat.Ffree
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -531,7 +531,7 @@ func (r *Runc) command(context context.Context, args ...string) *exec.Cmd {
|
|||
cmd := exec.CommandContext(context, command, append(r.args(), args...)...)
|
||||
if r.PdeathSignal != 0 {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Pdeathsig: r.PdeathSignal,
|
||||
//Pdeathsig: r.PdeathSignal,
|
||||
}
|
||||
}
|
||||
return cmd
|
||||
|
|
95
vendor/src/github.com/opencontainers/runc/libcontainer/utils/cmsg_freebsd.go
vendored
Normal file
95
vendor/src/github.com/opencontainers/runc/libcontainer/utils/cmsg_freebsd.go
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// +build freebsd
|
||||
|
||||
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)
|
||||
}
|
|
@ -124,3 +124,4 @@ func Annotations(labels []string) (bundle string, userAnnotations map[string]str
|
|||
func GetIntSize() int {
|
||||
return int(unsafe.Sizeof(1))
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ func getHandle(fn string) (*handle, error) {
|
|||
h := &handle{
|
||||
fn: fn,
|
||||
dev: uint64(stat.Dev),
|
||||
ino: stat.Ino,
|
||||
ino: uint64(stat.Ino),
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
@ -34,7 +34,7 @@ func (h *handle) Path() (string, error) {
|
|||
if err := syscall.Stat(h.fn, &stat); err != nil {
|
||||
return "", errors.Wrapf(err, "path %v could not be statted", h.fn)
|
||||
}
|
||||
if uint64(stat.Dev) != h.dev || stat.Ino != h.ino {
|
||||
if uint64(stat.Dev) != h.dev || uint64(stat.Ino) != h.ino {
|
||||
return "", errors.Errorf("failed to verify handle %v/%v %v/%v", stat.Dev, h.dev, stat.Ino, h.ino)
|
||||
}
|
||||
return h.fn, nil
|
||||
|
|
Loading…
Reference in New Issue