Merge pull request #8521 from AkihiroSuda/go-runc-v1.1.0

go.mod: github.com/containerd/go-runc v1.1.0
This commit is contained in:
Akihiro Suda 2023-05-17 20:02:36 +09:00 committed by GitHub
commit f35a105cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 425 additions and 234 deletions

2
go.mod
View File

@ -14,7 +14,7 @@ require (
github.com/containerd/continuity v0.3.1-0.20230307035957-72c70feb3081 github.com/containerd/continuity v0.3.1-0.20230307035957-72c70feb3081
github.com/containerd/fifo v1.1.0 github.com/containerd/fifo v1.1.0
github.com/containerd/go-cni v1.1.9 github.com/containerd/go-cni v1.1.9
github.com/containerd/go-runc v1.0.0 github.com/containerd/go-runc v1.1.0
github.com/containerd/imgcrypt v1.1.7 github.com/containerd/imgcrypt v1.1.7
github.com/containerd/nri v0.3.0 github.com/containerd/nri v0.3.0
github.com/containerd/ttrpc v1.2.2 github.com/containerd/ttrpc v1.2.2

3
go.sum
View File

@ -253,8 +253,9 @@ github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gGleA=
github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U=
github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=

View File

@ -9,7 +9,7 @@ require (
github.com/containerd/cgroups/v3 v3.0.1 github.com/containerd/cgroups/v3 v3.0.1
github.com/containerd/containerd v1.7.0 // see replace; the actual version of containerd is replaced with the code at the root of this repository github.com/containerd/containerd v1.7.0 // see replace; the actual version of containerd is replaced with the code at the root of this repository
github.com/containerd/continuity v0.3.1-0.20230307035957-72c70feb3081 github.com/containerd/continuity v0.3.1-0.20230307035957-72c70feb3081
github.com/containerd/go-runc v1.0.0 github.com/containerd/go-runc v1.1.0
github.com/containerd/ttrpc v1.2.2 github.com/containerd/ttrpc v1.2.2
github.com/containerd/typeurl/v2 v2.1.1 github.com/containerd/typeurl/v2 v2.1.1
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0

View File

@ -663,8 +663,9 @@ github.com/containerd/go-cni v1.1.6/go.mod h1:BWtoWl5ghVymxu6MBjg79W9NZrCRyHIdUt
github.com/containerd/go-cni v1.1.9/go.mod h1:XYrZJ1d5W6E2VOvjffL3IZq0Dz6bsVlERHbekNK90PM= github.com/containerd/go-cni v1.1.9/go.mod h1:XYrZJ1d5W6E2VOvjffL3IZq0Dz6bsVlERHbekNK90PM=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gGleA=
github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U=
github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpnMISGKSczt4k= github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpnMISGKSczt4k=
github.com/containerd/nri v0.3.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI= github.com/containerd/nri v0.3.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI=
github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=

View File

@ -21,6 +21,7 @@ package reaper
import ( import (
"errors" "errors"
"fmt" "fmt"
"runtime"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@ -100,6 +101,13 @@ func (m *Monitor) Start(c *exec.Cmd) (chan runc.Exit, error) {
return ec, nil return ec, nil
} }
// StartLocked starts the command and registers the process with the reaper
func (m *Monitor) StartLocked(c *exec.Cmd) (chan runc.Exit, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
return m.Start(c)
}
// Wait blocks until a process is signal as dead. // Wait blocks until a process is signal as dead.
// User should rely on the value of the exit status to determine if the // User should rely on the value of the exit status to determine if the
// command was successful or not. // command was successful or not.

20
vendor/github.com/containerd/go-runc/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,20 @@
linters:
enable:
- gofmt
- goimports
- ineffassign
- misspell
- revive
- staticcheck
- unconvert
- unused
- vet
disable:
- errcheck
issues:
include:
- EXC0002
run:
timeout: 2m

View File

@ -1,21 +0,0 @@
language: go
go:
- 1.13.x
- 1.14.x
- 1.15.x
install:
- go get -t ./...
- go get -u github.com/vbatts/git-validation
- go get -u github.com/kunalkushwaha/ltag
before_script:
- pushd ..; git clone https://github.com/containerd/project; popd
script:
- DCO_VERBOSITY=-q ../project/script/validate/dco
- ../project/script/validate/fileheader ../project/
- go test -v -race -covermode=atomic -coverprofile=coverage.txt ./...
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -1,7 +1,7 @@
# go-runc # go-runc
[![Build Status](https://travis-ci.org/containerd/go-runc.svg?branch=master)](https://travis-ci.org/containerd/go-runc) [![Build Status](https://github.com/containerd/go-runc/workflows/CI/badge.svg)](https://github.com/containerd/go-runc/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/containerd/go-runc/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/go-runc) [![codecov](https://codecov.io/gh/containerd/go-runc/branch/main/graph/badge.svg)](https://codecov.io/gh/containerd/go-runc)
This is a package for consuming the [runc](https://github.com/opencontainers/runc) binary in your Go applications. This is a package for consuming the [runc](https://github.com/opencontainers/runc) binary in your Go applications.
It tries to expose all the settings and features of the runc CLI. If there is something missing then add it, its opensource! It tries to expose all the settings and features of the runc CLI. If there is something missing then add it, its opensource!
@ -18,8 +18,8 @@ Docs can be found at [godoc.org](https://godoc.org/github.com/containerd/go-runc
The go-runc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). The go-runc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the: As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository. information in our [`containerd/project`](https://github.com/containerd/project) repository.

View File

@ -1,4 +1,4 @@
// +build !linux //go:build !linux
/* /*
Copyright The containerd Authors. Copyright The containerd Authors.

View File

@ -1,4 +1,4 @@
// +build !windows //go:build !windows
/* /*
Copyright The containerd Authors. Copyright The containerd Authors.
@ -20,7 +20,6 @@ package runc
import ( import (
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
@ -53,7 +52,7 @@ func NewConsoleSocket(path string) (*Socket, error) {
// On Close(), the socket is deleted // On Close(), the socket is deleted
func NewTempConsoleSocket() (*Socket, error) { func NewTempConsoleSocket() (*Socket, error) {
runtimeDir := os.Getenv("XDG_RUNTIME_DIR") runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
dir, err := ioutil.TempDir(runtimeDir, "pty") dir, err := os.MkdirTemp(runtimeDir, "pty")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -70,7 +69,7 @@ func NewTempConsoleSocket() (*Socket, error) {
return nil, err return nil, err
} }
if runtimeDir != "" { if runtimeDir != "" {
if err := os.Chmod(abs, 0755|os.ModeSticky); err != nil { if err := os.Chmod(abs, 0o755|os.ModeSticky); err != nil {
return nil, err return nil, err
} }
} }
@ -96,7 +95,7 @@ func (c *Socket) Path() string {
// locally (it is sent as non-auxiliary data in the same payload). // locally (it is sent as non-auxiliary data in the same payload).
func recvFd(socket *net.UnixConn) (*os.File, error) { func recvFd(socket *net.UnixConn) (*os.File, error) {
const MaxNameLen = 4096 const MaxNameLen = 4096
var oobSpace = unix.CmsgSpace(4) oobSpace := unix.CmsgSpace(4)
name := make([]byte, MaxNameLen) name := make([]byte, MaxNameLen)
oob := make([]byte, oobSpace) oob := make([]byte, oobSpace)

View File

@ -16,6 +16,7 @@
package runc package runc
// Event is a struct to pass runc event information
type Event struct { type Event struct {
// Type are the event type generated by runc // Type are the event type generated by runc
// If the type is "error" then check the Err field on the event for // If the type is "error" then check the Err field on the event for
@ -27,20 +28,23 @@ type Event struct {
Err error `json:"-"` Err error `json:"-"`
} }
// Stats is statistical information from the runc process
type Stats struct { type Stats struct {
Cpu Cpu `json:"cpu"` Cpu Cpu `json:"cpu"` //revive:disable
Memory Memory `json:"memory"` Memory Memory `json:"memory"`
Pids Pids `json:"pids"` Pids Pids `json:"pids"`
Blkio Blkio `json:"blkio"` Blkio Blkio `json:"blkio"`
Hugetlb map[string]Hugetlb `json:"hugetlb"` Hugetlb map[string]Hugetlb `json:"hugetlb"`
} }
// Hugetlb represents the detailed hugetlb component of the statistics data
type Hugetlb struct { type Hugetlb struct {
Usage uint64 `json:"usage,omitempty"` Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"` Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"` Failcnt uint64 `json:"failcnt"`
} }
// BlkioEntry represents a block IO entry in the IO stats
type BlkioEntry struct { type BlkioEntry struct {
Major uint64 `json:"major,omitempty"` Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"` Minor uint64 `json:"minor,omitempty"`
@ -48,6 +52,7 @@ type BlkioEntry struct {
Value uint64 `json:"value,omitempty"` Value uint64 `json:"value,omitempty"`
} }
// Blkio represents the statistical information from block IO devices
type Blkio struct { type Blkio struct {
IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"` IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"`
IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"` IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"`
@ -59,17 +64,22 @@ type Blkio struct {
SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"` SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"`
} }
// Pids represents the process ID information
type Pids struct { type Pids struct {
Current uint64 `json:"current,omitempty"` Current uint64 `json:"current,omitempty"`
Limit uint64 `json:"limit,omitempty"` Limit uint64 `json:"limit,omitempty"`
} }
// Throttling represents the throttling statistics
type Throttling struct { type Throttling struct {
Periods uint64 `json:"periods,omitempty"` Periods uint64 `json:"periods,omitempty"`
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"` ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
ThrottledTime uint64 `json:"throttledTime,omitempty"` ThrottledTime uint64 `json:"throttledTime,omitempty"`
} }
// CpuUsage represents the CPU usage statistics
//
//revive:disable-next-line
type CpuUsage struct { type CpuUsage struct {
// Units: nanoseconds. // Units: nanoseconds.
Total uint64 `json:"total,omitempty"` Total uint64 `json:"total,omitempty"`
@ -78,11 +88,15 @@ type CpuUsage struct {
User uint64 `json:"user"` User uint64 `json:"user"`
} }
// Cpu represents the CPU usage and throttling statistics
//
//revive:disable-next-line
type Cpu struct { type Cpu struct {
Usage CpuUsage `json:"usage,omitempty"` Usage CpuUsage `json:"usage,omitempty"`
Throttling Throttling `json:"throttling,omitempty"` Throttling Throttling `json:"throttling,omitempty"`
} }
// MemoryEntry represents an item in the memory use/statistics
type MemoryEntry struct { type MemoryEntry struct {
Limit uint64 `json:"limit"` Limit uint64 `json:"limit"`
Usage uint64 `json:"usage,omitempty"` Usage uint64 `json:"usage,omitempty"`
@ -90,6 +104,7 @@ type MemoryEntry struct {
Failcnt uint64 `json:"failcnt"` Failcnt uint64 `json:"failcnt"`
} }
// Memory represents the collection of memory statistics from the process
type Memory struct { type Memory struct {
Cache uint64 `json:"cache,omitempty"` Cache uint64 `json:"cache,omitempty"`
Usage MemoryEntry `json:"usage,omitempty"` Usage MemoryEntry `json:"usage,omitempty"`

View File

@ -22,6 +22,7 @@ import (
"os/exec" "os/exec"
) )
// IO is the terminal IO interface
type IO interface { type IO interface {
io.Closer io.Closer
Stdin() io.WriteCloser Stdin() io.WriteCloser
@ -30,6 +31,7 @@ type IO interface {
Set(*exec.Cmd) Set(*exec.Cmd)
} }
// StartCloser is an interface to handle IO closure after start
type StartCloser interface { type StartCloser interface {
CloseAfterStart() error CloseAfterStart() error
} }
@ -76,6 +78,12 @@ func (p *pipe) Close() error {
return err return err
} }
// NewPipeIO creates pipe pairs to be used with runc. It is not implemented
// on Windows.
func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
return newPipeIO(uid, gid, opts...)
}
type pipeIO struct { type pipeIO struct {
in *pipe in *pipe
out *pipe out *pipe
@ -144,12 +152,12 @@ func (i *pipeIO) Set(cmd *exec.Cmd) {
} }
} }
// NewSTDIO returns I/O setup for standard OS in/out/err usage
func NewSTDIO() (IO, error) { func NewSTDIO() (IO, error) {
return &stdio{}, nil return &stdio{}, nil
} }
type stdio struct { type stdio struct{}
}
func (s *stdio) Close() error { func (s *stdio) Close() error {
return nil return nil

View File

@ -1,4 +1,4 @@
// +build !windows //go:build !windows
/* /*
Copyright The containerd Authors. Copyright The containerd Authors.
@ -19,14 +19,15 @@
package runc package runc
import ( import (
"github.com/pkg/errors" "fmt"
"runtime"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"runtime"
) )
// NewPipeIO creates pipe pairs to be used with runc // newPipeIO creates pipe pairs to be used with runc
func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) { func newPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
option := defaultIOOption() option := defaultIOOption()
for _, o := range opts { for _, o := range opts {
o(option) o(option)
@ -54,7 +55,7 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
logrus.WithError(err).Debug("failed to chown stdin, ignored") logrus.WithError(err).Debug("failed to chown stdin, ignored")
} else { } else {
return nil, errors.Wrap(err, "failed to chown stdin") return nil, fmt.Errorf("failed to chown stdin: %w", err)
} }
} }
} }
@ -69,7 +70,7 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
logrus.WithError(err).Debug("failed to chown stdout, ignored") logrus.WithError(err).Debug("failed to chown stdout, ignored")
} else { } else {
return nil, errors.Wrap(err, "failed to chown stdout") return nil, fmt.Errorf("failed to chown stdout: %w", err)
} }
} }
} }
@ -84,7 +85,7 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
logrus.WithError(err).Debug("failed to chown stderr, ignored") logrus.WithError(err).Debug("failed to chown stderr, ignored")
} else { } else {
return nil, errors.Wrap(err, "failed to chown stderr") return nil, fmt.Errorf("failed to chown stderr: %w", err)
} }
} }
} }

View File

@ -1,4 +1,4 @@
// +build windows //go:build windows
/* /*
Copyright The containerd Authors. Copyright The containerd Authors.
@ -18,45 +18,8 @@
package runc package runc
// NewPipeIO creates pipe pairs to be used with runc import "errors"
func NewPipeIO(opts ...IOOpt) (i IO, err error) {
option := defaultIOOption() func newPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
for _, o := range opts { return nil, errors.New("not implemented on Windows")
o(option)
}
var (
pipes []*pipe
stdin, stdout, stderr *pipe
)
// cleanup in case of an error
defer func() {
if err != nil {
for _, p := range pipes {
p.Close()
}
}
}()
if option.OpenStdin {
if stdin, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdin)
}
if option.OpenStdout {
if stdout, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdout)
}
if option.OpenStderr {
if stderr, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stderr)
}
return &pipeIO{
in: stdin,
out: stdout,
err: stderr,
}, nil
} }

View File

@ -18,32 +18,37 @@ package runc
import ( import (
"os/exec" "os/exec"
"runtime"
"syscall" "syscall"
"time" "time"
) )
// Monitor is the default ProcessMonitor for handling runc process exit
var Monitor ProcessMonitor = &defaultMonitor{} var Monitor ProcessMonitor = &defaultMonitor{}
// Exit holds the exit information from a process
type Exit struct { type Exit struct {
Timestamp time.Time Timestamp time.Time
Pid int Pid int
Status int Status int
} }
// ProcessMonitor is an interface for process monitoring // ProcessMonitor is an interface for process monitoring.
// //
// It allows daemons using go-runc to have a SIGCHLD handler // It allows daemons using go-runc to have a SIGCHLD handler
// to handle exits without introducing races between the handler // to handle exits without introducing races between the handler
// and go's exec.Cmd // and go's exec.Cmd.
// These methods should match the methods exposed by exec.Cmd to provide //
// a consistent experience for the caller // ProcessMonitor also provides a StartLocked method which is similar to
// Start, but locks the goroutine used to start the process to an OS thread
// (for example: when Pdeathsig is set).
type ProcessMonitor interface { type ProcessMonitor interface {
Start(*exec.Cmd) (chan Exit, error) Start(*exec.Cmd) (chan Exit, error)
StartLocked(*exec.Cmd) (chan Exit, error)
Wait(*exec.Cmd, chan Exit) (int, error) Wait(*exec.Cmd, chan Exit) (int, error)
} }
type defaultMonitor struct { type defaultMonitor struct{}
}
func (m *defaultMonitor) Start(c *exec.Cmd) (chan Exit, error) { func (m *defaultMonitor) Start(c *exec.Cmd) (chan Exit, error) {
if err := c.Start(); err != nil { if err := c.Start(); err != nil {
@ -70,6 +75,43 @@ func (m *defaultMonitor) Start(c *exec.Cmd) (chan Exit, error) {
return ec, nil return ec, nil
} }
// StartLocked is like Start, but locks the goroutine used to start the process to
// the OS thread for use-cases where the parent thread matters to the child process
// (for example: when Pdeathsig is set).
func (m *defaultMonitor) StartLocked(c *exec.Cmd) (chan Exit, error) {
started := make(chan error)
ec := make(chan Exit, 1)
go func() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := c.Start(); err != nil {
started <- err
return
}
close(started)
var status int
if err := c.Wait(); err != nil {
status = 255
if exitErr, ok := err.(*exec.ExitError); ok {
if ws, ok := exitErr.Sys().(syscall.WaitStatus); ok {
status = ws.ExitStatus()
}
}
}
ec <- Exit{
Timestamp: time.Now(),
Pid: c.Process.Pid,
Status: status,
}
close(ec)
}()
if err := <-started; err != nil {
return nil, err
}
return ec, nil
}
func (m *defaultMonitor) Wait(c *exec.Cmd, ec chan Exit) (int, error) { func (m *defaultMonitor) Wait(c *exec.Cmd, ec chan Exit) (int, error) {
e := <-ec e := <-ec
return e.Status, nil return e.Status, nil

View File

@ -23,21 +23,22 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"syscall"
"time" "time"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-spec/specs-go/features"
) )
// Format is the type of log formatting options avaliable // Format is the type of log formatting options available
type Format string type Format string
// TopBody represents the structured data of the full ps output // TopResults represents the structured data of the full ps output
type TopResults struct { type TopResults struct {
// Processes running in the container, where each is process is an array of values corresponding to the headers // Processes running in the container, where each is process is an array of values corresponding to the headers
Processes [][]string `json:"Processes"` Processes [][]string `json:"Processes"`
@ -48,15 +49,53 @@ type TopResults struct {
const ( const (
none Format = "" none Format = ""
// JSON represents the JSON format
JSON Format = "json" JSON Format = "json"
// Text represents plain text format
Text Format = "text" Text Format = "text"
// DefaultCommand is the default command for Runc
DefaultCommand = "runc"
) )
// DefaultCommand is the default command for Runc
var DefaultCommand = "runc"
// Runc is the client to the runc cli
type Runc struct {
// Command overrides the name of the runc binary. If empty, DefaultCommand
// is used.
Command string
Root string
Debug bool
Log string
LogFormat Format
// PdeathSignal sets a signal the child process will receive when the
// parent dies.
//
// When Pdeathsig is set, command invocations will call runtime.LockOSThread
// to prevent OS thread termination from spuriously triggering the
// signal. See https://github.com/golang/go/issues/27505 and
// https://github.com/golang/go/blob/126c22a09824a7b52c019ed9a1d198b4e7781676/src/syscall/exec_linux.go#L48-L51
//
// A program with GOMAXPROCS=1 might hang because of the use of
// runtime.LockOSThread. Callers should ensure they retain at least one
// unlocked thread.
PdeathSignal syscall.Signal // using syscall.Signal to allow compilation on non-unix (unix.Syscall is an alias for syscall.Signal)
Setpgid bool
// Criu sets the path to the criu binary used for checkpoint and restore.
//
// Deprecated: runc option --criu is now ignored (with a warning), and the
// option will be removed entirely in a future release. Users who need a non-
// standard criu binary should rely on the standard way of looking up binaries
// in $PATH.
Criu string
SystemdCgroup bool
Rootless *bool // nil stands for "auto"
ExtraArgs []string
}
// List returns all containers created inside the provided runc root directory // List returns all containers created inside the provided runc root directory
func (r *Runc) List(context context.Context) ([]*Container, error) { func (r *Runc) List(context context.Context) ([]*Container, error) {
data, err := cmdOutput(r.command(context, "list", "--format=json"), false, nil) data, err := r.cmdOutput(r.command(context, "list", "--format=json"), false, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return nil, err return nil, err
@ -70,7 +109,7 @@ func (r *Runc) List(context context.Context) ([]*Container, error) {
// State returns the state for the container provided by id // State returns the state for the container provided by id
func (r *Runc) State(context context.Context, id string) (*Container, error) { func (r *Runc) State(context context.Context, id string) (*Container, error) {
data, err := cmdOutput(r.command(context, "state", id), true, nil) data, err := r.cmdOutput(r.command(context, "state", id), true, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s: %s", err, data.String()) return nil, fmt.Errorf("%s: %s", err, data.String())
@ -82,10 +121,12 @@ func (r *Runc) State(context context.Context, id string) (*Container, error) {
return &c, nil return &c, nil
} }
// ConsoleSocket handles the path of the socket for console access
type ConsoleSocket interface { type ConsoleSocket interface {
Path() string Path() string
} }
// CreateOpts holds all the options information for calling runc with supported options
type CreateOpts struct { type CreateOpts struct {
IO IO
// PidFile is a path to where a pid file should be created // PidFile is a path to where a pid file should be created
@ -96,6 +137,7 @@ type CreateOpts struct {
NoNewKeyring bool NoNewKeyring bool
ExtraFiles []*os.File ExtraFiles []*os.File
Started chan<- int Started chan<- int
ExtraArgs []string
} }
func (o *CreateOpts) args() (out []string, err error) { func (o *CreateOpts) args() (out []string, err error) {
@ -121,38 +163,50 @@ func (o *CreateOpts) args() (out []string, err error) {
if o.ExtraFiles != nil { if o.ExtraFiles != nil {
out = append(out, "--preserve-fds", strconv.Itoa(len(o.ExtraFiles))) out = append(out, "--preserve-fds", strconv.Itoa(len(o.ExtraFiles)))
} }
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out, nil return out, nil
} }
func (r *Runc) startCommand(cmd *exec.Cmd) (chan Exit, error) {
if r.PdeathSignal != 0 {
return Monitor.StartLocked(cmd)
}
return Monitor.Start(cmd)
}
// Create creates a new container and returns its pid if it was created successfully // Create creates a new container and returns its pid if it was created successfully
func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOpts) error { func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOpts) error {
args := []string{"create", "--bundle", bundle} args := []string{"create", "--bundle", bundle}
if opts != nil { if opts == nil {
oargs, err := opts.args() opts = &CreateOpts{}
if err != nil {
return err
}
args = append(args, oargs...)
} }
oargs, err := opts.args()
if err != nil {
return err
}
args = append(args, oargs...)
cmd := r.command(context, append(args, id)...) cmd := r.command(context, append(args, id)...)
if opts != nil && opts.IO != nil { if opts.IO != nil {
opts.Set(cmd) opts.Set(cmd)
} }
cmd.ExtraFiles = opts.ExtraFiles cmd.ExtraFiles = opts.ExtraFiles
if cmd.Stdout == nil && cmd.Stderr == nil { if cmd.Stdout == nil && cmd.Stderr == nil {
data, err := cmdOutput(cmd, true, nil) data, err := r.cmdOutput(cmd, true, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return fmt.Errorf("%s: %s", err, data.String()) return fmt.Errorf("%s: %s", err, data.String())
} }
return nil return nil
} }
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return err return err
} }
if opts != nil && opts.IO != nil { if opts.IO != nil {
if c, ok := opts.IO.(StartCloser); ok { if c, ok := opts.IO.(StartCloser); ok {
if err := c.CloseAfterStart(); err != nil { if err := c.CloseAfterStart(); err != nil {
return err return err
@ -171,12 +225,14 @@ func (r *Runc) Start(context context.Context, id string) error {
return r.runOrError(r.command(context, "start", id)) return r.runOrError(r.command(context, "start", id))
} }
// ExecOpts holds optional settings when starting an exec process with runc
type ExecOpts struct { type ExecOpts struct {
IO IO
PidFile string PidFile string
ConsoleSocket ConsoleSocket ConsoleSocket ConsoleSocket
Detach bool Detach bool
Started chan<- int Started chan<- int
ExtraArgs []string
} }
func (o *ExecOpts) args() (out []string, err error) { func (o *ExecOpts) args() (out []string, err error) {
@ -193,16 +249,22 @@ func (o *ExecOpts) args() (out []string, err error) {
} }
out = append(out, "--pid-file", abs) out = append(out, "--pid-file", abs)
} }
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out, nil return out, nil
} }
// Exec executes an additional process inside the container based on a full // Exec executes an additional process inside the container based on a full
// OCI Process specification // OCI Process specification
func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts *ExecOpts) error { func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts *ExecOpts) error {
if opts == nil {
opts = &ExecOpts{}
}
if opts.Started != nil { if opts.Started != nil {
defer close(opts.Started) defer close(opts.Started)
} }
f, err := ioutil.TempFile(os.Getenv("XDG_RUNTIME_DIR"), "runc-process") f, err := os.CreateTemp(os.Getenv("XDG_RUNTIME_DIR"), "runc-process")
if err != nil { if err != nil {
return err return err
} }
@ -213,33 +275,31 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts
return err return err
} }
args := []string{"exec", "--process", f.Name()} args := []string{"exec", "--process", f.Name()}
if opts != nil { oargs, err := opts.args()
oargs, err := opts.args() if err != nil {
if err != nil { return err
return err
}
args = append(args, oargs...)
} }
args = append(args, oargs...)
cmd := r.command(context, append(args, id)...) cmd := r.command(context, append(args, id)...)
if opts != nil && opts.IO != nil { if opts.IO != nil {
opts.Set(cmd) opts.Set(cmd)
} }
if cmd.Stdout == nil && cmd.Stderr == nil { if cmd.Stdout == nil && cmd.Stderr == nil {
data, err := cmdOutput(cmd, true, opts.Started) data, err := r.cmdOutput(cmd, true, opts.Started)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return fmt.Errorf("%w: %s", err, data.String()) return fmt.Errorf("%w: %s", err, data.String())
} }
return nil return nil
} }
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return err return err
} }
if opts.Started != nil { if opts.Started != nil {
opts.Started <- cmd.Process.Pid opts.Started <- cmd.Process.Pid
} }
if opts != nil && opts.IO != nil { if opts.IO != nil {
if c, ok := opts.IO.(StartCloser); ok { if c, ok := opts.IO.(StartCloser); ok {
if err := c.CloseAfterStart(); err != nil { if err := c.CloseAfterStart(); err != nil {
return err return err
@ -256,22 +316,24 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts
// Run runs the create, start, delete lifecycle of the container // Run runs the create, start, delete lifecycle of the container
// and returns its exit status after it has exited // and returns its exit status after it has exited
func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) { func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) {
if opts == nil {
opts = &CreateOpts{}
}
if opts.Started != nil { if opts.Started != nil {
defer close(opts.Started) defer close(opts.Started)
} }
args := []string{"run", "--bundle", bundle} args := []string{"run", "--bundle", bundle}
if opts != nil { oargs, err := opts.args()
oargs, err := opts.args() if err != nil {
if err != nil { return -1, err
return -1, err
}
args = append(args, oargs...)
} }
args = append(args, oargs...)
cmd := r.command(context, append(args, id)...) cmd := r.command(context, append(args, id)...)
if opts != nil && opts.IO != nil { if opts.IO != nil {
opts.Set(cmd) opts.Set(cmd)
} }
ec, err := Monitor.Start(cmd) cmd.ExtraFiles = opts.ExtraFiles
ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -285,14 +347,19 @@ func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts)
return status, err return status, err
} }
// DeleteOpts holds the deletion options for calling `runc delete`
type DeleteOpts struct { type DeleteOpts struct {
Force bool Force bool
ExtraArgs []string
} }
func (o *DeleteOpts) args() (out []string) { func (o *DeleteOpts) args() (out []string) {
if o.Force { if o.Force {
out = append(out, "--force") out = append(out, "--force")
} }
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out return out
} }
@ -307,13 +374,17 @@ func (r *Runc) Delete(context context.Context, id string, opts *DeleteOpts) erro
// KillOpts specifies options for killing a container and its processes // KillOpts specifies options for killing a container and its processes
type KillOpts struct { type KillOpts struct {
All bool All bool
ExtraArgs []string
} }
func (o *KillOpts) args() (out []string) { func (o *KillOpts) args() (out []string) {
if o.All { if o.All {
out = append(out, "--all") out = append(out, "--all")
} }
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out return out
} }
@ -335,7 +406,7 @@ func (r *Runc) Stats(context context.Context, id string) (*Stats, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -357,7 +428,7 @@ func (r *Runc) Events(context context.Context, id string, interval time.Duration
if err != nil { if err != nil {
return nil, err return nil, err
} }
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
rd.Close() rd.Close()
return nil, err return nil, err
@ -401,7 +472,7 @@ func (r *Runc) Resume(context context.Context, id string) error {
// Ps lists all the processes inside the container returning their pids // Ps lists all the processes inside the container returning their pids
func (r *Runc) Ps(context context.Context, id string) ([]int, error) { func (r *Runc) Ps(context context.Context, id string) ([]int, error) {
data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true, nil) data, err := r.cmdOutput(r.command(context, "ps", "--format", "json", id), true, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s: %s", err, data.String()) return nil, fmt.Errorf("%s: %s", err, data.String())
@ -415,7 +486,7 @@ func (r *Runc) Ps(context context.Context, id string) ([]int, error) {
// Top lists all the processes inside the container returning the full ps data // Top lists all the processes inside the container returning the full ps data
func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopResults, error) { func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopResults, error) {
data, err := cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true, nil) data, err := r.cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s: %s", err, data.String()) return nil, fmt.Errorf("%s: %s", err, data.String())
@ -428,6 +499,7 @@ func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopRe
return topResults, nil return topResults, nil
} }
// CheckpointOpts holds the options for performing a criu checkpoint using runc
type CheckpointOpts struct { type CheckpointOpts struct {
// ImagePath is the path for saving the criu image file // ImagePath is the path for saving the criu image file
ImagePath string ImagePath string
@ -454,13 +526,18 @@ type CheckpointOpts struct {
LazyPages bool LazyPages bool
// StatusFile is the file criu writes \0 to once lazy-pages is ready // StatusFile is the file criu writes \0 to once lazy-pages is ready
StatusFile *os.File StatusFile *os.File
ExtraArgs []string
} }
// CgroupMode defines the cgroup mode used for checkpointing
type CgroupMode string type CgroupMode string
const ( const (
Soft CgroupMode = "soft" // Soft is the "soft" cgroup mode
Full CgroupMode = "full" Soft CgroupMode = "soft"
// Full is the "full" cgroup mode
Full CgroupMode = "full"
// Strict is the "strict" cgroup mode
Strict CgroupMode = "strict" Strict CgroupMode = "strict"
) )
@ -498,9 +575,13 @@ func (o *CheckpointOpts) args() (out []string) {
if o.LazyPages { if o.LazyPages {
out = append(out, "--lazy-pages") out = append(out, "--lazy-pages")
} }
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out return out
} }
// CheckpointAction represents specific actions executed during checkpoint/restore
type CheckpointAction func([]string) []string type CheckpointAction func([]string) []string
// LeaveRunning keeps the container running after the checkpoint has been completed // LeaveRunning keeps the container running after the checkpoint has been completed
@ -535,6 +616,7 @@ func (r *Runc) Checkpoint(context context.Context, id string, opts *CheckpointOp
return r.runOrError(cmd) return r.runOrError(cmd)
} }
// RestoreOpts holds the options for performing a criu restore using runc
type RestoreOpts struct { type RestoreOpts struct {
CheckpointOpts CheckpointOpts
IO IO
@ -544,6 +626,7 @@ type RestoreOpts struct {
NoSubreaper bool NoSubreaper bool
NoPivot bool NoPivot bool
ConsoleSocket ConsoleSocket ConsoleSocket ConsoleSocket
ExtraArgs []string
} }
func (o *RestoreOpts) args() ([]string, error) { func (o *RestoreOpts) args() ([]string, error) {
@ -567,6 +650,9 @@ func (o *RestoreOpts) args() ([]string, error) {
if o.NoSubreaper { if o.NoSubreaper {
out = append(out, "-no-subreaper") out = append(out, "-no-subreaper")
} }
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out, nil return out, nil
} }
@ -585,7 +671,7 @@ func (r *Runc) Restore(context context.Context, id, bundle string, opts *Restore
if opts != nil && opts.IO != nil { if opts != nil && opts.IO != nil {
opts.Set(cmd) opts.Set(cmd)
} }
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return -1, err return -1, err
} }
@ -611,14 +697,16 @@ func (r *Runc) Update(context context.Context, id string, resources *specs.Linux
if err := json.NewEncoder(buf).Encode(resources); err != nil { if err := json.NewEncoder(buf).Encode(resources); err != nil {
return err return err
} }
args := []string{"update", "--resources", "-", id} args := []string{"update", "--resources=-", id}
cmd := r.command(context, args...) cmd := r.command(context, args...)
cmd.Stdin = buf cmd.Stdin = buf
return r.runOrError(cmd) return r.runOrError(cmd)
} }
// ErrParseRuncVersion is used when the runc version can't be parsed
var ErrParseRuncVersion = errors.New("unable to parse runc version") var ErrParseRuncVersion = errors.New("unable to parse runc version")
// Version represents the runc version information
type Version struct { type Version struct {
Runc string Runc string
Commit string Commit string
@ -627,7 +715,7 @@ type Version struct {
// Version returns the runc and runtime-spec versions // Version returns the runc and runtime-spec versions
func (r *Runc) Version(context context.Context) (Version, error) { func (r *Runc) Version(context context.Context) (Version, error) {
data, err := cmdOutput(r.command(context, "--version"), false, nil) data, err := r.cmdOutput(r.command(context, "--version"), false, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return Version{}, err return Version{}, err
@ -657,6 +745,26 @@ func parseVersion(data []byte) (Version, error) {
return v, nil return v, nil
} }
// Features shows the features implemented by the runtime.
//
// Availability:
//
// - runc: supported since runc v1.1.0
// - crun: https://github.com/containers/crun/issues/1177
// - youki: https://github.com/containers/youki/issues/815
func (r *Runc) Features(context context.Context) (*features.Features, error) {
data, err := r.cmdOutput(r.command(context, "features"), false, nil)
defer putBuf(data)
if err != nil {
return nil, err
}
var feat features.Features
if err := json.Unmarshal(data.Bytes(), &feat); err != nil {
return nil, err
}
return &feat, nil
}
func (r *Runc) args() (out []string) { func (r *Runc) args() (out []string) {
if r.Root != "" { if r.Root != "" {
out = append(out, "--root", r.Root) out = append(out, "--root", r.Root)
@ -670,9 +778,6 @@ func (r *Runc) args() (out []string) {
if r.LogFormat != none { if r.LogFormat != none {
out = append(out, "--log-format", string(r.LogFormat)) out = append(out, "--log-format", string(r.LogFormat))
} }
if r.Criu != "" {
out = append(out, "--criu", r.Criu)
}
if r.SystemdCgroup { if r.SystemdCgroup {
out = append(out, "--systemd-cgroup") out = append(out, "--systemd-cgroup")
} }
@ -680,6 +785,9 @@ func (r *Runc) args() (out []string) {
// nil stands for "auto" (differs from explicit "false") // nil stands for "auto" (differs from explicit "false")
out = append(out, "--rootless="+strconv.FormatBool(*r.Rootless)) out = append(out, "--rootless="+strconv.FormatBool(*r.Rootless))
} }
if len(r.ExtraArgs) > 0 {
out = append(out, r.ExtraArgs...)
}
return out return out
} }
@ -689,7 +797,7 @@ func (r *Runc) args() (out []string) {
// <stderr> // <stderr>
func (r *Runc) runOrError(cmd *exec.Cmd) error { func (r *Runc) runOrError(cmd *exec.Cmd) error {
if cmd.Stdout != nil || cmd.Stderr != nil { if cmd.Stdout != nil || cmd.Stderr != nil {
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return err return err
} }
@ -699,7 +807,7 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error {
} }
return err return err
} }
data, err := cmdOutput(cmd, true, nil) data, err := r.cmdOutput(cmd, true, nil)
defer putBuf(data) defer putBuf(data)
if err != nil { if err != nil {
return fmt.Errorf("%s: %s", err, data.String()) return fmt.Errorf("%s: %s", err, data.String())
@ -709,14 +817,14 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error {
// callers of cmdOutput are expected to call putBuf on the returned Buffer // callers of cmdOutput are expected to call putBuf on the returned Buffer
// to ensure it is released back to the shared pool after use. // to ensure it is released back to the shared pool after use.
func cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer, error) { func (r *Runc) cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer, error) {
b := getBuf() b := getBuf()
cmd.Stdout = b cmd.Stdout = b
if combined { if combined {
cmd.Stderr = b cmd.Stderr = b
} }
ec, err := Monitor.Start(cmd) ec, err := r.startCommand(cmd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -732,6 +840,7 @@ func cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer,
return b, err return b, err
} }
// ExitError holds the status return code when a process exits with an error code
type ExitError struct { type ExitError struct {
Status int Status int
} }

View File

@ -1,38 +0,0 @@
//+build !windows
/*
Copyright The containerd Authors.
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.
*/
package runc
import (
"golang.org/x/sys/unix"
)
// Runc is the client to the runc cli
type Runc struct {
//If command is empty, DefaultCommand is used
Command string
Root string
Debug bool
Log string
LogFormat Format
PdeathSignal unix.Signal
Setpgid bool
Criu string
SystemdCgroup bool
Rootless *bool // nil stands for "auto"
}

View File

@ -1,31 +0,0 @@
/*
Copyright The containerd Authors.
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.
*/
package runc
// Runc is the client to the runc cli
type Runc struct {
//If command is empty, DefaultCommand is used
Command string
Root string
Debug bool
Log string
LogFormat Format
Setpgid bool
Criu string
SystemdCgroup bool
Rootless *bool // nil stands for "auto"
}

View File

@ -18,34 +18,22 @@ package runc
import ( import (
"bytes" "bytes"
"io/ioutil" "os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
) )
// ReadPidFile reads the pid file at the provided path and returns // ReadPidFile reads the pid file at the provided path and returns
// the pid or an error if the read and conversion is unsuccessful // the pid or an error if the read and conversion is unsuccessful
func ReadPidFile(path string) (int, error) { func ReadPidFile(path string) (int, error) {
data, err := ioutil.ReadFile(path) data, err := os.ReadFile(path)
if err != nil { if err != nil {
return -1, err return -1, err
} }
return strconv.Atoi(string(data)) return strconv.Atoi(string(data))
} }
const exitSignalOffset = 128
// 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()
}
var bytesBufferPool = sync.Pool{ var bytesBufferPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return bytes.NewBuffer(nil) return bytes.NewBuffer(nil)

View File

@ -0,0 +1,125 @@
// Package features provides the Features struct.
package features
// Features represents the supported features of the runtime.
type Features struct {
// OCIVersionMin is the minimum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.0".
OCIVersionMin string `json:"ociVersionMin,omitempty"`
// OCIVersionMax is the maximum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.2-dev".
OCIVersionMax string `json:"ociVersionMax,omitempty"`
// Hooks is the list of the recognized hook names, e.g., "createRuntime".
// Nil value means "unknown", not "no support for any hook".
Hooks []string `json:"hooks,omitempty"`
// MountOptions is the list of the recognized mount options, e.g., "ro".
// Nil value means "unknown", not "no support for any mount option".
// This list does not contain filesystem-specific options passed to mount(2) syscall as (const void *).
MountOptions []string `json:"mountOptions,omitempty"`
// Linux is specific to Linux.
Linux *Linux `json:"linux,omitempty"`
// Annotations contains implementation-specific annotation strings,
// such as the implementation version, and third-party extensions.
Annotations map[string]string `json:"annotations,omitempty"`
}
// Linux is specific to Linux.
type Linux struct {
// Namespaces is the list of the recognized namespaces, e.g., "mount".
// Nil value means "unknown", not "no support for any namespace".
Namespaces []string `json:"namespaces,omitempty"`
// Capabilities is the list of the recognized capabilities , e.g., "CAP_SYS_ADMIN".
// Nil value means "unknown", not "no support for any capability".
Capabilities []string `json:"capabilities,omitempty"`
Cgroup *Cgroup `json:"cgroup,omitempty"`
Seccomp *Seccomp `json:"seccomp,omitempty"`
Apparmor *Apparmor `json:"apparmor,omitempty"`
Selinux *Selinux `json:"selinux,omitempty"`
IntelRdt *IntelRdt `json:"intelRdt,omitempty"`
}
// Cgroup represents the "cgroup" field.
type Cgroup struct {
// V1 represents whether Cgroup v1 support is compiled in.
// Unrelated to whether the host uses cgroup v1 or not.
// Nil value means "unknown", not "false".
V1 *bool `json:"v1,omitempty"`
// V2 represents whether Cgroup v2 support is compiled in.
// Unrelated to whether the host uses cgroup v2 or not.
// Nil value means "unknown", not "false".
V2 *bool `json:"v2,omitempty"`
// Systemd represents whether systemd-cgroup support is compiled in.
// Unrelated to whether the host uses systemd or not.
// Nil value means "unknown", not "false".
Systemd *bool `json:"systemd,omitempty"`
// SystemdUser represents whether user-scoped systemd-cgroup support is compiled in.
// Unrelated to whether the host uses systemd or not.
// Nil value means "unknown", not "false".
SystemdUser *bool `json:"systemdUser,omitempty"`
// Rdma represents whether RDMA cgroup support is compiled in.
// Unrelated to whether the host supports RDMA or not.
// Nil value means "unknown", not "false".
Rdma *bool `json:"rdma,omitempty"`
}
// Seccomp represents the "seccomp" field.
type Seccomp struct {
// Enabled is true if seccomp support is compiled in.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
// Actions is the list of the recognized actions, e.g., "SCMP_ACT_NOTIFY".
// Nil value means "unknown", not "no support for any action".
Actions []string `json:"actions,omitempty"`
// Operators is the list of the recognized operators, e.g., "SCMP_CMP_NE".
// Nil value means "unknown", not "no support for any operator".
Operators []string `json:"operators,omitempty"`
// Archs is the list of the recognized archs, e.g., "SCMP_ARCH_X86_64".
// Nil value means "unknown", not "no support for any arch".
Archs []string `json:"archs,omitempty"`
// KnownFlags is the list of the recognized filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
// Nil value means "unknown", not "no flags are recognized".
KnownFlags []string `json:"knownFlags,omitempty"`
// SupportedFlags is the list of the supported filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
// This list may be a subset of KnownFlags due to some flags
// not supported by the current kernel and/or libseccomp.
// Nil value means "unknown", not "no flags are supported".
SupportedFlags []string `json:"supportedFlags,omitempty"`
}
// Apparmor represents the "apparmor" field.
type Apparmor struct {
// Enabled is true if AppArmor support is compiled in.
// Unrelated to whether the host supports AppArmor or not.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
}
// Selinux represents the "selinux" field.
type Selinux struct {
// Enabled is true if SELinux support is compiled in.
// Unrelated to whether the host supports SELinux or not.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
}
// IntelRdt represents the "intelRdt" field.
type IntelRdt struct {
// Enabled is true if Intel RDT support is compiled in.
// Unrelated to whether the host supports Intel RDT or not.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
}

5
vendor/modules.txt vendored
View File

@ -116,8 +116,8 @@ github.com/containerd/fifo
# github.com/containerd/go-cni v1.1.9 # github.com/containerd/go-cni v1.1.9
## explicit; go 1.19 ## explicit; go 1.19
github.com/containerd/go-cni github.com/containerd/go-cni
# github.com/containerd/go-runc v1.0.0 # github.com/containerd/go-runc v1.1.0
## explicit; go 1.13 ## explicit; go 1.18
github.com/containerd/go-runc github.com/containerd/go-runc
# github.com/containerd/imgcrypt v1.1.7 # github.com/containerd/imgcrypt v1.1.7
## explicit; go 1.16 ## explicit; go 1.16
@ -346,6 +346,7 @@ github.com/opencontainers/runc/libcontainer/user
# github.com/opencontainers/runtime-spec v1.1.0-rc.2 # github.com/opencontainers/runtime-spec v1.1.0-rc.2
## explicit ## explicit
github.com/opencontainers/runtime-spec/specs-go github.com/opencontainers/runtime-spec/specs-go
github.com/opencontainers/runtime-spec/specs-go/features
# github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 # github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626
## explicit; go 1.16 ## explicit; go 1.16
github.com/opencontainers/runtime-tools/generate github.com/opencontainers/runtime-tools/generate