shimv2: fix the issue bring by updating containerd vendor

Fix the mismatch bring by the upgrading of vendor of  containerd,
cgroup and runtime spec.

Fixes: #1441

Signed-off-by: fupan.lfp <fupan.lfp@antgroup.com>
This commit is contained in:
fupan.lfp 2021-06-15 19:14:21 +08:00
parent 79e632bc23
commit f607641a6e
91 changed files with 14488 additions and 799 deletions

View File

@ -265,9 +265,9 @@ func getMemoryInfo() MemoryInfo {
}
return MemoryInfo{
Total: mi.MemTotal,
Free: mi.MemFree,
Available: mi.MemAvailable,
Total: *mi.MemTotal,
Free: *mi.MemFree,
Available: *mi.MemAvailable,
}
}

View File

@ -22,9 +22,9 @@ import (
otelTrace "go.opentelemetry.io/otel/trace"
// only register the proto type
crioption "github.com/containerd/containerd/pkg/runtimeoptions/v1"
_ "github.com/containerd/containerd/runtime/linux/runctypes"
_ "github.com/containerd/containerd/runtime/v2/runc/options"
crioption "github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"

View File

@ -8,7 +8,7 @@ package containerdshim
import (
"context"
"github.com/containerd/cgroups"
cgroupsv1 "github.com/containerd/cgroups/stats/v1"
"github.com/containerd/typeurl"
google_protobuf "github.com/gogo/protobuf/types"
@ -31,11 +31,11 @@ func marshalMetrics(ctx context.Context, s *service, containerID string) (*googl
return data, nil
}
func statsToMetrics(stats *vc.ContainerStats) *cgroups.Metrics {
metrics := &cgroups.Metrics{}
func statsToMetrics(stats *vc.ContainerStats) *cgroupsv1.Metrics {
metrics := &cgroupsv1.Metrics{}
if stats.CgroupStats != nil {
metrics = &cgroups.Metrics{
metrics = &cgroupsv1.Metrics{
Hugetlb: setHugetlbStats(stats.CgroupStats.HugetlbStats),
Pids: setPidsStats(stats.CgroupStats.PidsStats),
CPU: setCPUStats(stats.CgroupStats.CPUStats),
@ -49,12 +49,12 @@ func statsToMetrics(stats *vc.ContainerStats) *cgroups.Metrics {
return metrics
}
func setHugetlbStats(vcHugetlb map[string]vc.HugetlbStats) []*cgroups.HugetlbStat {
var hugetlbStats []*cgroups.HugetlbStat
func setHugetlbStats(vcHugetlb map[string]vc.HugetlbStats) []*cgroupsv1.HugetlbStat {
var hugetlbStats []*cgroupsv1.HugetlbStat
for _, v := range vcHugetlb {
hugetlbStats = append(
hugetlbStats,
&cgroups.HugetlbStat{
&cgroupsv1.HugetlbStat{
Usage: v.Usage,
Max: v.MaxUsage,
Failcnt: v.Failcnt,
@ -64,8 +64,8 @@ func setHugetlbStats(vcHugetlb map[string]vc.HugetlbStats) []*cgroups.HugetlbSta
return hugetlbStats
}
func setPidsStats(vcPids vc.PidsStats) *cgroups.PidsStat {
pidsStats := &cgroups.PidsStat{
func setPidsStats(vcPids vc.PidsStats) *cgroupsv1.PidsStat {
pidsStats := &cgroupsv1.PidsStat{
Current: vcPids.Current,
Limit: vcPids.Limit,
}
@ -73,19 +73,19 @@ func setPidsStats(vcPids vc.PidsStats) *cgroups.PidsStat {
return pidsStats
}
func setCPUStats(vcCPU vc.CPUStats) *cgroups.CPUStat {
func setCPUStats(vcCPU vc.CPUStats) *cgroupsv1.CPUStat {
var perCPU []uint64
perCPU = append(perCPU, vcCPU.CPUUsage.PercpuUsage...)
cpuStats := &cgroups.CPUStat{
Usage: &cgroups.CPUUsage{
cpuStats := &cgroupsv1.CPUStat{
Usage: &cgroupsv1.CPUUsage{
Total: vcCPU.CPUUsage.TotalUsage,
Kernel: vcCPU.CPUUsage.UsageInKernelmode,
User: vcCPU.CPUUsage.UsageInUsermode,
PerCPU: perCPU,
},
Throttling: &cgroups.Throttle{
Throttling: &cgroupsv1.Throttle{
Periods: vcCPU.ThrottlingData.Periods,
ThrottledPeriods: vcCPU.ThrottlingData.ThrottledPeriods,
ThrottledTime: vcCPU.ThrottlingData.ThrottledTime,
@ -95,27 +95,27 @@ func setCPUStats(vcCPU vc.CPUStats) *cgroups.CPUStat {
return cpuStats
}
func setMemoryStats(vcMemory vc.MemoryStats) *cgroups.MemoryStat {
memoryStats := &cgroups.MemoryStat{
Usage: &cgroups.MemoryEntry{
func setMemoryStats(vcMemory vc.MemoryStats) *cgroupsv1.MemoryStat {
memoryStats := &cgroupsv1.MemoryStat{
Usage: &cgroupsv1.MemoryEntry{
Limit: vcMemory.Usage.Limit,
Usage: vcMemory.Usage.Usage,
Max: vcMemory.Usage.MaxUsage,
Failcnt: vcMemory.Usage.Failcnt,
},
Swap: &cgroups.MemoryEntry{
Swap: &cgroupsv1.MemoryEntry{
Limit: vcMemory.SwapUsage.Limit,
Usage: vcMemory.SwapUsage.Usage,
Max: vcMemory.SwapUsage.MaxUsage,
Failcnt: vcMemory.SwapUsage.Failcnt,
},
Kernel: &cgroups.MemoryEntry{
Kernel: &cgroupsv1.MemoryEntry{
Limit: vcMemory.KernelUsage.Limit,
Usage: vcMemory.KernelUsage.Usage,
Max: vcMemory.KernelUsage.MaxUsage,
Failcnt: vcMemory.KernelUsage.Failcnt,
},
KernelTCP: &cgroups.MemoryEntry{
KernelTCP: &cgroupsv1.MemoryEntry{
Limit: vcMemory.KernelTCPUsage.Limit,
Usage: vcMemory.KernelTCPUsage.Usage,
Max: vcMemory.KernelTCPUsage.MaxUsage,
@ -145,8 +145,8 @@ func setMemoryStats(vcMemory vc.MemoryStats) *cgroups.MemoryStat {
return memoryStats
}
func setBlkioStats(vcBlkio vc.BlkioStats) *cgroups.BlkIOStat {
blkioStats := &cgroups.BlkIOStat{
func setBlkioStats(vcBlkio vc.BlkioStats) *cgroupsv1.BlkIOStat {
blkioStats := &cgroupsv1.BlkIOStat{
IoServiceBytesRecursive: copyBlkio(vcBlkio.IoServiceBytesRecursive),
IoServicedRecursive: copyBlkio(vcBlkio.IoServicedRecursive),
IoQueuedRecursive: copyBlkio(vcBlkio.IoQueuedRecursive),
@ -160,10 +160,10 @@ func setBlkioStats(vcBlkio vc.BlkioStats) *cgroups.BlkIOStat {
return blkioStats
}
func copyBlkio(s []vc.BlkioStatEntry) []*cgroups.BlkIOEntry {
ret := make([]*cgroups.BlkIOEntry, len(s))
func copyBlkio(s []vc.BlkioStatEntry) []*cgroupsv1.BlkIOEntry {
ret := make([]*cgroupsv1.BlkIOEntry, len(s))
for i, v := range s {
ret[i] = &cgroups.BlkIOEntry{
ret[i] = &cgroupsv1.BlkIOEntry{
Op: v.Op,
Major: v.Major,
Minor: v.Minor,
@ -174,10 +174,10 @@ func copyBlkio(s []vc.BlkioStatEntry) []*cgroups.BlkIOEntry {
return ret
}
func setNetworkStats(vcNetwork []*vc.NetworkStats) []*cgroups.NetworkStat {
networkStats := make([]*cgroups.NetworkStat, len(vcNetwork))
func setNetworkStats(vcNetwork []*vc.NetworkStats) []*cgroupsv1.NetworkStat {
networkStats := make([]*cgroupsv1.NetworkStat, len(vcNetwork))
for i, v := range vcNetwork {
networkStats[i] = &cgroups.NetworkStat{
networkStats[i] = &cgroupsv1.NetworkStat{
Name: v.Name,
RxBytes: v.RxBytes,
RxPackets: v.RxPackets,

View File

@ -10,7 +10,7 @@ import (
"context"
"testing"
"github.com/containerd/cgroups"
"github.com/containerd/cgroups/stats/v1"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/stretchr/testify/assert"
@ -29,7 +29,7 @@ func TestStatNetworkMetric(t *testing.T) {
},
}
expectedNetwork := []*cgroups.NetworkStat{
expectedNetwork := []*v1.NetworkStat{
{
Name: "test-network",
RxBytes: 10,

View File

@ -68,7 +68,7 @@ var shimLog = logrus.WithFields(logrus.Fields{
})
// New returns a new shim service that can be used via GRPC
func New(ctx context.Context, id string, publisher events.Publisher) (cdshim.Shim, error) {
func New(ctx context.Context, id string, publisher cdshim.Publisher, shutdown func()) (cdshim.Shim, error) {
shimLog = shimLog.WithFields(logrus.Fields{
"sandbox": id,
"pid": os.Getpid(),
@ -84,8 +84,6 @@ func New(ctx context.Context, id string, publisher events.Publisher) (cdshim.Shi
vci.SetLogger(ctx, shimLog)
katautils.SetLogger(ctx, shimLog, shimLog.Logger.Level)
ctx, cancel := context.WithCancel(ctx)
s := &service{
id: id,
pid: uint32(os.Getpid()),
@ -93,7 +91,7 @@ func New(ctx context.Context, id string, publisher events.Publisher) (cdshim.Shi
containers: make(map[string]*container),
events: make(chan interface{}, chSize),
ec: make(chan exit, bufferSize),
cancel: cancel,
cancel: shutdown,
}
go s.processExits()
@ -138,7 +136,7 @@ type service struct {
id string
}
func newCommand(ctx context.Context, containerdBinary, id, containerdAddress string) (*sysexec.Cmd, error) {
func newCommand(ctx context.Context, id, containerdBinary, containerdAddress string) (*sysexec.Cmd, error) {
ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
@ -176,13 +174,13 @@ func newCommand(ctx context.Context, containerdBinary, id, containerdAddress str
// StartShim willl start a kata shimv2 daemon which will implemented the
// ShimV2 APIs such as create/start/update etc containers.
func (s *service) StartShim(ctx context.Context, id, containerdBinary, containerdAddress string) (string, error) {
func (s *service) StartShim(ctx context.Context, opts cdshim.StartOpts) (_ string, retErr error) {
bundlePath, err := os.Getwd()
if err != nil {
return "", err
}
address, err := getAddress(ctx, bundlePath, id)
address, err := getAddress(ctx, bundlePath, opts.Address, opts.ID)
if err != nil {
return "", err
}
@ -193,26 +191,41 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
return address, nil
}
cmd, err := newCommand(ctx, containerdBinary, id, containerdAddress)
cmd, err := newCommand(ctx, opts.ID, opts.ContainerdBinary, opts.Address)
if err != nil {
return "", err
}
address, err = cdshim.SocketAddress(ctx, id)
address, err = cdshim.SocketAddress(ctx, opts.Address, opts.ID)
if err != nil {
return "", err
}
socket, err := cdshim.NewSocket(address)
if err != nil {
return "", err
if !cdshim.SocketEaddrinuse(err) {
return "", err
}
if err := cdshim.RemoveSocket(address); err != nil {
return "", errors.Wrap(err, "remove already used socket")
}
if socket, err = cdshim.NewSocket(address); err != nil {
return "", err
}
}
defer socket.Close()
defer func() {
if retErr != nil {
socket.Close()
_ = cdshim.RemoveSocket(address)
}
}()
f, err := socket.File()
if err != nil {
return "", err
}
defer f.Close()
cmd.ExtraFiles = append(cmd.ExtraFiles, f)
@ -220,7 +233,7 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
return "", err
}
defer func() {
if err != nil {
if retErr != nil {
cmd.Process.Kill()
}
}()
@ -290,7 +303,7 @@ func getTopic(e interface{}) string {
func trace(ctx context.Context, name string) (otelTrace.Span, context.Context) {
if ctx == nil {
logrus.WithField("type", "bug").Error("trace called before context set")
logrus.WithFields(logrus.Fields{"type": "bug", "name": name}).Error("called before context set")
ctx = context.Background()
}
tracer := otel.Tracer("kata")

View File

@ -78,7 +78,7 @@ func validBundle(containerID, bundlePath string) (string, error) {
return resolved, nil
}
func getAddress(ctx context.Context, bundlePath, id string) (string, error) {
func getAddress(ctx context.Context, bundlePath, address, id string) (string, error) {
var err error
// Checks the MUST and MUST NOT from OCI runtime specification
@ -101,7 +101,7 @@ func getAddress(ctx context.Context, bundlePath, id string) (string, error) {
if err != nil {
return "", err
}
address, err := cdshim.SocketAddress(ctx, sandboxID)
address, err := cdshim.SocketAddress(ctx, address, sandboxID)
if err != nil {
return "", err
}

View File

@ -120,6 +120,7 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@ -585,6 +586,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@ -743,6 +745,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=

View File

@ -0,0 +1,5 @@
test/test
test/piggie
test/phaul
image
rpc/rpc.proto

View File

@ -0,0 +1,28 @@
language: go
dist: bionic
os:
- linux
go:
- "1.14.x"
- "1.13.x"
- tip
env:
# Run the tests with CRIU master and criu-dev
- CRIU_BRANCH="master"
- CRIU_BRANCH="criu-dev"
install:
- sudo apt-get update
- sudo apt-get install -y libprotobuf-dev libprotobuf-c0-dev protobuf-c-compiler protobuf-compiler python-protobuf libnl-3-dev libnet-dev libcap-dev
- make install.tools
- go get github.com/checkpoint-restore/go-criu
- git clone --single-branch -b ${CRIU_BRANCH} https://github.com/checkpoint-restore/criu.git
- cd criu; make
- sudo install -D -m 755 criu/criu /usr/sbin/
- cd ..
script:
# This builds the code without running the tests.
- make lint build phaul test/test test/phaul test/piggie
# Run actual test as root as it uses CRIU.
- sudo make test phaul-test
# This builds crit-go
- make -C crit-go/magic-gen lint build magicgen test

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

View File

@ -0,0 +1,60 @@
GO ?= go
CC ?= gcc
ifeq ($(GOPATH),)
export GOPATH := $(shell $(GO) env GOPATH)
endif
FIRST_GOPATH := $(firstword $(subst :, ,$(GOPATH)))
GOBIN := $(shell $(GO) env GOBIN)
ifeq ($(GOBIN),)
GOBIN := $(FIRST_GOPATH)/bin
endif
all: build test phaul phaul-test
lint:
@golint -set_exit_status . test phaul
build:
@$(GO) build -v
test/piggie: test/piggie.c
@$(CC) $^ -o $@
test/test: test/main.go
@$(GO) build -v -o test/test test/main.go
test: test/test test/piggie
mkdir -p image
test/piggie
test/test dump `pidof piggie` image
test/test restore image
pkill -9 piggie || :
phaul:
@cd phaul; go build -v
test/phaul: test/phaul-main.go
@$(GO) build -v -o test/phaul test/phaul-main.go
phaul-test: test/phaul test/piggie
rm -rf image
test/piggie
test/phaul `pidof piggie`
pkill -9 piggie || :
clean:
@rm -f test/test test/piggie test/phaul
@rm -rf image
@rm -f rpc/rpc.proto
install.tools:
if [ ! -x "$(GOBIN)/golint" ]; then \
$(GO) get -u golang.org/x/lint/golint; \
fi
rpc/rpc.proto:
curl -s https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@
rpc/rpc.pb.go: rpc/rpc.proto
protoc --go_out=. $^
.PHONY: build test clean lint phaul

View File

@ -0,0 +1,75 @@
[![master](https://travis-ci.org/checkpoint-restore/go-criu.svg?branch=master)](https://travis-ci.org/checkpoint-restore/go-criu)
## go-criu -- Go bindings for [CRIU](https://criu.org/)
This repository provides Go bindings for CRIU. The code is based on the Go based PHaul
implementation from the CRIU repository. For easier inclusion into other Go projects the
CRIU Go bindings have been moved to this repository.
The Go bindings provide an easy way to use the CRIU RPC calls from Go without the need
to set up all the infrastructure to make the actual RPC connection to CRIU.
The following example would print the version of CRIU:
```
c := criu.MakeCriu()
version, err := c.GetCriuVersion()
fmt.Println(version)
```
or to just check if at least a certain CRIU version is installed:
```
c := criu.MakeCriu()
result, err := c.IsCriuAtLeast(31100)
```
## Releases
The first go-criu release was 3.11 based on CRIU 3.11. The initial plan
was to follow CRIU so that go-criu would carry the same version number as
CRIU.
As go-criu is imported in other projects and as Go modules are expected
to follow Semantic Versioning go-criu will also follow Semantic Versioning
starting with the 4.0.0 release.
4.0.0 is based on CRIU 3.14
## How to contribute
While bug fixes can first be identified via an "issue", that is not required.
It's ok to just open up a PR with the fix, but make sure you include the same
information you would have included in an issue - like how to reproduce it.
PRs for new features should include some background on what use cases the
new code is trying to address. When possible and when it makes sense, try to
break-up larger PRs into smaller ones - it's easier to review smaller
code changes. But only if those smaller ones make sense as stand-alone PRs.
Regardless of the type of PR, all PRs should include:
* well documented code changes
* additional testcases. Ideally, they should fail w/o your code change applied
* documentation changes
Squash your commits into logical pieces of work that might want to be reviewed
separate from the rest of the PRs. Ideally, each commit should implement a
single idea, and the PR branch should pass the tests at every commit. GitHub
makes it easy to review the cumulative effect of many commits; so, when in
doubt, use smaller commits.
PRs that fix issues should include a reference like `Closes #XXXX` in the
commit message so that github will automatically close the referenced issue
when the PR is merged.
Contributors must assert that they are in compliance with the [Developer
Certificate of Origin 1.1](http://developercertificate.org/). This is achieved
by adding a "Signed-off-by" line containing the contributor's name and e-mail
to every commit message. Your signature certifies that you wrote the patch or
otherwise have the right to pass it on as an open-source patch.
### License and copyright
Unless mentioned otherwise in a specific file's header, all code in
this project is released under the Apache 2.0 license.
The author of a change remains the copyright holder of their code
(no copyright assignment). The list of authors and contributors can be
retrieved from the git commit history and in some cases, the file headers.

View File

@ -0,0 +1,5 @@
module github.com/checkpoint-restore/go-criu/v4
go 1.13
require github.com/golang/protobuf v1.3.5

View File

@ -0,0 +1,2 @@
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=

View File

@ -0,0 +1,259 @@
package criu
import (
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"syscall"
"github.com/checkpoint-restore/go-criu/v4/rpc"
"github.com/golang/protobuf/proto"
)
// Criu struct
type Criu struct {
swrkCmd *exec.Cmd
swrkSk *os.File
swrkPath string
}
// MakeCriu returns the Criu object required for most operations
func MakeCriu() *Criu {
return &Criu{
swrkPath: "criu",
}
}
// SetCriuPath allows setting the path to the CRIU binary
// if it is in a non standard location
func (c *Criu) SetCriuPath(path string) {
c.swrkPath = path
}
// Prepare sets up everything for the RPC communication to CRIU
func (c *Criu) Prepare() error {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0)
if err != nil {
return err
}
cln := os.NewFile(uintptr(fds[0]), "criu-xprt-cln")
syscall.CloseOnExec(fds[0])
srv := os.NewFile(uintptr(fds[1]), "criu-xprt-srv")
defer srv.Close()
args := []string{"swrk", strconv.Itoa(fds[1])}
cmd := exec.Command(c.swrkPath, args...)
err = cmd.Start()
if err != nil {
cln.Close()
return err
}
c.swrkCmd = cmd
c.swrkSk = cln
return nil
}
// Cleanup cleans up
func (c *Criu) Cleanup() {
if c.swrkCmd != nil {
c.swrkSk.Close()
c.swrkSk = nil
c.swrkCmd.Wait()
c.swrkCmd = nil
}
}
func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
cln := c.swrkSk
_, err := cln.Write(reqB)
if err != nil {
return nil, 0, err
}
respB := make([]byte, 2*4096)
n, err := cln.Read(respB)
if err != nil {
return nil, 0, err
}
return respB, n, nil
}
func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
resp, err := c.doSwrkWithResp(reqType, opts, nfy)
if err != nil {
return err
}
respType := resp.GetType()
if respType != reqType {
return errors.New("unexpected responce")
}
return nil
}
func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) (*rpc.CriuResp, error) {
var resp *rpc.CriuResp
req := rpc.CriuReq{
Type: &reqType,
Opts: opts,
}
if nfy != nil {
opts.NotifyScripts = proto.Bool(true)
}
if c.swrkCmd == nil {
err := c.Prepare()
if err != nil {
return nil, err
}
defer c.Cleanup()
}
for {
reqB, err := proto.Marshal(&req)
if err != nil {
return nil, err
}
respB, respS, err := c.sendAndRecv(reqB)
if err != nil {
return nil, err
}
resp = &rpc.CriuResp{}
err = proto.Unmarshal(respB[:respS], resp)
if err != nil {
return nil, err
}
if !resp.GetSuccess() {
return resp, fmt.Errorf("operation failed (msg:%s err:%d)",
resp.GetCrErrmsg(), resp.GetCrErrno())
}
respType := resp.GetType()
if respType != rpc.CriuReqType_NOTIFY {
break
}
if nfy == nil {
return resp, errors.New("unexpected notify")
}
notify := resp.GetNotify()
switch notify.GetScript() {
case "pre-dump":
err = nfy.PreDump()
case "post-dump":
err = nfy.PostDump()
case "pre-restore":
err = nfy.PreRestore()
case "post-restore":
err = nfy.PostRestore(notify.GetPid())
case "network-lock":
err = nfy.NetworkLock()
case "network-unlock":
err = nfy.NetworkUnlock()
case "setup-namespaces":
err = nfy.SetupNamespaces(notify.GetPid())
case "post-setup-namespaces":
err = nfy.PostSetupNamespaces()
case "post-resume":
err = nfy.PostResume()
default:
err = nil
}
if err != nil {
return resp, err
}
req = rpc.CriuReq{
Type: &respType,
NotifySuccess: proto.Bool(true),
}
}
return resp, nil
}
// Dump dumps a process
func (c *Criu) Dump(opts rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_DUMP, &opts, nfy)
}
// Restore restores a process
func (c *Criu) Restore(opts rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_RESTORE, &opts, nfy)
}
// PreDump does a pre-dump
func (c *Criu) PreDump(opts rpc.CriuOpts, nfy Notify) error {
return c.doSwrk(rpc.CriuReqType_PRE_DUMP, &opts, nfy)
}
// StartPageServer starts the page server
func (c *Criu) StartPageServer(opts rpc.CriuOpts) error {
return c.doSwrk(rpc.CriuReqType_PAGE_SERVER, &opts, nil)
}
// StartPageServerChld starts the page server and returns PID and port
func (c *Criu) StartPageServerChld(opts rpc.CriuOpts) (int, int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, &opts, nil)
if err != nil {
return 0, 0, err
}
return int(resp.Ps.GetPid()), int(resp.Ps.GetPort()), nil
}
// GetCriuVersion executes the VERSION RPC call and returns the version
// as an integer. Major * 10000 + Minor * 100 + SubLevel
func (c *Criu) GetCriuVersion() (int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil)
if err != nil {
return 0, err
}
if resp.GetType() != rpc.CriuReqType_VERSION {
return 0, fmt.Errorf("Unexpected CRIU RPC response")
}
version := int(*resp.GetVersion().MajorNumber) * 10000
version += int(*resp.GetVersion().MinorNumber) * 100
if resp.GetVersion().Sublevel != nil {
version += int(*resp.GetVersion().Sublevel)
}
if resp.GetVersion().Gitid != nil {
// taken from runc: if it is a git release -> increase minor by 1
version -= (version % 100)
version += 100
}
return version, nil
}
// IsCriuAtLeast checks if the version is at least the same
// as the parameter version
func (c *Criu) IsCriuAtLeast(version int) (bool, error) {
criuVersion, err := c.GetCriuVersion()
if err != nil {
return false, err
}
if criuVersion >= version {
return true, nil
}
return false, nil
}

View File

@ -0,0 +1,63 @@
package criu
//Notify interface
type Notify interface {
PreDump() error
PostDump() error
PreRestore() error
PostRestore(pid int32) error
NetworkLock() error
NetworkUnlock() error
SetupNamespaces(pid int32) error
PostSetupNamespaces() error
PostResume() error
}
// NoNotify struct
type NoNotify struct {
}
// PreDump NoNotify
func (c NoNotify) PreDump() error {
return nil
}
// PostDump NoNotify
func (c NoNotify) PostDump() error {
return nil
}
// PreRestore NoNotify
func (c NoNotify) PreRestore() error {
return nil
}
// PostRestore NoNotify
func (c NoNotify) PostRestore(pid int32) error {
return nil
}
// NetworkLock NoNotify
func (c NoNotify) NetworkLock() error {
return nil
}
// NetworkUnlock NoNotify
func (c NoNotify) NetworkUnlock() error {
return nil
}
// SetupNamespaces NoNotify
func (c NoNotify) SetupNamespaces(pid int32) error {
return nil
}
// PostSetupNamespaces NoNotify
func (c NoNotify) PostSetupNamespaces() error {
return nil
}
// PostResume NoNotify
func (c NoNotify) PostResume() error {
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,397 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: github.com/containerd/containerd/pkg/runtimeoptions/v1/api.proto
package runtimeoptions_v1
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
reflect "reflect"
strings "strings"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Options struct {
// TypeUrl specifies the type of the content inside the config file.
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
// ConfigPath specifies the filesystem location of the config file
// used by the runtime.
ConfigPath string `protobuf:"bytes,2,opt,name=config_path,json=configPath,proto3" json:"config_path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Options) Reset() { *m = Options{} }
func (*Options) ProtoMessage() {}
func (*Options) Descriptor() ([]byte, []int) {
return fileDescriptor_7700dd27e3487aa6, []int{0}
}
func (m *Options) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Options.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Options) XXX_Merge(src proto.Message) {
xxx_messageInfo_Options.Merge(m, src)
}
func (m *Options) XXX_Size() int {
return m.Size()
}
func (m *Options) XXX_DiscardUnknown() {
xxx_messageInfo_Options.DiscardUnknown(m)
}
var xxx_messageInfo_Options proto.InternalMessageInfo
func (m *Options) GetTypeUrl() string {
if m != nil {
return m.TypeUrl
}
return ""
}
func (m *Options) GetConfigPath() string {
if m != nil {
return m.ConfigPath
}
return ""
}
func init() {
proto.RegisterType((*Options)(nil), "runtimeoptions.v1.Options")
}
func init() {
proto.RegisterFile("github.com/containerd/containerd/pkg/runtimeoptions/v1/api.proto", fileDescriptor_7700dd27e3487aa6)
}
var fileDescriptor_7700dd27e3487aa6 = []byte{
// 214 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x48, 0xcf, 0x2c, 0xc9,
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
0x4a, 0x41, 0x66, 0x16, 0x64, 0xa7, 0xeb, 0x17, 0x95, 0xe6, 0x95, 0x64, 0xe6, 0xa6, 0xe6, 0x17,
0x94, 0x64, 0xe6, 0xe7, 0x15, 0xeb, 0x97, 0x19, 0xea, 0x27, 0x16, 0x64, 0xea, 0x15, 0x14, 0xe5,
0x97, 0xe4, 0x0b, 0x09, 0xa2, 0x4a, 0xea, 0x95, 0x19, 0x4a, 0xe9, 0x22, 0x19, 0x9a, 0x9e, 0x9f,
0x9e, 0xaf, 0x0f, 0x56, 0x99, 0x54, 0x9a, 0x06, 0xe6, 0x81, 0x39, 0x60, 0x16, 0xc4, 0x04, 0x25,
0x57, 0x2e, 0x76, 0x7f, 0x88, 0x66, 0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2,
0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48,
0x9e, 0x8b, 0x3b, 0x39, 0x3f, 0x2f, 0x2d, 0x33, 0x3d, 0xbe, 0x20, 0xb1, 0x24, 0x43, 0x82, 0x09,
0x2c, 0xcb, 0x05, 0x11, 0x0a, 0x48, 0x2c, 0xc9, 0x70, 0x4a, 0x3b, 0xf1, 0x50, 0x8e, 0xf1, 0xc6,
0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8,
0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x51, 0x1e, 0xe4, 0x79, 0xd4, 0x1a, 0x55, 0x24,
0xbe, 0xcc, 0x30, 0x89, 0x0d, 0xec, 0x6a, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x91, 0x3c,
0x3e, 0x79, 0x3b, 0x01, 0x00, 0x00,
}
func (m *Options) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Options) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Options) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.ConfigPath) > 0 {
i -= len(m.ConfigPath)
copy(dAtA[i:], m.ConfigPath)
i = encodeVarintApi(dAtA, i, uint64(len(m.ConfigPath)))
i--
dAtA[i] = 0x12
}
if len(m.TypeUrl) > 0 {
i -= len(m.TypeUrl)
copy(dAtA[i:], m.TypeUrl)
i = encodeVarintApi(dAtA, i, uint64(len(m.TypeUrl)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintApi(dAtA []byte, offset int, v uint64) int {
offset -= sovApi(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Options) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.TypeUrl)
if l > 0 {
n += 1 + l + sovApi(uint64(l))
}
l = len(m.ConfigPath)
if l > 0 {
n += 1 + l + sovApi(uint64(l))
}
return n
}
func sovApi(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozApi(x uint64) (n int) {
return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (this *Options) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&Options{`,
`TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`,
`ConfigPath:` + fmt.Sprintf("%v", this.ConfigPath) + `,`,
`}`,
}, "")
return s
}
func valueToStringApi(v interface{}) string {
rv := reflect.ValueOf(v)
if rv.IsNil() {
return "nil"
}
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *Options) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Options: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthApi
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthApi
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.TypeUrl = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ConfigPath", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowApi
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthApi
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthApi
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ConfigPath = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipApi(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthApi
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipApi(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowApi
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowApi
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowApi
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthApi
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupApi
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthApi
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowApi = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupApi = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -0,0 +1,25 @@
// To regenerate api.pb.go run `make protos`
syntax = "proto3";
package runtimeoptions.v1;
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
option (gogoproto.goproto_stringer_all) = false;
option (gogoproto.stringer_all) = true;
option (gogoproto.goproto_getters_all) = true;
option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_unrecognized_all) = false;
option go_package = "github.com/containerd/containerd/pkg/runtimeoptions/v1;runtimeoptions_v1";
message Options {
// TypeUrl specifies the type of the content inside the config file.
string type_url = 1;
// ConfigPath specifies the filesystem location of the config file
// used by the runtime.
string config_path = 2;
}

View File

@ -0,0 +1 @@
/gocp

191
src/runtime/vendor/github.com/mrunalp/fileutils/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2014 Docker, Inc.
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.

View File

@ -0,0 +1 @@
Mrunal Patel <mrunalp@gmail.com> (@mrunalp)

View File

@ -0,0 +1,5 @@
# fileutils
Collection of utilities for file manipulation in golang
The library is based on docker pkg/archive pkg/idtools but does copies instead of handling archive formats.

View File

@ -0,0 +1,168 @@
package fileutils
import (
"fmt"
"io"
"os"
"path/filepath"
"syscall"
)
// CopyFile copies the file at source to dest
func CopyFile(source string, dest string) error {
si, err := os.Lstat(source)
if err != nil {
return err
}
st, ok := si.Sys().(*syscall.Stat_t)
if !ok {
return fmt.Errorf("could not convert to syscall.Stat_t")
}
uid := int(st.Uid)
gid := int(st.Gid)
modeType := si.Mode() & os.ModeType
// Handle symlinks
if modeType == os.ModeSymlink {
target, err := os.Readlink(source)
if err != nil {
return err
}
if err := os.Symlink(target, dest); err != nil {
return err
}
}
// Handle device files
if modeType == os.ModeDevice {
devMajor := int64(major(uint64(st.Rdev)))
devMinor := int64(minor(uint64(st.Rdev)))
mode := uint32(si.Mode() & os.ModePerm)
if si.Mode()&os.ModeCharDevice != 0 {
mode |= syscall.S_IFCHR
} else {
mode |= syscall.S_IFBLK
}
if err := syscall.Mknod(dest, mode, int(mkdev(devMajor, devMinor))); err != nil {
return err
}
}
// Handle regular files
if si.Mode().IsRegular() {
err = copyInternal(source, dest)
if err != nil {
return err
}
}
// Chown the file
if err := os.Lchown(dest, uid, gid); err != nil {
return err
}
// Chmod the file
if !(modeType == os.ModeSymlink) {
if err := os.Chmod(dest, si.Mode()); err != nil {
return err
}
}
return nil
}
func copyInternal(source, dest string) (retErr error) {
sf, err := os.Open(source)
if err != nil {
return err
}
defer sf.Close()
df, err := os.Create(dest)
if err != nil {
return err
}
defer func() {
err := df.Close()
if retErr == nil {
retErr = err
}
}()
_, err = io.Copy(df, sf)
return err
}
// CopyDirectory copies the files under the source directory
// to dest directory. The dest directory is created if it
// does not exist.
func CopyDirectory(source string, dest string) error {
fi, err := os.Stat(source)
if err != nil {
return err
}
// Get owner.
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return fmt.Errorf("could not convert to syscall.Stat_t")
}
// We have to pick an owner here anyway.
if err := MkdirAllNewAs(dest, fi.Mode(), int(st.Uid), int(st.Gid)); err != nil {
return err
}
return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Get the relative path
relPath, err := filepath.Rel(source, path)
if err != nil {
return nil
}
if info.IsDir() {
// Skip the source directory.
if path != source {
// Get the owner.
st, ok := info.Sys().(*syscall.Stat_t)
if !ok {
return fmt.Errorf("could not convert to syscall.Stat_t")
}
uid := int(st.Uid)
gid := int(st.Gid)
if err := os.Mkdir(filepath.Join(dest, relPath), info.Mode()); err != nil {
return err
}
if err := os.Lchown(filepath.Join(dest, relPath), uid, gid); err != nil {
return err
}
}
return nil
}
return CopyFile(path, filepath.Join(dest, relPath))
})
}
// Gives a number indicating the device driver to be used to access the passed device
func major(device uint64) uint64 {
return (device >> 8) & 0xfff
}
// Gives a number that serves as a flag to the device driver for the passed device
func minor(device uint64) uint64 {
return (device & 0xff) | ((device >> 12) & 0xfff00)
}
func mkdev(major int64, minor int64) uint32 {
return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
}

View File

@ -0,0 +1,3 @@
module github.com/mrunalp/fileutils
go 1.13

View File

@ -0,0 +1,54 @@
package fileutils
import (
"os"
"path/filepath"
"syscall"
)
// MkdirAllNewAs creates a directory (include any along the path) and then modifies
// ownership ONLY of newly created directories to the requested uid/gid. If the
// directories along the path exist, no change of ownership will be performed
func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
// make an array containing the original path asked for, plus (for mkAll == true)
// all path components leading up to the complete path that don't exist before we MkdirAll
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
// chown the full directory path if it exists
var paths []string
st, err := os.Stat(path)
if err != nil && os.IsNotExist(err) {
paths = []string{path}
} else if err == nil {
if !st.IsDir() {
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
}
// nothing to do; directory path fully exists already
return nil
}
// walk back to "/" looking for directories which do not exist
// and add them to the paths array for chown after creation
dirPath := path
for {
dirPath = filepath.Dir(dirPath)
if dirPath == "/" {
break
}
if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
paths = append(paths, dirPath)
}
}
if err := os.MkdirAll(path, mode); err != nil {
return err
}
// even if it existed, we will chown the requested path + any subpaths that
// didn't exist when we called MkdirAll
for _, pathComponent := range paths {
if err := os.Chown(pathComponent, ownerUID, ownerGID); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,330 @@
# libcontainer
[![GoDoc](https://godoc.org/github.com/opencontainers/runc/libcontainer?status.svg)](https://godoc.org/github.com/opencontainers/runc/libcontainer)
Libcontainer provides a native Go implementation for creating containers
with namespaces, cgroups, capabilities, and filesystem access controls.
It allows you to manage the lifecycle of the container performing additional operations
after the container is created.
#### Container
A container is a self contained execution environment that shares the kernel of the
host system and which is (optionally) isolated from other containers in the system.
#### Using libcontainer
Because containers are spawned in a two step process you will need a binary that
will be executed as the init process for the container. In libcontainer, we use
the current binary (/proc/self/exe) to be executed as the init process, and use
arg "init", we call the first step process "bootstrap", so you always need a "init"
function as the entry of "bootstrap".
In addition to the go init function the early stage bootstrap is handled by importing
[nsenter](https://github.com/opencontainers/runc/blob/master/libcontainer/nsenter/README.md).
```go
import (
_ "github.com/opencontainers/runc/libcontainer/nsenter"
)
func init() {
if len(os.Args) > 1 && os.Args[1] == "init" {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
factory, _ := libcontainer.New("")
if err := factory.StartInitialization(); err != nil {
logrus.Fatal(err)
}
panic("--this line should have never been executed, congratulations--")
}
}
```
Then to create a container you first have to initialize an instance of a factory
that will handle the creation and initialization for a container.
```go
factory, err := libcontainer.New("/var/lib/container", libcontainer.Cgroupfs, libcontainer.InitArgs(os.Args[0], "init"))
if err != nil {
logrus.Fatal(err)
return
}
```
Once you have an instance of the factory created we can create a configuration
struct describing how the container is to be created. A sample would look similar to this:
```go
defaultMountFlags := unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
config := &configs.Config{
Rootfs: "/your/path/to/rootfs",
Capabilities: &configs.Capabilities{
Bounding: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Effective: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Inheritable: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Permitted: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
Ambient: []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
},
},
Namespaces: configs.Namespaces([]configs.Namespace{
{Type: configs.NEWNS},
{Type: configs.NEWUTS},
{Type: configs.NEWIPC},
{Type: configs.NEWPID},
{Type: configs.NEWUSER},
{Type: configs.NEWNET},
{Type: configs.NEWCGROUP},
}),
Cgroups: &configs.Cgroup{
Name: "test-container",
Parent: "system",
Resources: &configs.Resources{
MemorySwappiness: nil,
Devices: specconv.AllowedDevices,
},
},
MaskPaths: []string{
"/proc/kcore",
"/sys/firmware",
},
ReadonlyPaths: []string{
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
},
Devices: specconv.AllowedDevices,
Hostname: "testing",
Mounts: []*configs.Mount{
{
Source: "proc",
Destination: "/proc",
Device: "proc",
Flags: defaultMountFlags,
},
{
Source: "tmpfs",
Destination: "/dev",
Device: "tmpfs",
Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
Data: "mode=755",
},
{
Source: "devpts",
Destination: "/dev/pts",
Device: "devpts",
Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
},
{
Device: "tmpfs",
Source: "shm",
Destination: "/dev/shm",
Data: "mode=1777,size=65536k",
Flags: defaultMountFlags,
},
{
Source: "mqueue",
Destination: "/dev/mqueue",
Device: "mqueue",
Flags: defaultMountFlags,
},
{
Source: "sysfs",
Destination: "/sys",
Device: "sysfs",
Flags: defaultMountFlags | unix.MS_RDONLY,
},
},
UidMappings: []configs.IDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 65536,
},
},
GidMappings: []configs.IDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 65536,
},
},
Networks: []*configs.Network{
{
Type: "loopback",
Address: "127.0.0.1/0",
Gateway: "localhost",
},
},
Rlimits: []configs.Rlimit{
{
Type: unix.RLIMIT_NOFILE,
Hard: uint64(1025),
Soft: uint64(1025),
},
},
}
```
Once you have the configuration populated you can create a container:
```go
container, err := factory.Create("container-id", config)
if err != nil {
logrus.Fatal(err)
return
}
```
To spawn bash as the initial process inside the container and have the
processes pid returned in order to wait, signal, or kill the process:
```go
process := &libcontainer.Process{
Args: []string{"/bin/bash"},
Env: []string{"PATH=/bin"},
User: "daemon",
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Init: true,
}
err := container.Run(process)
if err != nil {
container.Destroy()
logrus.Fatal(err)
return
}
// wait for the process to finish.
_, err := process.Wait()
if err != nil {
logrus.Fatal(err)
}
// destroy the container.
container.Destroy()
```
Additional ways to interact with a running container are:
```go
// return all the pids for all processes running inside the container.
processes, err := container.Processes()
// get detailed cpu, memory, io, and network statistics for the container and
// it's processes.
stats, err := container.Stats()
// pause all processes inside the container.
container.Pause()
// resume all paused processes.
container.Resume()
// send signal to container's init process.
container.Signal(signal)
// update container resource constraints.
container.Set(config)
// get current status of the container.
status, err := container.Status()
// get current container's state information.
state, err := container.State()
```
#### Checkpoint & Restore
libcontainer now integrates [CRIU](http://criu.org/) for checkpointing and restoring containers.
This let's you save the state of a process running inside a container to disk, and then restore
that state into a new process, on the same machine or on another machine.
`criu` version 1.5.2 or higher is required to use checkpoint and restore.
If you don't already have `criu` installed, you can build it from source, following the
[online instructions](http://criu.org/Installation). `criu` is also installed in the docker image
generated when building libcontainer with docker.
## Copyright and license
Code and documentation copyright 2014 Docker, inc.
The code and documentation are released under the [Apache 2.0 license](../LICENSE).
The documentation is also released under Creative Commons Attribution 4.0 International License.
You may obtain a copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/.

View File

@ -0,0 +1,465 @@
## Container Specification - v1
This is the standard configuration for version 1 containers. It includes
namespaces, standard filesystem setup, a default Linux capability set, and
information about resource reservations. It also has information about any
populated environment settings for the processes running inside a container.
Along with the configuration of how a container is created the standard also
discusses actions that can be performed on a container to manage and inspect
information about the processes running inside.
The v1 profile is meant to be able to accommodate the majority of applications
with a strong security configuration.
### System Requirements and Compatibility
Minimum requirements:
* Kernel version - 3.10 recommended 2.6.2x minimum(with backported patches)
* Mounted cgroups with each subsystem in its own hierarchy
### Namespaces
| Flag | Enabled |
| --------------- | ------- |
| CLONE_NEWPID | 1 |
| CLONE_NEWUTS | 1 |
| CLONE_NEWIPC | 1 |
| CLONE_NEWNET | 1 |
| CLONE_NEWNS | 1 |
| CLONE_NEWUSER | 1 |
| CLONE_NEWCGROUP | 1 |
Namespaces are created for the container via the `unshare` syscall.
### Filesystem
A root filesystem must be provided to a container for execution. The container
will use this root filesystem (rootfs) to jail and spawn processes inside where
the binaries and system libraries are local to that directory. Any binaries
to be executed must be contained within this rootfs.
Mounts that happen inside the container are automatically cleaned up when the
container exits as the mount namespace is destroyed and the kernel will
unmount all the mounts that were setup within that namespace.
For a container to execute properly there are certain filesystems that
are required to be mounted within the rootfs that the runtime will setup.
| Path | Type | Flags | Data |
| ----------- | ------ | -------------------------------------- | ---------------------------------------- |
| /proc | proc | MS_NOEXEC,MS_NOSUID,MS_NODEV | |
| /dev | tmpfs | MS_NOEXEC,MS_STRICTATIME | mode=755 |
| /dev/shm | tmpfs | MS_NOEXEC,MS_NOSUID,MS_NODEV | mode=1777,size=65536k |
| /dev/mqueue | mqueue | MS_NOEXEC,MS_NOSUID,MS_NODEV | |
| /dev/pts | devpts | MS_NOEXEC,MS_NOSUID | newinstance,ptmxmode=0666,mode=620,gid=5 |
| /sys | sysfs | MS_NOEXEC,MS_NOSUID,MS_NODEV,MS_RDONLY | |
After a container's filesystems are mounted within the newly created
mount namespace `/dev` will need to be populated with a set of device nodes.
It is expected that a rootfs does not need to have any device nodes specified
for `/dev` within the rootfs as the container will setup the correct devices
that are required for executing a container's process.
| Path | Mode | Access |
| ------------ | ---- | ---------- |
| /dev/null | 0666 | rwm |
| /dev/zero | 0666 | rwm |
| /dev/full | 0666 | rwm |
| /dev/tty | 0666 | rwm |
| /dev/random | 0666 | rwm |
| /dev/urandom | 0666 | rwm |
**ptmx**
`/dev/ptmx` will need to be a symlink to the host's `/dev/ptmx` within
the container.
The use of a pseudo TTY is optional within a container and it should support both.
If a pseudo is provided to the container `/dev/console` will need to be
setup by binding the console in `/dev/` after it has been populated and mounted
in tmpfs.
| Source | Destination | UID GID | Mode | Type |
| --------------- | ------------ | ------- | ---- | ---- |
| *pty host path* | /dev/console | 0 0 | 0600 | bind |
After `/dev/null` has been setup we check for any external links between
the container's io, STDIN, STDOUT, STDERR. If the container's io is pointing
to `/dev/null` outside the container we close and `dup2` the `/dev/null`
that is local to the container's rootfs.
After the container has `/proc` mounted a few standard symlinks are setup
within `/dev/` for the io.
| Source | Destination |
| --------------- | ----------- |
| /proc/self/fd | /dev/fd |
| /proc/self/fd/0 | /dev/stdin |
| /proc/self/fd/1 | /dev/stdout |
| /proc/self/fd/2 | /dev/stderr |
A `pivot_root` is used to change the root for the process, effectively
jailing the process inside the rootfs.
```c
put_old = mkdir(...);
pivot_root(rootfs, put_old);
chdir("/");
unmount(put_old, MS_DETACH);
rmdir(put_old);
```
For container's running with a rootfs inside `ramfs` a `MS_MOVE` combined
with a `chroot` is required as `pivot_root` is not supported in `ramfs`.
```c
mount(rootfs, "/", NULL, MS_MOVE, NULL);
chroot(".");
chdir("/");
```
The `umask` is set back to `0022` after the filesystem setup has been completed.
### Resources
Cgroups are used to handle resource allocation for containers. This includes
system resources like cpu, memory, and device access.
| Subsystem | Enabled |
| ---------- | ------- |
| devices | 1 |
| memory | 1 |
| cpu | 1 |
| cpuacct | 1 |
| cpuset | 1 |
| blkio | 1 |
| perf_event | 1 |
| freezer | 1 |
| hugetlb | 1 |
| pids | 1 |
All cgroup subsystem are joined so that statistics can be collected from
each of the subsystems. Freezer does not expose any stats but is joined
so that containers can be paused and resumed.
The parent process of the container's init must place the init pid inside
the correct cgroups before the initialization begins. This is done so
that no processes or threads escape the cgroups. This sync is
done via a pipe ( specified in the runtime section below ) that the container's
init process will block waiting for the parent to finish setup.
### IntelRdt
Intel platforms with new Xeon CPU support Resource Director Technology (RDT).
Cache Allocation Technology (CAT) and Memory Bandwidth Allocation (MBA) are
two sub-features of RDT.
Cache Allocation Technology (CAT) provides a way for the software to restrict
cache allocation to a defined 'subset' of L3 cache which may be overlapping
with other 'subsets'. The different subsets are identified by class of
service (CLOS) and each CLOS has a capacity bitmask (CBM).
Memory Bandwidth Allocation (MBA) provides indirect and approximate throttle
over memory bandwidth for the software. A user controls the resource by
indicating the percentage of maximum memory bandwidth or memory bandwidth limit
in MBps unit if MBA Software Controller is enabled.
It can be used to handle L3 cache and memory bandwidth resources allocation
for containers if hardware and kernel support Intel RDT CAT and MBA features.
In Linux 4.10 kernel or newer, the interface is defined and exposed via
"resource control" filesystem, which is a "cgroup-like" interface.
Comparing with cgroups, it has similar process management lifecycle and
interfaces in a container. But unlike cgroups' hierarchy, it has single level
filesystem layout.
CAT and MBA features are introduced in Linux 4.10 and 4.12 kernel via
"resource control" filesystem.
Intel RDT "resource control" filesystem hierarchy:
```
mount -t resctrl resctrl /sys/fs/resctrl
tree /sys/fs/resctrl
/sys/fs/resctrl/
|-- info
| |-- L3
| | |-- cbm_mask
| | |-- min_cbm_bits
| | |-- num_closids
| |-- MB
| |-- bandwidth_gran
| |-- delay_linear
| |-- min_bandwidth
| |-- num_closids
|-- ...
|-- schemata
|-- tasks
|-- <container_id>
|-- ...
|-- schemata
|-- tasks
```
For runc, we can make use of `tasks` and `schemata` configuration for L3
cache and memory bandwidth resources constraints.
The file `tasks` has a list of tasks that belongs to this group (e.g.,
<container_id>" group). Tasks can be added to a group by writing the task ID
to the "tasks" file (which will automatically remove them from the previous
group to which they belonged). New tasks created by fork(2) and clone(2) are
added to the same group as their parent.
The file `schemata` has a list of all the resources available to this group.
Each resource (L3 cache, memory bandwidth) has its own line and format.
L3 cache schema:
It has allocation bitmasks/values for L3 cache on each socket, which
contains L3 cache id and capacity bitmask (CBM).
```
Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
```
For example, on a two-socket machine, the schema line could be "L3:0=ff;1=c0"
which means L3 cache id 0's CBM is 0xff, and L3 cache id 1's CBM is 0xc0.
The valid L3 cache CBM is a *contiguous bits set* and number of bits that can
be set is less than the max bit. The max bits in the CBM is varied among
supported Intel CPU models. Kernel will check if it is valid when writing.
e.g., default value 0xfffff in root indicates the max bits of CBM is 20
bits, which mapping to entire L3 cache capacity. Some valid CBM values to
set in a group: 0xf, 0xf0, 0x3ff, 0x1f00 and etc.
Memory bandwidth schema:
It has allocation values for memory bandwidth on each socket, which contains
L3 cache id and memory bandwidth.
```
Format: "MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;..."
```
For example, on a two-socket machine, the schema line could be "MB:0=20;1=70"
The minimum bandwidth percentage value for each CPU model is predefined and
can be looked up through "info/MB/min_bandwidth". The bandwidth granularity
that is allocated is also dependent on the CPU model and can be looked up at
"info/MB/bandwidth_gran". The available bandwidth control steps are:
min_bw + N * bw_gran. Intermediate values are rounded to the next control
step available on the hardware.
If MBA Software Controller is enabled through mount option "-o mba_MBps"
mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl
We could specify memory bandwidth in "MBps" (Mega Bytes per second) unit
instead of "percentages". The kernel underneath would use a software feedback
mechanism or a "Software Controller" which reads the actual bandwidth using
MBM counters and adjust the memory bandwidth percentages to ensure:
"actual memory bandwidth < user specified memory bandwidth".
For example, on a two-socket machine, the schema line could be
"MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on socket 0
and 7000 MBps memory bandwidth limit on socket 1.
For more information about Intel RDT kernel interface:
https://www.kernel.org/doc/Documentation/x86/intel_rdt_ui.txt
```
An example for runc:
Consider a two-socket machine with two L3 caches where the default CBM is
0x7ff and the max CBM length is 11 bits, and minimum memory bandwidth of 10%
with a memory bandwidth granularity of 10%.
Tasks inside the container only have access to the "upper" 7/11 of L3 cache
on socket 0 and the "lower" 5/11 L3 cache on socket 1, and may use a
maximum memory bandwidth of 20% on socket 0 and 70% on socket 1.
"linux": {
"intelRdt": {
"closID": "guaranteed_group",
"l3CacheSchema": "L3:0=7f0;1=1f",
"memBwSchema": "MB:0=20;1=70"
}
}
```
### Security
The standard set of Linux capabilities that are set in a container
provide a good default for security and flexibility for the applications.
| Capability | Enabled |
| -------------------- | ------- |
| CAP_NET_RAW | 1 |
| CAP_NET_BIND_SERVICE | 1 |
| CAP_AUDIT_READ | 1 |
| CAP_AUDIT_WRITE | 1 |
| CAP_DAC_OVERRIDE | 1 |
| CAP_SETFCAP | 1 |
| CAP_SETPCAP | 1 |
| CAP_SETGID | 1 |
| CAP_SETUID | 1 |
| CAP_MKNOD | 1 |
| CAP_CHOWN | 1 |
| CAP_FOWNER | 1 |
| CAP_FSETID | 1 |
| CAP_KILL | 1 |
| CAP_SYS_CHROOT | 1 |
| CAP_NET_BROADCAST | 0 |
| CAP_SYS_MODULE | 0 |
| CAP_SYS_RAWIO | 0 |
| CAP_SYS_PACCT | 0 |
| CAP_SYS_ADMIN | 0 |
| CAP_SYS_NICE | 0 |
| CAP_SYS_RESOURCE | 0 |
| CAP_SYS_TIME | 0 |
| CAP_SYS_TTY_CONFIG | 0 |
| CAP_AUDIT_CONTROL | 0 |
| CAP_MAC_OVERRIDE | 0 |
| CAP_MAC_ADMIN | 0 |
| CAP_NET_ADMIN | 0 |
| CAP_SYSLOG | 0 |
| CAP_DAC_READ_SEARCH | 0 |
| CAP_LINUX_IMMUTABLE | 0 |
| CAP_IPC_LOCK | 0 |
| CAP_IPC_OWNER | 0 |
| CAP_SYS_PTRACE | 0 |
| CAP_SYS_BOOT | 0 |
| CAP_LEASE | 0 |
| CAP_WAKE_ALARM | 0 |
| CAP_BLOCK_SUSPEND | 0 |
Additional security layers like [apparmor](https://wiki.ubuntu.com/AppArmor)
and [selinux](http://selinuxproject.org/page/Main_Page) can be used with
the containers. A container should support setting an apparmor profile or
selinux process and mount labels if provided in the configuration.
Standard apparmor profile:
```c
#include <tunables/global>
profile <profile_name> flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network,
capability,
file,
umount,
deny @{PROC}/sys/fs/** wklx,
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
deny @{PROC}/kmem rwklx,
deny @{PROC}/sys/kernel/[^s][^h][^m]* wklx,
deny @{PROC}/sys/kernel/*/** wklx,
deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
}
```
*TODO: seccomp work is being done to find a good default config*
### Runtime and Init Process
During container creation the parent process needs to talk to the container's init
process and have a form of synchronization. This is accomplished by creating
a pipe that is passed to the container's init. When the init process first spawns
it will block on its side of the pipe until the parent closes its side. This
allows the parent to have time to set the new process inside a cgroup hierarchy
and/or write any uid/gid mappings required for user namespaces.
The pipe is passed to the init process via FD 3.
The application consuming libcontainer should be compiled statically. libcontainer
does not define any init process and the arguments provided are used to `exec` the
process inside the application. There should be no long running init within the
container spec.
If a pseudo tty is provided to a container it will open and `dup2` the console
as the container's STDIN, STDOUT, STDERR as well as mounting the console
as `/dev/console`.
An extra set of mounts are provided to a container and setup for use. A container's
rootfs can contain some non portable files inside that can cause side effects during
execution of a process. These files are usually created and populated with the container
specific information via the runtime.
**Extra runtime files:**
* /etc/hosts
* /etc/resolv.conf
* /etc/hostname
* /etc/localtime
#### Defaults
There are a few defaults that can be overridden by users, but in their omission
these apply to processes within a container.
| Type | Value |
| ------------------- | ------------------------------ |
| Parent Death Signal | SIGKILL |
| UID | 0 |
| GID | 0 |
| GROUPS | 0, NULL |
| CWD | "/" |
| $HOME | Current user's home dir or "/" |
| Readonly rootfs | false |
| Pseudo TTY | false |
## Actions
After a container is created there is a standard set of actions that can
be done to the container. These actions are part of the public API for
a container.
| Action | Description |
| -------------- | ------------------------------------------------------------------ |
| Get processes | Return all the pids for processes running inside a container |
| Get Stats | Return resource statistics for the container as a whole |
| Wait | Waits on the container's init process ( pid 1 ) |
| Wait Process | Wait on any of the container's processes returning the exit status |
| Destroy | Kill the container's init process and remove any filesystem state |
| Signal | Send a signal to the container's init process |
| Signal Process | Send a signal to any of the container's processes |
| Pause | Pause all processes inside the container |
| Resume | Resume all processes inside the container if paused |
| Exec | Execute a new process inside of the container ( requires setns ) |
| Set | Setup configs of the container after it's created |
### Execute a new process inside of a running container
User can execute a new process inside of a running container. Any binaries to be
executed must be accessible within the container's rootfs.
The started process will run inside the container's rootfs. Any changes
made by the process to the container's filesystem will persist after the
process finished executing.
The started process will join all the container's existing namespaces. When the
container is paused, the process will also be paused and will resume when
the container is unpaused. The started process will only run when the container's
primary process (PID 1) is running, and will not be restarted when the container
is restarted.
#### Planned additions
The started process will have its own cgroups nested inside the container's
cgroups. This is used for process tracking and optionally resource allocation
handling for the new process. Freezer cgroup is required, the rest of the cgroups
are optional. The process executor must place its pid inside the correct
cgroups before starting the process. This is done so that no child processes or
threads can escape the cgroups.
When the process is stopped, the process executor will try (in a best-effort way)
to stop all its children and remove the sub-cgroups.

View File

@ -0,0 +1,54 @@
package apparmor
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"github.com/opencontainers/runc/libcontainer/utils"
)
// IsEnabled returns true if apparmor is enabled for the host.
func IsEnabled() bool {
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil {
buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
return err == nil && bytes.HasPrefix(buf, []byte("Y"))
}
return false
}
func setProcAttr(attr, value string) error {
// Under AppArmor you can only change your own attr, so use /proc/self/
// instead of /proc/<tid>/ like libapparmor does
f, err := os.OpenFile("/proc/self/attr/"+attr, os.O_WRONLY, 0)
if err != nil {
return err
}
defer f.Close()
if err := utils.EnsureProcHandle(f); err != nil {
return err
}
_, err = f.WriteString(value)
return err
}
// changeOnExec reimplements aa_change_onexec from libapparmor in Go
func changeOnExec(name string) error {
if err := setProcAttr("exec", "exec "+name); err != nil {
return fmt.Errorf("apparmor failed to apply profile: %s", err)
}
return nil
}
// ApplyProfile will apply the profile with the specified name to the process after
// the next exec.
func ApplyProfile(name string) error {
if name == "" {
return nil
}
return changeOnExec(name)
}

View File

@ -0,0 +1,20 @@
// +build !linux
package apparmor
import (
"errors"
)
var ErrApparmorNotEnabled = errors.New("apparmor: config provided but apparmor not supported")
func IsEnabled() bool {
return false
}
func ApplyProfile(name string) error {
if name != "" {
return ErrApparmorNotEnabled
}
return nil
}

View File

@ -0,0 +1,96 @@
// +build linux
package capabilities
import (
"fmt"
"strings"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/syndtr/gocapability/capability"
)
const allCapabilityTypes = capability.CAPS | capability.BOUNDS | capability.AMBS
var capabilityMap map[string]capability.Cap
func init() {
capabilityMap = make(map[string]capability.Cap, capability.CAP_LAST_CAP+1)
for _, c := range capability.List() {
if c > capability.CAP_LAST_CAP {
continue
}
capabilityMap["CAP_"+strings.ToUpper(c.String())] = c
}
}
// New creates a new Caps from the given Capabilities config.
func New(capConfig *configs.Capabilities) (*Caps, error) {
var (
err error
caps Caps
)
if caps.bounding, err = capSlice(capConfig.Bounding); err != nil {
return nil, err
}
if caps.effective, err = capSlice(capConfig.Effective); err != nil {
return nil, err
}
if caps.inheritable, err = capSlice(capConfig.Inheritable); err != nil {
return nil, err
}
if caps.permitted, err = capSlice(capConfig.Permitted); err != nil {
return nil, err
}
if caps.ambient, err = capSlice(capConfig.Ambient); err != nil {
return nil, err
}
if caps.pid, err = capability.NewPid2(0); err != nil {
return nil, err
}
if err = caps.pid.Load(); err != nil {
return nil, err
}
return &caps, nil
}
func capSlice(caps []string) ([]capability.Cap, error) {
out := make([]capability.Cap, len(caps))
for i, c := range caps {
v, ok := capabilityMap[c]
if !ok {
return nil, fmt.Errorf("unknown capability %q", c)
}
out[i] = v
}
return out, nil
}
// Caps holds the capabilities for a container.
type Caps struct {
pid capability.Capabilities
bounding []capability.Cap
effective []capability.Cap
inheritable []capability.Cap
permitted []capability.Cap
ambient []capability.Cap
}
// ApplyBoundingSet sets the capability bounding set to those specified in the whitelist.
func (c *Caps) ApplyBoundingSet() error {
c.pid.Clear(capability.BOUNDS)
c.pid.Set(capability.BOUNDS, c.bounding...)
return c.pid.Apply(capability.BOUNDS)
}
// Apply sets all the capabilities for the current process in the config.
func (c *Caps) ApplyCaps() error {
c.pid.Clear(allCapabilityTypes)
c.pid.Set(capability.BOUNDS, c.bounding...)
c.pid.Set(capability.PERMITTED, c.permitted...)
c.pid.Set(capability.INHERITABLE, c.inheritable...)
c.pid.Set(capability.EFFECTIVE, c.effective...)
c.pid.Set(capability.AMBIENT, c.ambient...)
return c.pid.Apply(allCapabilityTypes)
}

View File

@ -0,0 +1,3 @@
// +build !linux
package capabilities

View File

@ -0,0 +1,90 @@
package validate
import (
"errors"
"fmt"
"strings"
"github.com/opencontainers/runc/libcontainer/configs"
)
// rootlessEUID makes sure that the config can be applied when runc
// is being executed as a non-root user (euid != 0) in the current user namespace.
func (v *ConfigValidator) rootlessEUID(config *configs.Config) error {
if err := rootlessEUIDMappings(config); err != nil {
return err
}
if err := rootlessEUIDMount(config); err != nil {
return err
}
// XXX: We currently can't verify the user config at all, because
// configs.Config doesn't store the user-related configs. So this
// has to be verified by setupUser() in init_linux.go.
return nil
}
func hasIDMapping(id int, mappings []configs.IDMap) bool {
for _, m := range mappings {
if id >= m.ContainerID && id < m.ContainerID+m.Size {
return true
}
}
return false
}
func rootlessEUIDMappings(config *configs.Config) error {
if !config.Namespaces.Contains(configs.NEWUSER) {
return errors.New("rootless container requires user namespaces")
}
if len(config.UidMappings) == 0 {
return errors.New("rootless containers requires at least one UID mapping")
}
if len(config.GidMappings) == 0 {
return errors.New("rootless containers requires at least one GID mapping")
}
return nil
}
// mount verifies that the user isn't trying to set up any mounts they don't have
// the rights to do. In addition, it makes sure that no mount has a `uid=` or
// `gid=` option that doesn't resolve to root.
func rootlessEUIDMount(config *configs.Config) error {
// XXX: We could whitelist allowed devices at this point, but I'm not
// convinced that's a good idea. The kernel is the best arbiter of
// access control.
for _, mount := range config.Mounts {
// Check that the options list doesn't contain any uid= or gid= entries
// that don't resolve to root.
for _, opt := range strings.Split(mount.Data, ",") {
if strings.HasPrefix(opt, "uid=") {
var uid int
n, err := fmt.Sscanf(opt, "uid=%d", &uid)
if n != 1 || err != nil {
// Ignore unknown mount options.
continue
}
if !hasIDMapping(uid, config.UidMappings) {
return errors.New("cannot specify uid= mount options for unmapped uid in rootless containers")
}
}
if strings.HasPrefix(opt, "gid=") {
var gid int
n, err := fmt.Sscanf(opt, "gid=%d", &gid)
if n != 1 || err != nil {
// Ignore unknown mount options.
continue
}
if !hasIDMapping(gid, config.GidMappings) {
return errors.New("cannot specify gid= mount options for unmapped gid in rootless containers")
}
}
}
}
return nil
}

View File

@ -0,0 +1,239 @@
package validate
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/intelrdt"
selinux "github.com/opencontainers/selinux/go-selinux"
"golang.org/x/sys/unix"
)
type Validator interface {
Validate(*configs.Config) error
}
func New() Validator {
return &ConfigValidator{}
}
type ConfigValidator struct {
}
func (v *ConfigValidator) Validate(config *configs.Config) error {
if err := v.rootfs(config); err != nil {
return err
}
if err := v.network(config); err != nil {
return err
}
if err := v.hostname(config); err != nil {
return err
}
if err := v.security(config); err != nil {
return err
}
if err := v.usernamespace(config); err != nil {
return err
}
if err := v.cgroupnamespace(config); err != nil {
return err
}
if err := v.sysctl(config); err != nil {
return err
}
if err := v.intelrdt(config); err != nil {
return err
}
if config.RootlessEUID {
if err := v.rootlessEUID(config); err != nil {
return err
}
}
return nil
}
// rootfs validates if the rootfs is an absolute path and is not a symlink
// to the container's root filesystem.
func (v *ConfigValidator) rootfs(config *configs.Config) error {
if _, err := os.Stat(config.Rootfs); err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("rootfs (%s) does not exist", config.Rootfs)
}
return err
}
cleaned, err := filepath.Abs(config.Rootfs)
if err != nil {
return err
}
if cleaned, err = filepath.EvalSymlinks(cleaned); err != nil {
return err
}
if filepath.Clean(config.Rootfs) != cleaned {
return fmt.Errorf("%s is not an absolute path or is a symlink", config.Rootfs)
}
return nil
}
func (v *ConfigValidator) network(config *configs.Config) error {
if !config.Namespaces.Contains(configs.NEWNET) {
if len(config.Networks) > 0 || len(config.Routes) > 0 {
return errors.New("unable to apply network settings without a private NET namespace")
}
}
return nil
}
func (v *ConfigValidator) hostname(config *configs.Config) error {
if config.Hostname != "" && !config.Namespaces.Contains(configs.NEWUTS) {
return errors.New("unable to set hostname without a private UTS namespace")
}
return nil
}
func (v *ConfigValidator) security(config *configs.Config) error {
// restrict sys without mount namespace
if (len(config.MaskPaths) > 0 || len(config.ReadonlyPaths) > 0) &&
!config.Namespaces.Contains(configs.NEWNS) {
return errors.New("unable to restrict sys entries without a private MNT namespace")
}
if config.ProcessLabel != "" && !selinux.GetEnabled() {
return errors.New("selinux label is specified in config, but selinux is disabled or not supported")
}
return nil
}
func (v *ConfigValidator) usernamespace(config *configs.Config) error {
if config.Namespaces.Contains(configs.NEWUSER) {
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
return errors.New("USER namespaces aren't enabled in the kernel")
}
} else {
if config.UidMappings != nil || config.GidMappings != nil {
return errors.New("User namespace mappings specified, but USER namespace isn't enabled in the config")
}
}
return nil
}
func (v *ConfigValidator) cgroupnamespace(config *configs.Config) error {
if config.Namespaces.Contains(configs.NEWCGROUP) {
if _, err := os.Stat("/proc/self/ns/cgroup"); os.IsNotExist(err) {
return errors.New("cgroup namespaces aren't enabled in the kernel")
}
}
return nil
}
// sysctl validates that the specified sysctl keys are valid or not.
// /proc/sys isn't completely namespaced and depending on which namespaces
// are specified, a subset of sysctls are permitted.
func (v *ConfigValidator) sysctl(config *configs.Config) error {
validSysctlMap := map[string]bool{
"kernel.msgmax": true,
"kernel.msgmnb": true,
"kernel.msgmni": true,
"kernel.sem": true,
"kernel.shmall": true,
"kernel.shmmax": true,
"kernel.shmmni": true,
"kernel.shm_rmid_forced": true,
}
var (
netOnce sync.Once
hostnet bool
hostnetErr error
)
for s := range config.Sysctl {
if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") {
if config.Namespaces.Contains(configs.NEWIPC) {
continue
} else {
return fmt.Errorf("sysctl %q is not allowed in the hosts ipc namespace", s)
}
}
if strings.HasPrefix(s, "net.") {
// Is container using host netns?
// Here "host" means "current", not "initial".
netOnce.Do(func() {
if !config.Namespaces.Contains(configs.NEWNET) {
hostnet = true
return
}
path := config.Namespaces.PathOf(configs.NEWNET)
if path == "" {
// own netns, so hostnet = false
return
}
hostnet, hostnetErr = isHostNetNS(path)
})
if hostnetErr != nil {
return hostnetErr
}
if hostnet {
return fmt.Errorf("sysctl %q not allowed in host network namespace", s)
}
continue
}
if config.Namespaces.Contains(configs.NEWUTS) {
switch s {
case "kernel.domainname":
// This is namespaced and there's no explicit OCI field for it.
continue
case "kernel.hostname":
// This is namespaced but there's a conflicting (dedicated) OCI field for it.
return fmt.Errorf("sysctl %q is not allowed as it conflicts with the OCI %q field", s, "hostname")
}
}
return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
}
return nil
}
func (v *ConfigValidator) intelrdt(config *configs.Config) error {
if config.IntelRdt != nil {
if !intelrdt.IsCATEnabled() && !intelrdt.IsMBAEnabled() {
return errors.New("intelRdt is specified in config, but Intel RDT is not supported or enabled")
}
if !intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema != "" {
return errors.New("intelRdt.l3CacheSchema is specified in config, but Intel RDT/CAT is not enabled")
}
if !intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema != "" {
return errors.New("intelRdt.memBwSchema is specified in config, but Intel RDT/MBA is not enabled")
}
if intelrdt.IsCATEnabled() && config.IntelRdt.L3CacheSchema == "" {
return errors.New("Intel RDT/CAT is enabled and intelRdt is specified in config, but intelRdt.l3CacheSchema is empty")
}
if intelrdt.IsMBAEnabled() && config.IntelRdt.MemBwSchema == "" {
return errors.New("Intel RDT/MBA is enabled and intelRdt is specified in config, but intelRdt.memBwSchema is empty")
}
}
return nil
}
func isHostNetNS(path string) (bool, error) {
const currentProcessNetns = "/proc/self/ns/net"
var st1, st2 unix.Stat_t
if err := unix.Stat(currentProcessNetns, &st1); err != nil {
return false, fmt.Errorf("unable to stat %q: %s", currentProcessNetns, err)
}
if err := unix.Stat(path, &st2); err != nil {
return false, fmt.Errorf("unable to stat %q: %s", path, err)
}
return (st1.Dev == st2.Dev) && (st1.Ino == st2.Ino), nil
}

View File

@ -0,0 +1,41 @@
package libcontainer
import (
"os"
"golang.org/x/sys/unix"
)
// mount initializes the console inside the rootfs mounting with the specified mount label
// and applying the correct ownership of the console.
func mountConsole(slavePath string) error {
oldMask := unix.Umask(0000)
defer unix.Umask(oldMask)
f, err := os.Create("/dev/console")
if err != nil && !os.IsExist(err) {
return err
}
if f != nil {
f.Close()
}
return unix.Mount(slavePath, "/dev/console", "bind", unix.MS_BIND, "")
}
// dupStdio opens the slavePath for the console and dups the fds to the current
// processes stdio, fd 0,1,2.
func dupStdio(slavePath string) error {
fd, err := unix.Open(slavePath, unix.O_RDWR, 0)
if err != nil {
return &os.PathError{
Op: "open",
Path: slavePath,
Err: err,
}
}
for _, i := range []int{0, 1, 2} {
if err := unix.Dup3(fd, i, 0); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,173 @@
// Package libcontainer provides a native Go implementation for creating containers
// with namespaces, cgroups, capabilities, and filesystem access controls.
// It allows you to manage the lifecycle of the container performing additional operations
// after the container is created.
package libcontainer
import (
"os"
"time"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
)
// Status is the status of a container.
type Status int
const (
// Created is the status that denotes the container exists but has not been run yet.
Created Status = iota
// Running is the status that denotes the container exists and is running.
Running
// Pausing is the status that denotes the container exists, it is in the process of being paused.
Pausing
// Paused is the status that denotes the container exists, but all its processes are paused.
Paused
// Stopped is the status that denotes the container does not have a created or running process.
Stopped
)
func (s Status) String() string {
switch s {
case Created:
return "created"
case Running:
return "running"
case Pausing:
return "pausing"
case Paused:
return "paused"
case Stopped:
return "stopped"
default:
return "unknown"
}
}
// BaseState represents the platform agnostic pieces relating to a
// running container's state
type BaseState struct {
// ID is the container ID.
ID string `json:"id"`
// InitProcessPid is the init process id in the parent namespace.
InitProcessPid int `json:"init_process_pid"`
// InitProcessStartTime is the init process start time in clock cycles since boot time.
InitProcessStartTime uint64 `json:"init_process_start"`
// Created is the unix timestamp for the creation time of the container in UTC
Created time.Time `json:"created"`
// Config is the container's configuration.
Config configs.Config `json:"config"`
}
// BaseContainer is a libcontainer container object.
//
// Each container is thread-safe within the same process. Since a container can
// be destroyed by a separate process, any function may return that the container
// was not found. BaseContainer includes methods that are platform agnostic.
type BaseContainer interface {
// Returns the ID of the container
ID() string
// Returns the current status of the container.
//
// errors:
// ContainerNotExists - Container no longer exists,
// Systemerror - System error.
Status() (Status, error)
// State returns the current container's state information.
//
// errors:
// SystemError - System error.
State() (*State, error)
// OCIState returns the current container's state information.
//
// errors:
// SystemError - System error.
OCIState() (*specs.State, error)
// Returns the current config of the container.
Config() configs.Config
// Returns the PIDs inside this container. The PIDs are in the namespace of the calling process.
//
// errors:
// ContainerNotExists - Container no longer exists,
// Systemerror - System error.
//
// Some of the returned PIDs may no longer refer to processes in the Container, unless
// the Container state is PAUSED in which case every PID in the slice is valid.
Processes() ([]int, error)
// Returns statistics for the container.
//
// errors:
// ContainerNotExists - Container no longer exists,
// Systemerror - System error.
Stats() (*Stats, error)
// Set resources of container as configured
//
// We can use this to change resources when containers are running.
//
// errors:
// SystemError - System error.
Set(config configs.Config) error
// Start a process inside the container. Returns error if process fails to
// start. You can track process lifecycle with passed Process structure.
//
// errors:
// ContainerNotExists - Container no longer exists,
// ConfigInvalid - config is invalid,
// ContainerPaused - Container is paused,
// SystemError - System error.
Start(process *Process) (err error)
// Run immediately starts the process inside the container. Returns error if process
// fails to start. It does not block waiting for the exec fifo after start returns but
// opens the fifo after start returns.
//
// errors:
// ContainerNotExists - Container no longer exists,
// ConfigInvalid - config is invalid,
// ContainerPaused - Container is paused,
// SystemError - System error.
Run(process *Process) (err error)
// Destroys the container, if its in a valid state, after killing any
// remaining running processes.
//
// Any event registrations are removed before the container is destroyed.
// No error is returned if the container is already destroyed.
//
// Running containers must first be stopped using Signal(..).
// Paused containers must first be resumed using Resume(..).
//
// errors:
// ContainerNotStopped - Container is still running,
// ContainerPaused - Container is paused,
// SystemError - System error.
Destroy() error
// Signal sends the provided signal code to the container's initial process.
//
// If all is specified the signal is sent to all processes in the container
// including the initial process.
//
// errors:
// SystemError - System error.
Signal(s os.Signal, all bool) error
// Exec signals the container to exec the users process at the end of the init.
//
// errors:
// SystemError - System error.
Exec() error
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
package libcontainer
import criu "github.com/checkpoint-restore/go-criu/v4/rpc"
type CriuPageServerInfo struct {
Address string // IP address of CRIU page server
Port int32 // port number of CRIU page server
}
type VethPairName struct {
ContainerInterfaceName string
HostInterfaceName string
}
type CriuOpts struct {
ImagesDirectory string // directory for storing image files
WorkDirectory string // directory to cd and write logs/pidfiles/stats to
ParentImage string // directory for storing parent image files in pre-dump and dump
LeaveRunning bool // leave container in running state after checkpoint
TcpEstablished bool // checkpoint/restore established TCP connections
ExternalUnixConnections bool // allow external unix connections
ShellJob bool // allow to dump and restore shell jobs
FileLocks bool // handle file locks, for safety
PreDump bool // call criu predump to perform iterative checkpoint
PageServer CriuPageServerInfo // allow to dump to criu page server
VethPairs []VethPairName // pass the veth to criu when restore
ManageCgroupsMode criu.CriuCgMode // dump or restore cgroup mode
EmptyNs uint32 // don't c/r properties for namespace from this mask
AutoDedup bool // auto deduplication for incremental dumps
LazyPages bool // restore memory pages lazily using userfaultfd
StatusFd int // fd for feedback when lazy server is ready
}

View File

@ -0,0 +1,70 @@
package libcontainer
import "io"
// ErrorCode is the API error code type.
type ErrorCode int
// API error codes.
const (
// Factory errors
IdInUse ErrorCode = iota
InvalidIdFormat
// Container errors
ContainerNotExists
ContainerPaused
ContainerNotStopped
ContainerNotRunning
ContainerNotPaused
// Process errors
NoProcessOps
// Common errors
ConfigInvalid
ConsoleExists
SystemError
)
func (c ErrorCode) String() string {
switch c {
case IdInUse:
return "Id already in use"
case InvalidIdFormat:
return "Invalid format"
case ContainerPaused:
return "Container paused"
case ConfigInvalid:
return "Invalid configuration"
case SystemError:
return "System error"
case ContainerNotExists:
return "Container does not exist"
case ContainerNotStopped:
return "Container is not stopped"
case ContainerNotRunning:
return "Container is not running"
case ConsoleExists:
return "Console exists for process"
case ContainerNotPaused:
return "Container is not paused"
case NoProcessOps:
return "No process operations"
default:
return "Unknown error"
}
}
// Error is the API error type.
type Error interface {
error
// Returns an error if it failed to write the detail of the Error to w.
// The detail of the Error may include the error message and a
// representation of the stack trace.
Detail(w io.Writer) error
// Returns the error code for this error.
Code() ErrorCode
}

View File

@ -0,0 +1,44 @@
package libcontainer
import (
"github.com/opencontainers/runc/libcontainer/configs"
)
type Factory interface {
// Creates a new container with the given id and starts the initial process inside it.
// id must be a string containing only letters, digits and underscores and must contain
// between 1 and 1024 characters, inclusive.
//
// The id must not already be in use by an existing container. Containers created using
// a factory with the same path (and filesystem) must have distinct ids.
//
// Returns the new container with a running process.
//
// errors:
// IdInUse - id is already in use by a container
// InvalidIdFormat - id has incorrect format
// ConfigInvalid - config is invalid
// Systemerror - System error
//
// On error, any partially created container parts are cleaned up (the operation is atomic).
Create(id string, config *configs.Config) (Container, error)
// Load takes an ID for an existing container and returns the container information
// from the state. This presents a read only view of the container.
//
// errors:
// Path does not exist
// System error
Load(id string) (Container, error)
// StartInitialization is an internal API to libcontainer used during the reexec of the
// container.
//
// Errors:
// Pipe connection error
// System error
StartInitialization() error
// Type returns info string about factory type (e.g. lxc, libcontainer...)
Type() string
}

View File

@ -0,0 +1,443 @@
// +build linux
package libcontainer
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"runtime/debug"
"strconv"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/moby/sys/mountinfo"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/configs/validate"
"github.com/opencontainers/runc/libcontainer/intelrdt"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
const (
stateFilename = "state.json"
execFifoFilename = "exec.fifo"
)
var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)
// InitArgs returns an options func to configure a LinuxFactory with the
// provided init binary path and arguments.
func InitArgs(args ...string) func(*LinuxFactory) error {
return func(l *LinuxFactory) (err error) {
if len(args) > 0 {
// Resolve relative paths to ensure that its available
// after directory changes.
if args[0], err = filepath.Abs(args[0]); err != nil {
return newGenericError(err, ConfigInvalid)
}
}
l.InitArgs = args
return nil
}
}
func getUnifiedPath(paths map[string]string) string {
path := ""
for k, v := range paths {
if path == "" {
path = v
} else if v != path {
panic(errors.Errorf("expected %q path to be unified path %q, got %q", k, path, v))
}
}
// can be empty
if path != "" {
if filepath.Clean(path) != path || !filepath.IsAbs(path) {
panic(errors.Errorf("invalid dir path %q", path))
}
}
return path
}
func systemdCgroupV2(l *LinuxFactory, rootless bool) error {
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return systemd.NewUnifiedManager(config, getUnifiedPath(paths), rootless)
}
return nil
}
// SystemdCgroups is an options func to configure a LinuxFactory to return
// containers that use systemd to create and manage cgroups.
func SystemdCgroups(l *LinuxFactory) error {
if !systemd.IsRunningSystemd() {
return fmt.Errorf("systemd not running on this host, can't use systemd as cgroups manager")
}
if cgroups.IsCgroup2UnifiedMode() {
return systemdCgroupV2(l, false)
}
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return systemd.NewLegacyManager(config, paths)
}
return nil
}
// RootlessSystemdCgroups is rootless version of SystemdCgroups.
func RootlessSystemdCgroups(l *LinuxFactory) error {
if !systemd.IsRunningSystemd() {
return fmt.Errorf("systemd not running on this host, can't use systemd as cgroups manager")
}
if !cgroups.IsCgroup2UnifiedMode() {
return fmt.Errorf("cgroup v2 not enabled on this host, can't use systemd (rootless) as cgroups manager")
}
return systemdCgroupV2(l, true)
}
func cgroupfs2(l *LinuxFactory, rootless bool) error {
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
m, err := fs2.NewManager(config, getUnifiedPath(paths), rootless)
if err != nil {
panic(err)
}
return m
}
return nil
}
func cgroupfs(l *LinuxFactory, rootless bool) error {
if cgroups.IsCgroup2UnifiedMode() {
return cgroupfs2(l, rootless)
}
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
return fs.NewManager(config, paths, rootless)
}
return nil
}
// Cgroupfs is an options func to configure a LinuxFactory to return containers
// that use the native cgroups filesystem implementation to create and manage
// cgroups.
func Cgroupfs(l *LinuxFactory) error {
return cgroupfs(l, false)
}
// RootlessCgroupfs is an options func to configure a LinuxFactory to return
// containers that use the native cgroups filesystem implementation to create
// and manage cgroups. The difference between RootlessCgroupfs and Cgroupfs is
// that RootlessCgroupfs can transparently handle permission errors that occur
// during rootless container (including euid=0 in userns) setup (while still allowing cgroup usage if
// they've been set up properly).
func RootlessCgroupfs(l *LinuxFactory) error {
return cgroupfs(l, true)
}
// IntelRdtfs is an options func to configure a LinuxFactory to return
// containers that use the Intel RDT "resource control" filesystem to
// create and manage Intel RDT resources (e.g., L3 cache, memory bandwidth).
func IntelRdtFs(l *LinuxFactory) error {
if !intelrdt.IsCATEnabled() && !intelrdt.IsMBAEnabled() {
l.NewIntelRdtManager = nil
} else {
l.NewIntelRdtManager = func(config *configs.Config, id string, path string) intelrdt.Manager {
return intelrdt.NewManager(config, id, path)
}
}
return nil
}
// TmpfsRoot is an option func to mount LinuxFactory.Root to tmpfs.
func TmpfsRoot(l *LinuxFactory) error {
mounted, err := mountinfo.Mounted(l.Root)
if err != nil {
return err
}
if !mounted {
if err := unix.Mount("tmpfs", l.Root, "tmpfs", 0, ""); err != nil {
return err
}
}
return nil
}
// CriuPath returns an option func to configure a LinuxFactory with the
// provided criupath
func CriuPath(criupath string) func(*LinuxFactory) error {
return func(l *LinuxFactory) error {
l.CriuPath = criupath
return nil
}
}
// New returns a linux based container factory based in the root directory and
// configures the factory with the provided option funcs.
func New(root string, options ...func(*LinuxFactory) error) (Factory, error) {
if root != "" {
if err := os.MkdirAll(root, 0700); err != nil {
return nil, newGenericError(err, SystemError)
}
}
l := &LinuxFactory{
Root: root,
InitPath: "/proc/self/exe",
InitArgs: []string{os.Args[0], "init"},
Validator: validate.New(),
CriuPath: "criu",
}
Cgroupfs(l)
for _, opt := range options {
if opt == nil {
continue
}
if err := opt(l); err != nil {
return nil, err
}
}
return l, nil
}
// LinuxFactory implements the default factory interface for linux based systems.
type LinuxFactory struct {
// Root directory for the factory to store state.
Root string
// InitPath is the path for calling the init responsibilities for spawning
// a container.
InitPath string
// InitArgs are arguments for calling the init responsibilities for spawning
// a container.
InitArgs []string
// CriuPath is the path to the criu binary used for checkpoint and restore of
// containers.
CriuPath string
// New{u,g}uidmapPath is the path to the binaries used for mapping with
// rootless containers.
NewuidmapPath string
NewgidmapPath string
// Validator provides validation to container configurations.
Validator validate.Validator
// NewCgroupsManager returns an initialized cgroups manager for a single container.
NewCgroupsManager func(config *configs.Cgroup, paths map[string]string) cgroups.Manager
// NewIntelRdtManager returns an initialized Intel RDT manager for a single container.
NewIntelRdtManager func(config *configs.Config, id string, path string) intelrdt.Manager
}
func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) {
if l.Root == "" {
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
}
if err := l.validateID(id); err != nil {
return nil, err
}
if err := l.Validator.Validate(config); err != nil {
return nil, newGenericError(err, ConfigInvalid)
}
containerRoot, err := securejoin.SecureJoin(l.Root, id)
if err != nil {
return nil, err
}
if _, err := os.Stat(containerRoot); err == nil {
return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse)
} else if !os.IsNotExist(err) {
return nil, newGenericError(err, SystemError)
}
if err := os.MkdirAll(containerRoot, 0711); err != nil {
return nil, newGenericError(err, SystemError)
}
if err := os.Chown(containerRoot, unix.Geteuid(), unix.Getegid()); err != nil {
return nil, newGenericError(err, SystemError)
}
c := &linuxContainer{
id: id,
root: containerRoot,
config: config,
initPath: l.InitPath,
initArgs: l.InitArgs,
criuPath: l.CriuPath,
newuidmapPath: l.NewuidmapPath,
newgidmapPath: l.NewgidmapPath,
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
}
if l.NewIntelRdtManager != nil {
c.intelRdtManager = l.NewIntelRdtManager(config, id, "")
}
c.state = &stoppedState{c: c}
return c, nil
}
func (l *LinuxFactory) Load(id string) (Container, error) {
if l.Root == "" {
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
}
//when load, we need to check id is valid or not.
if err := l.validateID(id); err != nil {
return nil, err
}
containerRoot, err := securejoin.SecureJoin(l.Root, id)
if err != nil {
return nil, err
}
state, err := l.loadState(containerRoot, id)
if err != nil {
return nil, err
}
r := &nonChildProcess{
processPid: state.InitProcessPid,
processStartTime: state.InitProcessStartTime,
fds: state.ExternalDescriptors,
}
c := &linuxContainer{
initProcess: r,
initProcessStartTime: state.InitProcessStartTime,
id: id,
config: &state.Config,
initPath: l.InitPath,
initArgs: l.InitArgs,
criuPath: l.CriuPath,
newuidmapPath: l.NewuidmapPath,
newgidmapPath: l.NewgidmapPath,
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
root: containerRoot,
created: state.Created,
}
if l.NewIntelRdtManager != nil {
c.intelRdtManager = l.NewIntelRdtManager(&state.Config, id, state.IntelRdtPath)
}
c.state = &loadedState{c: c}
if err := c.refreshState(); err != nil {
return nil, err
}
return c, nil
}
func (l *LinuxFactory) Type() string {
return "libcontainer"
}
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
// This is a low level implementation detail of the reexec and should not be consumed externally
func (l *LinuxFactory) StartInitialization() (err error) {
// Get the INITPIPE.
envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
pipefd, err := strconv.Atoi(envInitPipe)
if err != nil {
return fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE=%s to int: %s", envInitPipe, err)
}
pipe := os.NewFile(uintptr(pipefd), "pipe")
defer pipe.Close()
// Only init processes have FIFOFD.
fifofd := -1
envInitType := os.Getenv("_LIBCONTAINER_INITTYPE")
it := initType(envInitType)
if it == initStandard {
envFifoFd := os.Getenv("_LIBCONTAINER_FIFOFD")
if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD=%s to int: %s", envFifoFd, err)
}
}
var consoleSocket *os.File
if envConsole := os.Getenv("_LIBCONTAINER_CONSOLE"); envConsole != "" {
console, err := strconv.Atoi(envConsole)
if err != nil {
return fmt.Errorf("unable to convert _LIBCONTAINER_CONSOLE=%s to int: %s", envConsole, err)
}
consoleSocket = os.NewFile(uintptr(console), "console-socket")
defer consoleSocket.Close()
}
// clear the current process's environment to clean any libcontainer
// specific env vars.
os.Clearenv()
defer func() {
// We have an error during the initialization of the container's init,
// send it back to the parent process in the form of an initError.
if werr := utils.WriteJSON(pipe, syncT{procError}); werr != nil {
fmt.Fprintln(os.Stderr, err)
return
}
if werr := utils.WriteJSON(pipe, newSystemError(err)); werr != nil {
fmt.Fprintln(os.Stderr, err)
return
}
}()
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic from initialization: %v, %v", e, string(debug.Stack()))
}
}()
i, err := newContainerInit(it, pipe, consoleSocket, fifofd)
if err != nil {
return err
}
// If Init succeeds, syscall.Exec will not return, hence none of the defers will be called.
return i.Init()
}
func (l *LinuxFactory) loadState(root, id string) (*State, error) {
stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
if err != nil {
return nil, err
}
f, err := os.Open(stateFilePath)
if err != nil {
if os.IsNotExist(err) {
return nil, newGenericError(fmt.Errorf("container %q does not exist", id), ContainerNotExists)
}
return nil, newGenericError(err, SystemError)
}
defer f.Close()
var state *State
if err := json.NewDecoder(f).Decode(&state); err != nil {
return nil, newGenericError(err, SystemError)
}
return state, nil
}
func (l *LinuxFactory) validateID(id string) error {
if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
}
return nil
}
// NewuidmapPath returns an option func to configure a LinuxFactory with the
// provided ..
func NewuidmapPath(newuidmapPath string) func(*LinuxFactory) error {
return func(l *LinuxFactory) error {
l.NewuidmapPath = newuidmapPath
return nil
}
}
// NewgidmapPath returns an option func to configure a LinuxFactory with the
// provided ..
func NewgidmapPath(newgidmapPath string) func(*LinuxFactory) error {
return func(l *LinuxFactory) error {
l.NewgidmapPath = newgidmapPath
return nil
}
}

View File

@ -0,0 +1,92 @@
package libcontainer
import (
"fmt"
"io"
"text/template"
"time"
"github.com/opencontainers/runc/libcontainer/stacktrace"
)
var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}}
Code: {{.ECode}}
{{if .Message }}
Message: {{.Message}}
{{end}}
Frames:{{range $i, $frame := .Stack.Frames}}
---
{{$i}}: {{$frame.Function}}
Package: {{$frame.Package}}
File: {{$frame.File}}@{{$frame.Line}}{{end}}
`))
func newGenericError(err error, c ErrorCode) Error {
if le, ok := err.(Error); ok {
return le
}
gerr := &genericError{
Timestamp: time.Now(),
Err: err,
ECode: c,
Stack: stacktrace.Capture(1),
}
if err != nil {
gerr.Message = err.Error()
}
return gerr
}
func newSystemError(err error) Error {
return createSystemError(err, "")
}
func newSystemErrorWithCausef(err error, cause string, v ...interface{}) Error {
return createSystemError(err, fmt.Sprintf(cause, v...))
}
func newSystemErrorWithCause(err error, cause string) Error {
return createSystemError(err, cause)
}
// createSystemError creates the specified error with the correct number of
// stack frames skipped. This is only to be called by the other functions for
// formatting the error.
func createSystemError(err error, cause string) Error {
gerr := &genericError{
Timestamp: time.Now(),
Err: err,
ECode: SystemError,
Cause: cause,
Stack: stacktrace.Capture(2),
}
if err != nil {
gerr.Message = err.Error()
}
return gerr
}
type genericError struct {
Timestamp time.Time
ECode ErrorCode
Err error `json:"-"`
Cause string
Message string
Stack stacktrace.Stacktrace
}
func (e *genericError) Error() string {
if e.Cause == "" {
return e.Message
}
frame := e.Stack.Frames[0]
return fmt.Sprintf("%s:%d: %s caused: %s", frame.File, frame.Line, e.Cause, e.Message)
}
func (e *genericError) Code() ErrorCode {
return e.ECode
}
func (e *genericError) Detail(w io.Writer) error {
return errorTemplate.Execute(w, e)
}

View File

@ -0,0 +1,544 @@
// +build linux
package libcontainer
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"strings"
"unsafe"
"github.com/containerd/console"
"github.com/opencontainers/runc/libcontainer/capabilities"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
)
type initType string
const (
initSetns initType = "setns"
initStandard initType = "standard"
)
type pid struct {
Pid int `json:"pid"`
PidFirstChild int `json:"pid_first"`
}
// network is an internal struct used to setup container networks.
type network struct {
configs.Network
// TempVethPeerName is a unique temporary veth peer name that was placed into
// the container's namespace.
TempVethPeerName string `json:"temp_veth_peer_name"`
}
// initConfig is used for transferring parameters from Exec() to Init()
type initConfig struct {
Args []string `json:"args"`
Env []string `json:"env"`
Cwd string `json:"cwd"`
Capabilities *configs.Capabilities `json:"capabilities"`
ProcessLabel string `json:"process_label"`
AppArmorProfile string `json:"apparmor_profile"`
NoNewPrivileges bool `json:"no_new_privileges"`
User string `json:"user"`
AdditionalGroups []string `json:"additional_groups"`
Config *configs.Config `json:"config"`
Networks []*network `json:"network"`
PassedFilesCount int `json:"passed_files_count"`
ContainerId string `json:"containerid"`
Rlimits []configs.Rlimit `json:"rlimits"`
CreateConsole bool `json:"create_console"`
ConsoleWidth uint16 `json:"console_width"`
ConsoleHeight uint16 `json:"console_height"`
RootlessEUID bool `json:"rootless_euid,omitempty"`
RootlessCgroups bool `json:"rootless_cgroups,omitempty"`
SpecState *specs.State `json:"spec_state,omitempty"`
}
type initer interface {
Init() error
}
func newContainerInit(t initType, pipe *os.File, consoleSocket *os.File, fifoFd int) (initer, error) {
var config *initConfig
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
return nil, err
}
if err := populateProcessEnvironment(config.Env); err != nil {
return nil, err
}
switch t {
case initSetns:
return &linuxSetnsInit{
pipe: pipe,
consoleSocket: consoleSocket,
config: config,
}, nil
case initStandard:
return &linuxStandardInit{
pipe: pipe,
consoleSocket: consoleSocket,
parentPid: unix.Getppid(),
config: config,
fifoFd: fifoFd,
}, nil
}
return nil, fmt.Errorf("unknown init type %q", t)
}
// populateProcessEnvironment loads the provided environment variables into the
// current processes's environment.
func populateProcessEnvironment(env []string) error {
for _, pair := range env {
p := strings.SplitN(pair, "=", 2)
if len(p) < 2 {
return fmt.Errorf("invalid environment '%v'", pair)
}
if err := os.Setenv(p[0], p[1]); err != nil {
return err
}
}
return nil
}
// finalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
// Ensure that all unwanted fds we may have accidentally
// inherited are marked close-on-exec so they stay out of the
// container
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
return errors.Wrap(err, "close exec fds")
}
caps := &configs.Capabilities{}
if config.Capabilities != nil {
caps = config.Capabilities
} else if config.Config.Capabilities != nil {
caps = config.Config.Capabilities
}
w, err := capabilities.New(caps)
if err != nil {
return err
}
// drop capabilities in bounding set before changing user
if err := w.ApplyBoundingSet(); err != nil {
return errors.Wrap(err, "apply bounding set")
}
// preserve existing capabilities while we change users
if err := system.SetKeepCaps(); err != nil {
return errors.Wrap(err, "set keep caps")
}
if err := setupUser(config); err != nil {
return errors.Wrap(err, "setup user")
}
// Change working directory AFTER the user has been set up.
// Otherwise, if the cwd is also a volume that's been chowned to the container user (and not the user running runc),
// this command will EPERM.
if config.Cwd != "" {
if err := unix.Chdir(config.Cwd); err != nil {
return fmt.Errorf("chdir to cwd (%q) set in config.json failed: %v", config.Cwd, err)
}
}
if err := system.ClearKeepCaps(); err != nil {
return errors.Wrap(err, "clear keep caps")
}
if err := w.ApplyCaps(); err != nil {
return errors.Wrap(err, "apply caps")
}
return nil
}
// setupConsole sets up the console from inside the container, and sends the
// master pty fd to the config.Pipe (using cmsg). This is done to ensure that
// consoles are scoped to a container properly (see runc#814 and the many
// issues related to that). This has to be run *after* we've pivoted to the new
// rootfs (and the users' configuration is entirely set up).
func setupConsole(socket *os.File, config *initConfig, mount bool) error {
defer socket.Close()
// At this point, /dev/ptmx points to something that we would expect. We
// used to change the owner of the slave path, but since the /dev/pts mount
// can have gid=X set (at the users' option). So touching the owner of the
// slave PTY is not necessary, as the kernel will handle that for us. Note
// however, that setupUser (specifically fixStdioPermissions) *will* change
// the UID owner of the console to be the user the process will run as (so
// they can actually control their console).
pty, slavePath, err := console.NewPty()
if err != nil {
return err
}
// After we return from here, we don't need the console anymore.
defer pty.Close()
if config.ConsoleHeight != 0 && config.ConsoleWidth != 0 {
err = pty.Resize(console.WinSize{
Height: config.ConsoleHeight,
Width: config.ConsoleWidth,
})
if err != nil {
return err
}
}
// Mount the console inside our rootfs.
if mount {
if err := mountConsole(slavePath); err != nil {
return err
}
}
// While we can access console.master, using the API is a good idea.
if err := utils.SendFd(socket, pty.Name(), pty.Fd()); err != nil {
return err
}
// Now, dup over all the things.
return dupStdio(slavePath)
}
// syncParentReady sends to the given pipe a JSON payload which indicates that
// the init is ready to Exec the child process. It then waits for the parent to
// indicate that it is cleared to Exec.
func syncParentReady(pipe io.ReadWriter) error {
// Tell parent.
if err := writeSync(pipe, procReady); err != nil {
return err
}
// Wait for parent to give the all-clear.
return readSync(pipe, procRun)
}
// syncParentHooks sends to the given pipe a JSON payload which indicates that
// the parent should execute pre-start hooks. It then waits for the parent to
// indicate that it is cleared to resume.
func syncParentHooks(pipe io.ReadWriter) error {
// Tell parent.
if err := writeSync(pipe, procHooks); err != nil {
return err
}
// Wait for parent to give the all-clear.
return readSync(pipe, procResume)
}
// setupUser changes the groups, gid, and uid for the user inside the container
func setupUser(config *initConfig) error {
// Set up defaults.
defaultExecUser := user.ExecUser{
Uid: 0,
Gid: 0,
Home: "/",
}
passwdPath, err := user.GetPasswdPath()
if err != nil {
return err
}
groupPath, err := user.GetGroupPath()
if err != nil {
return err
}
execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath)
if err != nil {
return err
}
var addGroups []int
if len(config.AdditionalGroups) > 0 {
addGroups, err = user.GetAdditionalGroupsPath(config.AdditionalGroups, groupPath)
if err != nil {
return err
}
}
// Rather than just erroring out later in setuid(2) and setgid(2), check
// that the user is mapped here.
if _, err := config.Config.HostUID(execUser.Uid); err != nil {
return errors.New("cannot set uid to unmapped user in user namespace")
}
if _, err := config.Config.HostGID(execUser.Gid); err != nil {
return errors.New("cannot set gid to unmapped user in user namespace")
}
if config.RootlessEUID {
// We cannot set any additional groups in a rootless container and thus
// we bail if the user asked us to do so. TODO: We currently can't do
// this check earlier, but if libcontainer.Process.User was typesafe
// this might work.
if len(addGroups) > 0 {
return errors.New("cannot set any additional groups in a rootless container")
}
}
// Before we change to the container's user make sure that the processes
// STDIO is correctly owned by the user that we are switching to.
if err := fixStdioPermissions(config, execUser); err != nil {
return err
}
setgroups, err := ioutil.ReadFile("/proc/self/setgroups")
if err != nil && !os.IsNotExist(err) {
return err
}
// This isn't allowed in an unprivileged user namespace since Linux 3.19.
// There's nothing we can do about /etc/group entries, so we silently
// ignore setting groups here (since the user didn't explicitly ask us to
// set the group).
allowSupGroups := !config.RootlessEUID && string(bytes.TrimSpace(setgroups)) != "deny"
if allowSupGroups {
suppGroups := append(execUser.Sgids, addGroups...)
if err := unix.Setgroups(suppGroups); err != nil {
return err
}
}
if err := system.Setgid(execUser.Gid); err != nil {
return err
}
if err := system.Setuid(execUser.Uid); err != nil {
return err
}
// if we didn't get HOME already, set it based on the user's HOME
if envHome := os.Getenv("HOME"); envHome == "" {
if err := os.Setenv("HOME", execUser.Home); err != nil {
return err
}
}
return nil
}
// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user.
// The ownership needs to match because it is created outside of the container and needs to be
// localized.
func fixStdioPermissions(config *initConfig, u *user.ExecUser) error {
var null unix.Stat_t
if err := unix.Stat("/dev/null", &null); err != nil {
return err
}
for _, fd := range []uintptr{
os.Stdin.Fd(),
os.Stderr.Fd(),
os.Stdout.Fd(),
} {
var s unix.Stat_t
if err := unix.Fstat(int(fd), &s); err != nil {
return err
}
// Skip chown of /dev/null if it was used as one of the STDIO fds.
if s.Rdev == null.Rdev {
continue
}
// We only change the uid owner (as it is possible for the mount to
// prefer a different gid, and there's no reason for us to change it).
// The reason why we don't just leave the default uid=X mount setup is
// that users expect to be able to actually use their console. Without
// this code, you couldn't effectively run as a non-root user inside a
// container and also have a console set up.
if err := unix.Fchown(int(fd), u.Uid, int(s.Gid)); err != nil {
// If we've hit an EINVAL then s.Gid isn't mapped in the user
// namespace. If we've hit an EPERM then the inode's current owner
// is not mapped in our user namespace (in particular,
// privileged_wrt_inode_uidgid() has failed). In either case, we
// are in a configuration where it's better for us to just not
// touch the stdio rather than bail at this point.
if err == unix.EINVAL || err == unix.EPERM {
continue
}
return err
}
}
return nil
}
// setupNetwork sets up and initializes any network interface inside the container.
func setupNetwork(config *initConfig) error {
for _, config := range config.Networks {
strategy, err := getStrategy(config.Type)
if err != nil {
return err
}
if err := strategy.initialize(config); err != nil {
return err
}
}
return nil
}
func setupRoute(config *configs.Config) error {
for _, config := range config.Routes {
_, dst, err := net.ParseCIDR(config.Destination)
if err != nil {
return err
}
src := net.ParseIP(config.Source)
if src == nil {
return fmt.Errorf("Invalid source for route: %s", config.Source)
}
gw := net.ParseIP(config.Gateway)
if gw == nil {
return fmt.Errorf("Invalid gateway for route: %s", config.Gateway)
}
l, err := netlink.LinkByName(config.InterfaceName)
if err != nil {
return err
}
route := &netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
Dst: dst,
Src: src,
Gw: gw,
LinkIndex: l.Attrs().Index,
}
if err := netlink.RouteAdd(route); err != nil {
return err
}
}
return nil
}
func setupRlimits(limits []configs.Rlimit, pid int) error {
for _, rlimit := range limits {
if err := system.Prlimit(pid, rlimit.Type, unix.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}); err != nil {
return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
}
}
return nil
}
const _P_PID = 1
//nolint:structcheck,unused
type siginfo struct {
si_signo int32
si_errno int32
si_code int32
// below here is a union; si_pid is the only field we use
si_pid int32
// Pad to 128 bytes as detailed in blockUntilWaitable
pad [96]byte
}
// isWaitable returns true if the process has exited false otherwise.
// Its based off blockUntilWaitable in src/os/wait_waitid.go
func isWaitable(pid int) (bool, error) {
si := &siginfo{}
_, _, e := unix.Syscall6(unix.SYS_WAITID, _P_PID, uintptr(pid), uintptr(unsafe.Pointer(si)), unix.WEXITED|unix.WNOWAIT|unix.WNOHANG, 0, 0)
if e != 0 {
return false, os.NewSyscallError("waitid", e)
}
return si.si_pid != 0, nil
}
// isNoChildren returns true if err represents a unix.ECHILD (formerly syscall.ECHILD) false otherwise
func isNoChildren(err error) bool {
switch err := err.(type) {
case unix.Errno:
if err == unix.ECHILD {
return true
}
case *os.SyscallError:
if err.Err == unix.ECHILD {
return true
}
}
return false
}
// signalAllProcesses freezes then iterates over all the processes inside the
// manager's cgroups sending the signal s to them.
// If s is SIGKILL then it will wait for each process to exit.
// For all other signals it will check if the process is ready to report its
// exit status and only if it is will a wait be performed.
func signalAllProcesses(m cgroups.Manager, s os.Signal) error {
var procs []*os.Process
if err := m.Freeze(configs.Frozen); err != nil {
logrus.Warn(err)
}
pids, err := m.GetAllPids()
if err != nil {
if err := m.Freeze(configs.Thawed); err != nil {
logrus.Warn(err)
}
return err
}
for _, pid := range pids {
p, err := os.FindProcess(pid)
if err != nil {
logrus.Warn(err)
continue
}
procs = append(procs, p)
if err := p.Signal(s); err != nil {
logrus.Warn(err)
}
}
if err := m.Freeze(configs.Thawed); err != nil {
logrus.Warn(err)
}
subreaper, err := system.GetSubreaper()
if err != nil {
// The error here means that PR_GET_CHILD_SUBREAPER is not
// supported because this code might run on a kernel older
// than 3.4. We don't want to throw an error in that case,
// and we simplify things, considering there is no subreaper
// set.
subreaper = 0
}
for _, p := range procs {
if s != unix.SIGKILL {
if ok, err := isWaitable(p.Pid); err != nil {
if !isNoChildren(err) {
logrus.Warn("signalAllProcesses: ", p.Pid, err)
}
continue
} else if !ok {
// Not ready to report so don't wait
continue
}
}
// In case a subreaper has been setup, this code must not
// wait for the process. Otherwise, we cannot be sure the
// current process will be reaped by the subreaper, while
// the subreaper might be waiting for this process in order
// to retrieve its exit code.
if subreaper == 0 {
if _, err := p.Wait(); err != nil {
if !isNoChildren(err) {
logrus.Warn("wait: ", err)
}
}
}
}
return nil
}

View File

@ -0,0 +1,25 @@
package intelrdt
var (
cmtEnabled bool
)
// Check if Intel RDT/CMT is enabled.
func IsCMTEnabled() bool {
featuresInit()
return cmtEnabled
}
func getCMTNumaNodeStats(numaPath string) (*CMTNumaNodeStats, error) {
stats := &CMTNumaNodeStats{}
if enabledMonFeatures.llcOccupancy {
llcOccupancy, err := getIntelRdtParamUint(numaPath, "llc_occupancy")
if err != nil {
return nil, err
}
stats.LLCOccupancy = llcOccupancy
}
return stats, nil
}

View File

@ -0,0 +1,816 @@
// +build linux
package intelrdt
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"github.com/moby/sys/mountinfo"
"github.com/opencontainers/runc/libcontainer/configs"
)
/*
* About Intel RDT features:
* Intel platforms with new Xeon CPU support Resource Director Technology (RDT).
* Cache Allocation Technology (CAT) and Memory Bandwidth Allocation (MBA) are
* two sub-features of RDT.
*
* Cache Allocation Technology (CAT) provides a way for the software to restrict
* cache allocation to a defined 'subset' of L3 cache which may be overlapping
* with other 'subsets'. The different subsets are identified by class of
* service (CLOS) and each CLOS has a capacity bitmask (CBM).
*
* Memory Bandwidth Allocation (MBA) provides indirect and approximate throttle
* over memory bandwidth for the software. A user controls the resource by
* indicating the percentage of maximum memory bandwidth or memory bandwidth
* limit in MBps unit if MBA Software Controller is enabled.
*
* More details about Intel RDT CAT and MBA can be found in the section 17.18
* of Intel Software Developer Manual:
* https://software.intel.com/en-us/articles/intel-sdm
*
* About Intel RDT kernel interface:
* In Linux 4.10 kernel or newer, the interface is defined and exposed via
* "resource control" filesystem, which is a "cgroup-like" interface.
*
* Comparing with cgroups, it has similar process management lifecycle and
* interfaces in a container. But unlike cgroups' hierarchy, it has single level
* filesystem layout.
*
* CAT and MBA features are introduced in Linux 4.10 and 4.12 kernel via
* "resource control" filesystem.
*
* Intel RDT "resource control" filesystem hierarchy:
* mount -t resctrl resctrl /sys/fs/resctrl
* tree /sys/fs/resctrl
* /sys/fs/resctrl/
* |-- info
* | |-- L3
* | | |-- cbm_mask
* | | |-- min_cbm_bits
* | | |-- num_closids
* | |-- L3_MON
* | | |-- max_threshold_occupancy
* | | |-- mon_features
* | | |-- num_rmids
* | |-- MB
* | |-- bandwidth_gran
* | |-- delay_linear
* | |-- min_bandwidth
* | |-- num_closids
* |-- ...
* |-- schemata
* |-- tasks
* |-- <container_id>
* |-- ...
* |-- schemata
* |-- tasks
*
* For runc, we can make use of `tasks` and `schemata` configuration for L3
* cache and memory bandwidth resources constraints.
*
* The file `tasks` has a list of tasks that belongs to this group (e.g.,
* <container_id>" group). Tasks can be added to a group by writing the task ID
* to the "tasks" file (which will automatically remove them from the previous
* group to which they belonged). New tasks created by fork(2) and clone(2) are
* added to the same group as their parent.
*
* The file `schemata` has a list of all the resources available to this group.
* Each resource (L3 cache, memory bandwidth) has its own line and format.
*
* L3 cache schema:
* It has allocation bitmasks/values for L3 cache on each socket, which
* contains L3 cache id and capacity bitmask (CBM).
* Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
* For example, on a two-socket machine, the schema line could be "L3:0=ff;1=c0"
* which means L3 cache id 0's CBM is 0xff, and L3 cache id 1's CBM is 0xc0.
*
* The valid L3 cache CBM is a *contiguous bits set* and number of bits that can
* be set is less than the max bit. The max bits in the CBM is varied among
* supported Intel CPU models. Kernel will check if it is valid when writing.
* e.g., default value 0xfffff in root indicates the max bits of CBM is 20
* bits, which mapping to entire L3 cache capacity. Some valid CBM values to
* set in a group: 0xf, 0xf0, 0x3ff, 0x1f00 and etc.
*
* Memory bandwidth schema:
* It has allocation values for memory bandwidth on each socket, which contains
* L3 cache id and memory bandwidth.
* Format: "MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;..."
* For example, on a two-socket machine, the schema line could be "MB:0=20;1=70"
*
* The minimum bandwidth percentage value for each CPU model is predefined and
* can be looked up through "info/MB/min_bandwidth". The bandwidth granularity
* that is allocated is also dependent on the CPU model and can be looked up at
* "info/MB/bandwidth_gran". The available bandwidth control steps are:
* min_bw + N * bw_gran. Intermediate values are rounded to the next control
* step available on the hardware.
*
* If MBA Software Controller is enabled through mount option "-o mba_MBps":
* mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl
* We could specify memory bandwidth in "MBps" (Mega Bytes per second) unit
* instead of "percentages". The kernel underneath would use a software feedback
* mechanism or a "Software Controller" which reads the actual bandwidth using
* MBM counters and adjust the memory bandwidth percentages to ensure:
* "actual memory bandwidth < user specified memory bandwidth".
*
* For example, on a two-socket machine, the schema line could be
* "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on socket 0
* and 7000 MBps memory bandwidth limit on socket 1.
*
* For more information about Intel RDT kernel interface:
* https://www.kernel.org/doc/Documentation/x86/intel_rdt_ui.txt
*
* An example for runc:
* Consider a two-socket machine with two L3 caches where the default CBM is
* 0x7ff and the max CBM length is 11 bits, and minimum memory bandwidth of 10%
* with a memory bandwidth granularity of 10%.
*
* Tasks inside the container only have access to the "upper" 7/11 of L3 cache
* on socket 0 and the "lower" 5/11 L3 cache on socket 1, and may use a
* maximum memory bandwidth of 20% on socket 0 and 70% on socket 1.
*
* "linux": {
* "intelRdt": {
* "l3CacheSchema": "L3:0=7f0;1=1f",
* "memBwSchema": "MB:0=20;1=70"
* }
* }
*/
type Manager interface {
// Applies Intel RDT configuration to the process with the specified pid
Apply(pid int) error
// Returns statistics for Intel RDT
GetStats() (*Stats, error)
// Destroys the Intel RDT 'container_id' group
Destroy() error
// Returns Intel RDT path to save in a state file and to be able to
// restore the object later
GetPath() string
// Set Intel RDT "resource control" filesystem as configured.
Set(container *configs.Config) error
}
// This implements interface Manager
type intelRdtManager struct {
mu sync.Mutex
config *configs.Config
id string
path string
}
func NewManager(config *configs.Config, id string, path string) Manager {
return &intelRdtManager{
config: config,
id: id,
path: path,
}
}
const (
IntelRdtTasks = "tasks"
)
var (
// The absolute root path of the Intel RDT "resource control" filesystem
intelRdtRoot string
intelRdtRootLock sync.Mutex
// The flag to indicate if Intel RDT/CAT is enabled
catEnabled bool
// The flag to indicate if Intel RDT/MBA is enabled
mbaEnabled bool
// The flag to indicate if Intel RDT/MBA Software Controller is enabled
mbaScEnabled bool
// For Intel RDT initialization
initOnce sync.Once
)
type intelRdtData struct {
root string
config *configs.Config
pid int
}
// Check if Intel RDT sub-features are enabled in featuresInit()
func featuresInit() {
initOnce.Do(func() {
// 1. Check if hardware and kernel support Intel RDT sub-features
flagsSet, err := parseCpuInfoFile("/proc/cpuinfo")
if err != nil {
return
}
// 2. Check if Intel RDT "resource control" filesystem is mounted
// The user guarantees to mount the filesystem
if !isIntelRdtMounted() {
return
}
// 3. Double check if Intel RDT sub-features are available in
// "resource control" filesystem. Intel RDT sub-features can be
// selectively disabled or enabled by kernel command line
// (e.g., rdt=!l3cat,mba) in 4.14 and newer kernel
if flagsSet.CAT {
if _, err := os.Stat(filepath.Join(intelRdtRoot, "info", "L3")); err == nil {
catEnabled = true
}
}
if mbaScEnabled {
// We confirm MBA Software Controller is enabled in step 2,
// MBA should be enabled because MBA Software Controller
// depends on MBA
mbaEnabled = true
} else if flagsSet.MBA {
if _, err := os.Stat(filepath.Join(intelRdtRoot, "info", "MB")); err == nil {
mbaEnabled = true
}
}
if flagsSet.MBMTotal || flagsSet.MBMLocal || flagsSet.CMT {
if _, err := os.Stat(filepath.Join(intelRdtRoot, "info", "L3_MON")); err != nil {
return
}
enabledMonFeatures, err = getMonFeatures(intelRdtRoot)
if err != nil {
return
}
if enabledMonFeatures.mbmTotalBytes || enabledMonFeatures.mbmLocalBytes {
mbmEnabled = true
}
if enabledMonFeatures.llcOccupancy {
cmtEnabled = true
}
}
})
}
// Return the mount point path of Intel RDT "resource control" filesysem
func findIntelRdtMountpointDir(f io.Reader) (string, error) {
mi, err := mountinfo.GetMountsFromReader(f, func(m *mountinfo.Info) (bool, bool) {
// similar to mountinfo.FSTypeFilter but stops after the first match
if m.FSType == "resctrl" {
return false, true // don't skip, stop
}
return true, false // skip, keep going
})
if err != nil {
return "", err
}
if len(mi) < 1 {
return "", NewNotFoundError("Intel RDT")
}
// Check if MBA Software Controller is enabled through mount option "-o mba_MBps"
if strings.Contains(","+mi[0].VFSOptions+",", ",mba_MBps,") {
mbaScEnabled = true
}
return mi[0].Mountpoint, nil
}
// Gets the root path of Intel RDT "resource control" filesystem
func getIntelRdtRoot() (string, error) {
intelRdtRootLock.Lock()
defer intelRdtRootLock.Unlock()
if intelRdtRoot != "" {
return intelRdtRoot, nil
}
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return "", err
}
root, err := findIntelRdtMountpointDir(f)
f.Close()
if err != nil {
return "", err
}
if _, err := os.Stat(root); err != nil {
return "", err
}
intelRdtRoot = root
return intelRdtRoot, nil
}
func isIntelRdtMounted() bool {
_, err := getIntelRdtRoot()
return err == nil
}
type cpuInfoFlags struct {
CAT bool // Cache Allocation Technology
MBA bool // Memory Bandwidth Allocation
// Memory Bandwidth Monitoring related.
MBMTotal bool
MBMLocal bool
CMT bool // Cache Monitoring Technology
}
func parseCpuInfoFile(path string) (cpuInfoFlags, error) {
infoFlags := cpuInfoFlags{}
f, err := os.Open(path)
if err != nil {
return infoFlags, err
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
line := s.Text()
// Search "cat_l3" and "mba" flags in first "flags" line
if strings.HasPrefix(line, "flags") {
flags := strings.Split(line, " ")
// "cat_l3" flag for CAT and "mba" flag for MBA
for _, flag := range flags {
switch flag {
case "cat_l3":
infoFlags.CAT = true
case "mba":
infoFlags.MBA = true
case "cqm_mbm_total":
infoFlags.MBMTotal = true
case "cqm_mbm_local":
infoFlags.MBMLocal = true
case "cqm_occup_llc":
infoFlags.CMT = true
}
}
return infoFlags, nil
}
}
if err := s.Err(); err != nil {
return infoFlags, err
}
return infoFlags, nil
}
func parseUint(s string, base, bitSize int) (uint64, error) {
value, err := strconv.ParseUint(s, base, bitSize)
if err != nil {
intValue, intErr := strconv.ParseInt(s, base, bitSize)
// 1. Handle negative values greater than MinInt64 (and)
// 2. Handle negative values lesser than MinInt64
if intErr == nil && intValue < 0 {
return 0, nil
} else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 {
return 0, nil
}
return value, err
}
return value, nil
}
// Gets a single uint64 value from the specified file.
func getIntelRdtParamUint(path, file string) (uint64, error) {
fileName := filepath.Join(path, file)
contents, err := ioutil.ReadFile(fileName)
if err != nil {
return 0, err
}
res, err := parseUint(string(bytes.TrimSpace(contents)), 10, 64)
if err != nil {
return res, fmt.Errorf("unable to parse %q as a uint from file %q", string(contents), fileName)
}
return res, nil
}
// Gets a string value from the specified file
func getIntelRdtParamString(path, file string) (string, error) {
contents, err := ioutil.ReadFile(filepath.Join(path, file))
if err != nil {
return "", err
}
return string(bytes.TrimSpace(contents)), nil
}
func writeFile(dir, file, data string) error {
if dir == "" {
return fmt.Errorf("no such directory for %s", file)
}
if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data+"\n"), 0o600); err != nil {
return fmt.Errorf("failed to write %v to %v: %v", data, file, err)
}
return nil
}
func getIntelRdtData(c *configs.Config, pid int) (*intelRdtData, error) {
rootPath, err := getIntelRdtRoot()
if err != nil {
return nil, err
}
return &intelRdtData{
root: rootPath,
config: c,
pid: pid,
}, nil
}
// Get the read-only L3 cache information
func getL3CacheInfo() (*L3CacheInfo, error) {
l3CacheInfo := &L3CacheInfo{}
rootPath, err := getIntelRdtRoot()
if err != nil {
return l3CacheInfo, err
}
path := filepath.Join(rootPath, "info", "L3")
cbmMask, err := getIntelRdtParamString(path, "cbm_mask")
if err != nil {
return l3CacheInfo, err
}
minCbmBits, err := getIntelRdtParamUint(path, "min_cbm_bits")
if err != nil {
return l3CacheInfo, err
}
numClosids, err := getIntelRdtParamUint(path, "num_closids")
if err != nil {
return l3CacheInfo, err
}
l3CacheInfo.CbmMask = cbmMask
l3CacheInfo.MinCbmBits = minCbmBits
l3CacheInfo.NumClosids = numClosids
return l3CacheInfo, nil
}
// Get the read-only memory bandwidth information
func getMemBwInfo() (*MemBwInfo, error) {
memBwInfo := &MemBwInfo{}
rootPath, err := getIntelRdtRoot()
if err != nil {
return memBwInfo, err
}
path := filepath.Join(rootPath, "info", "MB")
bandwidthGran, err := getIntelRdtParamUint(path, "bandwidth_gran")
if err != nil {
return memBwInfo, err
}
delayLinear, err := getIntelRdtParamUint(path, "delay_linear")
if err != nil {
return memBwInfo, err
}
minBandwidth, err := getIntelRdtParamUint(path, "min_bandwidth")
if err != nil {
return memBwInfo, err
}
numClosids, err := getIntelRdtParamUint(path, "num_closids")
if err != nil {
return memBwInfo, err
}
memBwInfo.BandwidthGran = bandwidthGran
memBwInfo.DelayLinear = delayLinear
memBwInfo.MinBandwidth = minBandwidth
memBwInfo.NumClosids = numClosids
return memBwInfo, nil
}
// Get diagnostics for last filesystem operation error from file info/last_cmd_status
func getLastCmdStatus() (string, error) {
rootPath, err := getIntelRdtRoot()
if err != nil {
return "", err
}
path := filepath.Join(rootPath, "info")
lastCmdStatus, err := getIntelRdtParamString(path, "last_cmd_status")
if err != nil {
return "", err
}
return lastCmdStatus, nil
}
// WriteIntelRdtTasks writes the specified pid into the "tasks" file
func WriteIntelRdtTasks(dir string, pid int) error {
if dir == "" {
return fmt.Errorf("no such directory for %s", IntelRdtTasks)
}
// Don't attach any pid if -1 is specified as a pid
if pid != -1 {
if err := ioutil.WriteFile(filepath.Join(dir, IntelRdtTasks), []byte(strconv.Itoa(pid)), 0o600); err != nil {
return fmt.Errorf("failed to write %v to %v: %v", pid, IntelRdtTasks, err)
}
}
return nil
}
// Check if Intel RDT/CAT is enabled
func IsCATEnabled() bool {
featuresInit()
return catEnabled
}
// Check if Intel RDT/MBA is enabled
func IsMBAEnabled() bool {
featuresInit()
return mbaEnabled
}
// Check if Intel RDT/MBA Software Controller is enabled
func IsMBAScEnabled() bool {
featuresInit()
return mbaScEnabled
}
// Get the 'container_id' path in Intel RDT "resource control" filesystem
func GetIntelRdtPath(id string) (string, error) {
rootPath, err := getIntelRdtRoot()
if err != nil {
return "", err
}
path := filepath.Join(rootPath, id)
return path, nil
}
// Applies Intel RDT configuration to the process with the specified pid
func (m *intelRdtManager) Apply(pid int) (err error) {
// If intelRdt is not specified in config, we do nothing
if m.config.IntelRdt == nil {
return nil
}
d, err := getIntelRdtData(m.config, pid)
if err != nil && !IsNotFound(err) {
return err
}
m.mu.Lock()
defer m.mu.Unlock()
path, err := d.join(m.id)
if err != nil {
return err
}
m.path = path
return nil
}
// Destroys the Intel RDT 'container_id' group
func (m *intelRdtManager) Destroy() error {
m.mu.Lock()
defer m.mu.Unlock()
if err := os.RemoveAll(m.GetPath()); err != nil {
return err
}
m.path = ""
return nil
}
// Returns Intel RDT path to save in a state file and to be able to
// restore the object later
func (m *intelRdtManager) GetPath() string {
if m.path == "" {
m.path, _ = GetIntelRdtPath(m.id)
}
return m.path
}
// Returns statistics for Intel RDT
func (m *intelRdtManager) GetStats() (*Stats, error) {
// If intelRdt is not specified in config
if m.config.IntelRdt == nil {
return nil, nil
}
m.mu.Lock()
defer m.mu.Unlock()
stats := NewStats()
rootPath, err := getIntelRdtRoot()
if err != nil {
return nil, err
}
// The read-only L3 cache and memory bandwidth schemata in root
tmpRootStrings, err := getIntelRdtParamString(rootPath, "schemata")
if err != nil {
return nil, err
}
schemaRootStrings := strings.Split(tmpRootStrings, "\n")
// The L3 cache and memory bandwidth schemata in 'container_id' group
containerPath := m.GetPath()
tmpStrings, err := getIntelRdtParamString(containerPath, "schemata")
if err != nil {
return nil, err
}
schemaStrings := strings.Split(tmpStrings, "\n")
if IsCATEnabled() {
// The read-only L3 cache information
l3CacheInfo, err := getL3CacheInfo()
if err != nil {
return nil, err
}
stats.L3CacheInfo = l3CacheInfo
// The read-only L3 cache schema in root
for _, schemaRoot := range schemaRootStrings {
if strings.Contains(schemaRoot, "L3") {
stats.L3CacheSchemaRoot = strings.TrimSpace(schemaRoot)
}
}
// The L3 cache schema in 'container_id' group
for _, schema := range schemaStrings {
if strings.Contains(schema, "L3") {
stats.L3CacheSchema = strings.TrimSpace(schema)
}
}
}
if IsMBAEnabled() {
// The read-only memory bandwidth information
memBwInfo, err := getMemBwInfo()
if err != nil {
return nil, err
}
stats.MemBwInfo = memBwInfo
// The read-only memory bandwidth information
for _, schemaRoot := range schemaRootStrings {
if strings.Contains(schemaRoot, "MB") {
stats.MemBwSchemaRoot = strings.TrimSpace(schemaRoot)
}
}
// The memory bandwidth schema in 'container_id' group
for _, schema := range schemaStrings {
if strings.Contains(schema, "MB") {
stats.MemBwSchema = strings.TrimSpace(schema)
}
}
}
if IsMBMEnabled() || IsCMTEnabled() {
err = getMonitoringStats(containerPath, stats)
if err != nil {
return nil, err
}
}
return stats, nil
}
// Set Intel RDT "resource control" filesystem as configured.
func (m *intelRdtManager) Set(container *configs.Config) error {
// About L3 cache schema:
// It has allocation bitmasks/values for L3 cache on each socket,
// which contains L3 cache id and capacity bitmask (CBM).
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
// For example, on a two-socket machine, the schema line could be:
// L3:0=ff;1=c0
// which means L3 cache id 0's CBM is 0xff, and L3 cache id 1's CBM
// is 0xc0.
//
// The valid L3 cache CBM is a *contiguous bits set* and number of
// bits that can be set is less than the max bit. The max bits in the
// CBM is varied among supported Intel CPU models. Kernel will check
// if it is valid when writing. e.g., default value 0xfffff in root
// indicates the max bits of CBM is 20 bits, which mapping to entire
// L3 cache capacity. Some valid CBM values to set in a group:
// 0xf, 0xf0, 0x3ff, 0x1f00 and etc.
//
//
// About memory bandwidth schema:
// It has allocation values for memory bandwidth on each socket, which
// contains L3 cache id and memory bandwidth.
// Format: "MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;..."
// For example, on a two-socket machine, the schema line could be:
// "MB:0=20;1=70"
//
// The minimum bandwidth percentage value for each CPU model is
// predefined and can be looked up through "info/MB/min_bandwidth".
// The bandwidth granularity that is allocated is also dependent on
// the CPU model and can be looked up at "info/MB/bandwidth_gran".
// The available bandwidth control steps are: min_bw + N * bw_gran.
// Intermediate values are rounded to the next control step available
// on the hardware.
//
// If MBA Software Controller is enabled through mount option
// "-o mba_MBps": mount -t resctrl resctrl -o mba_MBps /sys/fs/resctrl
// We could specify memory bandwidth in "MBps" (Mega Bytes per second)
// unit instead of "percentages". The kernel underneath would use a
// software feedback mechanism or a "Software Controller" which reads
// the actual bandwidth using MBM counters and adjust the memory
// bandwidth percentages to ensure:
// "actual memory bandwidth < user specified memory bandwidth".
//
// For example, on a two-socket machine, the schema line could be
// "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on
// socket 0 and 7000 MBps memory bandwidth limit on socket 1.
if container.IntelRdt != nil {
path := m.GetPath()
l3CacheSchema := container.IntelRdt.L3CacheSchema
memBwSchema := container.IntelRdt.MemBwSchema
// Write a single joint schema string to schemata file
if l3CacheSchema != "" && memBwSchema != "" {
if err := writeFile(path, "schemata", l3CacheSchema+"\n"+memBwSchema); err != nil {
return NewLastCmdError(err)
}
}
// Write only L3 cache schema string to schemata file
if l3CacheSchema != "" && memBwSchema == "" {
if err := writeFile(path, "schemata", l3CacheSchema); err != nil {
return NewLastCmdError(err)
}
}
// Write only memory bandwidth schema string to schemata file
if l3CacheSchema == "" && memBwSchema != "" {
if err := writeFile(path, "schemata", memBwSchema); err != nil {
return NewLastCmdError(err)
}
}
}
return nil
}
func (raw *intelRdtData) join(id string) (string, error) {
path := filepath.Join(raw.root, id)
if err := os.MkdirAll(path, 0o755); err != nil {
return "", NewLastCmdError(err)
}
if err := WriteIntelRdtTasks(path, raw.pid); err != nil {
return "", NewLastCmdError(err)
}
return path, nil
}
type NotFoundError struct {
ResourceControl string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("mountpoint for %s not found", e.ResourceControl)
}
func NewNotFoundError(res string) error {
return &NotFoundError{
ResourceControl: res,
}
}
func IsNotFound(err error) bool {
if err == nil {
return false
}
_, ok := err.(*NotFoundError)
return ok
}
type LastCmdError struct {
LastCmdStatus string
Err error
}
func (e *LastCmdError) Error() string {
return e.Err.Error() + ", last_cmd_status: " + e.LastCmdStatus
}
func NewLastCmdError(err error) error {
lastCmdStatus, err1 := getLastCmdStatus()
if err1 == nil {
return &LastCmdError{
LastCmdStatus: lastCmdStatus,
Err: err,
}
}
return err
}

View File

@ -0,0 +1,35 @@
// +build linux
package intelrdt
var (
// The flag to indicate if Intel RDT/MBM is enabled
mbmEnabled bool
)
// Check if Intel RDT/MBM is enabled.
func IsMBMEnabled() bool {
featuresInit()
return mbmEnabled
}
func getMBMNumaNodeStats(numaPath string) (*MBMNumaNodeStats, error) {
stats := &MBMNumaNodeStats{}
if enabledMonFeatures.mbmTotalBytes {
mbmTotalBytes, err := getIntelRdtParamUint(numaPath, "mbm_total_bytes")
if err != nil {
return nil, err
}
stats.MBMTotalBytes = mbmTotalBytes
}
if enabledMonFeatures.mbmLocalBytes {
mbmLocalBytes, err := getIntelRdtParamUint(numaPath, "mbm_local_bytes")
if err != nil {
return nil, err
}
stats.MBMLocalBytes = mbmLocalBytes
}
return stats, nil
}

View File

@ -0,0 +1,86 @@
package intelrdt
import (
"bufio"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/sirupsen/logrus"
)
var (
enabledMonFeatures monFeatures
)
type monFeatures struct {
mbmTotalBytes bool
mbmLocalBytes bool
llcOccupancy bool
}
func getMonFeatures(intelRdtRoot string) (monFeatures, error) {
file, err := os.Open(filepath.Join(intelRdtRoot, "info", "L3_MON", "mon_features"))
if err != nil {
return monFeatures{}, err
}
defer file.Close()
return parseMonFeatures(file)
}
func parseMonFeatures(reader io.Reader) (monFeatures, error) {
scanner := bufio.NewScanner(reader)
monFeatures := monFeatures{}
for scanner.Scan() {
switch feature := scanner.Text(); feature {
case "mbm_total_bytes":
monFeatures.mbmTotalBytes = true
case "mbm_local_bytes":
monFeatures.mbmLocalBytes = true
case "llc_occupancy":
monFeatures.llcOccupancy = true
default:
logrus.Warnf("Unsupported Intel RDT monitoring feature: %s", feature)
}
}
return monFeatures, scanner.Err()
}
func getMonitoringStats(containerPath string, stats *Stats) error {
numaFiles, err := ioutil.ReadDir(filepath.Join(containerPath, "mon_data"))
if err != nil {
return err
}
var mbmStats []MBMNumaNodeStats
var cmtStats []CMTNumaNodeStats
for _, file := range numaFiles {
if file.IsDir() {
numaPath := filepath.Join(containerPath, "mon_data", file.Name())
if IsMBMEnabled() {
numaMBMStats, err := getMBMNumaNodeStats(numaPath)
if err != nil {
return err
}
mbmStats = append(mbmStats, *numaMBMStats)
}
if IsCMTEnabled() {
numaCMTStats, err := getCMTNumaNodeStats(numaPath)
if err != nil {
return err
}
cmtStats = append(cmtStats, *numaCMTStats)
}
}
}
stats.MBMStats = &mbmStats
stats.CMTStats = &cmtStats
return err
}

View File

@ -0,0 +1,59 @@
// +build linux
package intelrdt
type L3CacheInfo struct {
CbmMask string `json:"cbm_mask,omitempty"`
MinCbmBits uint64 `json:"min_cbm_bits,omitempty"`
NumClosids uint64 `json:"num_closids,omitempty"`
}
type MemBwInfo struct {
BandwidthGran uint64 `json:"bandwidth_gran,omitempty"`
DelayLinear uint64 `json:"delay_linear,omitempty"`
MinBandwidth uint64 `json:"min_bandwidth,omitempty"`
NumClosids uint64 `json:"num_closids,omitempty"`
}
type MBMNumaNodeStats struct {
// The 'mbm_total_bytes' in 'container_id' group.
MBMTotalBytes uint64 `json:"mbm_total_bytes"`
// The 'mbm_local_bytes' in 'container_id' group.
MBMLocalBytes uint64 `json:"mbm_local_bytes"`
}
type CMTNumaNodeStats struct {
// The 'llc_occupancy' in 'container_id' group.
LLCOccupancy uint64 `json:"llc_occupancy"`
}
type Stats struct {
// The read-only L3 cache information
L3CacheInfo *L3CacheInfo `json:"l3_cache_info,omitempty"`
// The read-only L3 cache schema in root
L3CacheSchemaRoot string `json:"l3_cache_schema_root,omitempty"`
// The L3 cache schema in 'container_id' group
L3CacheSchema string `json:"l3_cache_schema,omitempty"`
// The read-only memory bandwidth information
MemBwInfo *MemBwInfo `json:"mem_bw_info,omitempty"`
// The read-only memory bandwidth schema in root
MemBwSchemaRoot string `json:"mem_bw_schema_root,omitempty"`
// The memory bandwidth schema in 'container_id' group
MemBwSchema string `json:"mem_bw_schema,omitempty"`
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
MBMStats *[]MBMNumaNodeStats `json:"mbm_stats,omitempty"`
// The cache monitoring technology statistics from NUMA nodes in 'container_id' group
CMTStats *[]CMTNumaNodeStats `json:"cmt_stats,omitempty"`
}
func NewStats() *Stats {
return &Stats{}
}

View File

@ -0,0 +1,47 @@
// +build linux
package keys
import (
"strconv"
"strings"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
type KeySerial uint32
func JoinSessionKeyring(name string) (KeySerial, error) {
sessKeyId, err := unix.KeyctlJoinSessionKeyring(name)
if err != nil {
return 0, errors.Wrap(err, "create session key")
}
return KeySerial(sessKeyId), nil
}
// ModKeyringPerm modifies permissions on a keyring by reading the current permissions,
// anding the bits with the given mask (clearing permissions) and setting
// additional permission bits
func ModKeyringPerm(ringId KeySerial, mask, setbits uint32) error {
dest, err := unix.KeyctlString(unix.KEYCTL_DESCRIBE, int(ringId))
if err != nil {
return err
}
res := strings.Split(dest, ";")
if len(res) < 5 {
return errors.New("Destination buffer for key description is too small")
}
// parse permissions
perm64, err := strconv.ParseUint(res[3], 16, 32)
if err != nil {
return err
}
perm := (uint32(perm64) & mask) | setbits
return unix.KeyctlSetperm(int(ringId), perm)
}

View File

@ -0,0 +1,102 @@
package logs
import (
"bufio"
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"sync"
"github.com/sirupsen/logrus"
)
var (
configureMutex = sync.Mutex{}
// loggingConfigured will be set once logging has been configured via invoking `ConfigureLogging`.
// Subsequent invocations of `ConfigureLogging` would be no-op
loggingConfigured = false
)
type Config struct {
LogLevel logrus.Level
LogFormat string
LogFilePath string
LogPipeFd string
}
func ForwardLogs(logPipe io.Reader) {
lineReader := bufio.NewReader(logPipe)
for {
line, err := lineReader.ReadBytes('\n')
if len(line) > 0 {
processEntry(line)
}
if err == io.EOF {
logrus.Debugf("log pipe has been closed: %+v", err)
return
}
if err != nil {
logrus.Errorf("log pipe read error: %+v", err)
}
}
}
func processEntry(text []byte) {
type jsonLog struct {
Level string `json:"level"`
Msg string `json:"msg"`
}
var jl jsonLog
if err := json.Unmarshal(text, &jl); err != nil {
logrus.Errorf("failed to decode %q to json: %+v", text, err)
return
}
lvl, err := logrus.ParseLevel(jl.Level)
if err != nil {
logrus.Errorf("failed to parse log level %q: %v\n", jl.Level, err)
return
}
logrus.StandardLogger().Logf(lvl, jl.Msg)
}
func ConfigureLogging(config Config) error {
configureMutex.Lock()
defer configureMutex.Unlock()
if loggingConfigured {
logrus.Debug("logging has already been configured")
return nil
}
logrus.SetLevel(config.LogLevel)
if config.LogPipeFd != "" {
logPipeFdInt, err := strconv.Atoi(config.LogPipeFd)
if err != nil {
return fmt.Errorf("failed to convert _LIBCONTAINER_LOGPIPE environment variable value %q to int: %v", config.LogPipeFd, err)
}
logrus.SetOutput(os.NewFile(uintptr(logPipeFdInt), "logpipe"))
} else if config.LogFilePath != "" {
f, err := os.OpenFile(config.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0644)
if err != nil {
return err
}
logrus.SetOutput(f)
}
switch config.LogFormat {
case "text":
// retain logrus's default.
case "json":
logrus.SetFormatter(new(logrus.JSONFormatter))
default:
return fmt.Errorf("unknown log-format %q", config.LogFormat)
}
loggingConfigured = true
return nil
}

View File

@ -0,0 +1,89 @@
// +build linux
package libcontainer
import (
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// list of known message types we want to send to bootstrap program
// The number is randomly chosen to not conflict with known netlink types
const (
InitMsg uint16 = 62000
CloneFlagsAttr uint16 = 27281
NsPathsAttr uint16 = 27282
UidmapAttr uint16 = 27283
GidmapAttr uint16 = 27284
SetgroupAttr uint16 = 27285
OomScoreAdjAttr uint16 = 27286
RootlessEUIDAttr uint16 = 27287
UidmapPathAttr uint16 = 27288
GidmapPathAttr uint16 = 27289
)
type Int32msg struct {
Type uint16
Value uint32
}
// Serialize serializes the message.
// Int32msg has the following representation
// | nlattr len | nlattr type |
// | uint32 value |
func (msg *Int32msg) Serialize() []byte {
buf := make([]byte, msg.Len())
native := nl.NativeEndian()
native.PutUint16(buf[0:2], uint16(msg.Len()))
native.PutUint16(buf[2:4], msg.Type)
native.PutUint32(buf[4:8], msg.Value)
return buf
}
func (msg *Int32msg) Len() int {
return unix.NLA_HDRLEN + 4
}
// Bytemsg has the following representation
// | nlattr len | nlattr type |
// | value | pad |
type Bytemsg struct {
Type uint16
Value []byte
}
func (msg *Bytemsg) Serialize() []byte {
l := msg.Len()
buf := make([]byte, (l+unix.NLA_ALIGNTO-1) & ^(unix.NLA_ALIGNTO-1))
native := nl.NativeEndian()
native.PutUint16(buf[0:2], uint16(l))
native.PutUint16(buf[2:4], msg.Type)
copy(buf[4:], msg.Value)
return buf
}
func (msg *Bytemsg) Len() int {
return unix.NLA_HDRLEN + len(msg.Value) + 1 // null-terminated
}
type Boolmsg struct {
Type uint16
Value bool
}
func (msg *Boolmsg) Serialize() []byte {
buf := make([]byte, msg.Len())
native := nl.NativeEndian()
native.PutUint16(buf[0:2], uint16(msg.Len()))
native.PutUint16(buf[2:4], msg.Type)
if msg.Value {
native.PutUint32(buf[4:8], uint32(1))
} else {
native.PutUint32(buf[4:8], uint32(0))
}
return buf
}
func (msg *Boolmsg) Len() int {
return unix.NLA_HDRLEN + 4 // alignment
}

View File

@ -0,0 +1,103 @@
// +build linux
package libcontainer
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"strconv"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/types"
"github.com/vishvananda/netlink"
)
var strategies = map[string]networkStrategy{
"loopback": &loopback{},
}
// networkStrategy represents a specific network configuration for
// a container's networking stack
type networkStrategy interface {
create(*network, int) error
initialize(*network) error
detach(*configs.Network) error
attach(*configs.Network) error
}
// getStrategy returns the specific network strategy for the
// provided type.
func getStrategy(tpe string) (networkStrategy, error) {
s, exists := strategies[tpe]
if !exists {
return nil, fmt.Errorf("unknown strategy type %q", tpe)
}
return s, nil
}
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
func getNetworkInterfaceStats(interfaceName string) (*types.NetworkInterface, error) {
out := &types.NetworkInterface{Name: interfaceName}
// This can happen if the network runtime information is missing - possible if the
// container was created by an old version of libcontainer.
if interfaceName == "" {
return out, nil
}
type netStatsPair struct {
// Where to write the output.
Out *uint64
// The network stats file to read.
File string
}
// Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container.
netStats := []netStatsPair{
{Out: &out.RxBytes, File: "tx_bytes"},
{Out: &out.RxPackets, File: "tx_packets"},
{Out: &out.RxErrors, File: "tx_errors"},
{Out: &out.RxDropped, File: "tx_dropped"},
{Out: &out.TxBytes, File: "rx_bytes"},
{Out: &out.TxPackets, File: "rx_packets"},
{Out: &out.TxErrors, File: "rx_errors"},
{Out: &out.TxDropped, File: "rx_dropped"},
}
for _, netStat := range netStats {
data, err := readSysfsNetworkStats(interfaceName, netStat.File)
if err != nil {
return nil, err
}
*(netStat.Out) = data
}
return out, nil
}
// Reads the specified statistics available under /sys/class/net/<EthInterface>/statistics
func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) {
data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile))
if err != nil {
return 0, err
}
return strconv.ParseUint(string(bytes.TrimSpace(data)), 10, 64)
}
// loopback is a network strategy that provides a basic loopback device
type loopback struct {
}
func (l *loopback) create(n *network, nspid int) error {
return nil
}
func (l *loopback) initialize(config *network) error {
return netlink.LinkSetUp(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: "lo"}})
}
func (l *loopback) attach(n *configs.Network) (err error) {
return nil
}
func (l *loopback) detach(n *configs.Network) (err error) {
return nil
}

View File

@ -0,0 +1,87 @@
// +build linux
package libcontainer
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"golang.org/x/sys/unix"
)
type PressureLevel uint
const (
LowPressure PressureLevel = iota
MediumPressure
CriticalPressure
)
func registerMemoryEvent(cgDir string, evName string, arg string) (<-chan struct{}, error) {
evFile, err := os.Open(filepath.Join(cgDir, evName))
if err != nil {
return nil, err
}
fd, err := unix.Eventfd(0, unix.EFD_CLOEXEC)
if err != nil {
evFile.Close()
return nil, err
}
eventfd := os.NewFile(uintptr(fd), "eventfd")
eventControlPath := filepath.Join(cgDir, "cgroup.event_control")
data := fmt.Sprintf("%d %d %s", eventfd.Fd(), evFile.Fd(), arg)
if err := ioutil.WriteFile(eventControlPath, []byte(data), 0700); err != nil {
eventfd.Close()
evFile.Close()
return nil, err
}
ch := make(chan struct{})
go func() {
defer func() {
eventfd.Close()
evFile.Close()
close(ch)
}()
buf := make([]byte, 8)
for {
if _, err := eventfd.Read(buf); err != nil {
return
}
// When a cgroup is destroyed, an event is sent to eventfd.
// So if the control path is gone, return instead of notifying.
if _, err := os.Lstat(eventControlPath); os.IsNotExist(err) {
return
}
ch <- struct{}{}
}
}()
return ch, nil
}
// notifyOnOOM returns channel on which you can expect event about OOM,
// if process died without OOM this channel will be closed.
func notifyOnOOM(dir string) (<-chan struct{}, error) {
if dir == "" {
return nil, errors.New("memory controller missing")
}
return registerMemoryEvent(dir, "memory.oom_control", "")
}
func notifyMemoryPressure(dir string, level PressureLevel) (<-chan struct{}, error) {
if dir == "" {
return nil, errors.New("memory controller missing")
}
if level > CriticalPressure {
return nil, fmt.Errorf("invalid pressure level %d", level)
}
levelStr := []string{"low", "medium", "critical"}[level]
return registerMemoryEvent(dir, "memory.pressure_level", levelStr)
}

View File

@ -0,0 +1,102 @@
// +build linux
package libcontainer
import (
"io/ioutil"
"path/filepath"
"strconv"
"strings"
"unsafe"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func getValueFromCgroup(path, key string) (int, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return 0, err
}
lines := strings.Split(string(content), "\n")
for _, line := range lines {
arr := strings.Split(line, " ")
if len(arr) == 2 && arr[0] == key {
return strconv.Atoi(arr[1])
}
}
return 0, nil
}
func registerMemoryEventV2(cgDir, evName, cgEvName string) (<-chan struct{}, error) {
eventControlPath := filepath.Join(cgDir, evName)
cgEvPath := filepath.Join(cgDir, cgEvName)
fd, err := unix.InotifyInit()
if err != nil {
return nil, errors.Wrap(err, "unable to init inotify")
}
// watching oom kill
evFd, err := unix.InotifyAddWatch(fd, eventControlPath, unix.IN_MODIFY)
if err != nil {
unix.Close(fd)
return nil, errors.Wrap(err, "unable to add inotify watch")
}
// Because no `unix.IN_DELETE|unix.IN_DELETE_SELF` event for cgroup file system, so watching all process exited
cgFd, err := unix.InotifyAddWatch(fd, cgEvPath, unix.IN_MODIFY)
if err != nil {
unix.Close(fd)
return nil, errors.Wrap(err, "unable to add inotify watch")
}
ch := make(chan struct{})
go func() {
var (
buffer [unix.SizeofInotifyEvent + unix.PathMax + 1]byte
offset uint32
)
defer func() {
unix.Close(fd)
close(ch)
}()
for {
n, err := unix.Read(fd, buffer[:])
if err != nil {
logrus.Warnf("unable to read event data from inotify, got error: %v", err)
return
}
if n < unix.SizeofInotifyEvent {
logrus.Warnf("we should read at least %d bytes from inotify, but got %d bytes.", unix.SizeofInotifyEvent, n)
return
}
offset = 0
for offset <= uint32(n-unix.SizeofInotifyEvent) {
rawEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buffer[offset]))
offset += unix.SizeofInotifyEvent + uint32(rawEvent.Len)
if rawEvent.Mask&unix.IN_MODIFY != unix.IN_MODIFY {
continue
}
switch int(rawEvent.Wd) {
case evFd:
oom, err := getValueFromCgroup(eventControlPath, "oom_kill")
if err != nil || oom > 0 {
ch <- struct{}{}
}
case cgFd:
pids, err := getValueFromCgroup(cgEvPath, "populated")
if err != nil || pids == 0 {
return
}
}
}
}
}()
return ch, nil
}
// notifyOnOOMV2 returns channel on which you can expect event about OOM,
// if process died without OOM this channel will be closed.
func notifyOnOOMV2(path string) (<-chan struct{}, error) {
return registerMemoryEventV2(path, "memory.events", "cgroup.events")
}

View File

@ -0,0 +1,115 @@
package libcontainer
import (
"fmt"
"io"
"math"
"os"
"github.com/opencontainers/runc/libcontainer/configs"
)
type processOperations interface {
wait() (*os.ProcessState, error)
signal(sig os.Signal) error
pid() int
}
// Process specifies the configuration and IO for a process inside
// a container.
type Process struct {
// The command to be run followed by any arguments.
Args []string
// Env specifies the environment variables for the process.
Env []string
// User will set the uid and gid of the executing process running inside the container
// local to the container's user and group configuration.
User string
// AdditionalGroups specifies the gids that should be added to supplementary groups
// in addition to those that the user belongs to.
AdditionalGroups []string
// Cwd will change the processes current working directory inside the container's rootfs.
Cwd string
// Stdin is a pointer to a reader which provides the standard input stream.
Stdin io.Reader
// Stdout is a pointer to a writer which receives the standard output stream.
Stdout io.Writer
// Stderr is a pointer to a writer which receives the standard error stream.
Stderr io.Writer
// ExtraFiles specifies additional open files to be inherited by the container
ExtraFiles []*os.File
// Initial sizings for the console
ConsoleWidth uint16
ConsoleHeight uint16
// Capabilities specify the capabilities to keep when executing the process inside the container
// All capabilities not specified will be dropped from the processes capability mask
Capabilities *configs.Capabilities
// AppArmorProfile specifies the profile to apply to the process and is
// changed at the time the process is execed
AppArmorProfile string
// Label specifies the label to apply to the process. It is commonly used by selinux
Label string
// NoNewPrivileges controls whether processes can gain additional privileges.
NoNewPrivileges *bool
// Rlimits specifies the resource limits, such as max open files, to set in the container
// If Rlimits are not set, the container will inherit rlimits from the parent process
Rlimits []configs.Rlimit
// ConsoleSocket provides the masterfd console.
ConsoleSocket *os.File
// Init specifies whether the process is the first process in the container.
Init bool
ops processOperations
LogLevel string
}
// Wait waits for the process to exit.
// Wait releases any resources associated with the Process
func (p Process) Wait() (*os.ProcessState, error) {
if p.ops == nil {
return nil, newGenericError(fmt.Errorf("invalid process"), NoProcessOps)
}
return p.ops.wait()
}
// Pid returns the process ID
func (p Process) Pid() (int, error) {
// math.MinInt32 is returned here, because it's invalid value
// for the kill() system call.
if p.ops == nil {
return math.MinInt32, newGenericError(fmt.Errorf("invalid process"), NoProcessOps)
}
return p.ops.pid(), nil
}
// Signal sends a signal to the Process.
func (p Process) Signal(sig os.Signal) error {
if p.ops == nil {
return newGenericError(fmt.Errorf("invalid process"), NoProcessOps)
}
return p.ops.signal(sig)
}
// IO holds the process's STDIO
type IO struct {
Stdin io.WriteCloser
Stdout io.ReadCloser
Stderr io.ReadCloser
}

View File

@ -0,0 +1,651 @@
// +build linux
package libcontainer
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"time"
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/intelrdt"
"github.com/opencontainers/runc/libcontainer/logs"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
// Synchronisation value for cgroup namespace setup.
// The same constant is defined in nsexec.c as "CREATECGROUPNS".
const createCgroupns = 0x80
type parentProcess interface {
// pid returns the pid for the running process.
pid() int
// start starts the process execution.
start() error
// send a SIGKILL to the process and wait for the exit.
terminate() error
// wait waits on the process returning the process state.
wait() (*os.ProcessState, error)
// startTime returns the process start time.
startTime() (uint64, error)
signal(os.Signal) error
externalDescriptors() []string
setExternalDescriptors(fds []string)
forwardChildLogs()
}
type filePair struct {
parent *os.File
child *os.File
}
type setnsProcess struct {
cmd *exec.Cmd
messageSockPair filePair
logFilePair filePair
cgroupPaths map[string]string
rootlessCgroups bool
intelRdtPath string
config *initConfig
fds []string
process *Process
bootstrapData io.Reader
initProcessPid int
}
func (p *setnsProcess) startTime() (uint64, error) {
stat, err := system.Stat(p.pid())
return stat.StartTime, err
}
func (p *setnsProcess) signal(sig os.Signal) error {
s, ok := sig.(unix.Signal)
if !ok {
return errors.New("os: unsupported signal type")
}
return unix.Kill(p.pid(), s)
}
func (p *setnsProcess) start() (retErr error) {
defer p.messageSockPair.parent.Close()
err := p.cmd.Start()
// close the write-side of the pipes (controlled by child)
p.messageSockPair.child.Close()
p.logFilePair.child.Close()
if err != nil {
return newSystemErrorWithCause(err, "starting setns process")
}
defer func() {
if retErr != nil {
err := ignoreTerminateErrors(p.terminate())
if err != nil {
logrus.WithError(err).Warn("unable to terminate setnsProcess")
}
}
}()
if p.bootstrapData != nil {
if _, err := io.Copy(p.messageSockPair.parent, p.bootstrapData); err != nil {
return newSystemErrorWithCause(err, "copying bootstrap data to pipe")
}
}
if err := p.execSetns(); err != nil {
return newSystemErrorWithCause(err, "executing setns process")
}
if len(p.cgroupPaths) > 0 {
if err := cgroups.EnterPid(p.cgroupPaths, p.pid()); err != nil && !p.rootlessCgroups {
// On cgroup v2 + nesting + domain controllers, EnterPid may fail with EBUSY.
// https://github.com/opencontainers/runc/issues/2356#issuecomment-621277643
// Try to join the cgroup of InitProcessPid.
if cgroups.IsCgroup2UnifiedMode() {
initProcCgroupFile := fmt.Sprintf("/proc/%d/cgroup", p.initProcessPid)
initCg, initCgErr := cgroups.ParseCgroupFile(initProcCgroupFile)
if initCgErr == nil {
if initCgPath, ok := initCg[""]; ok {
initCgDirpath := filepath.Join(fs2.UnifiedMountpoint, initCgPath)
logrus.Debugf("adding pid %d to cgroups %v failed (%v), attempting to join %q (obtained from %s)",
p.pid(), p.cgroupPaths, err, initCg, initCgDirpath)
// NOTE: initCgDirPath is not guaranteed to exist because we didn't pause the container.
err = cgroups.WriteCgroupProc(initCgDirpath, p.pid())
}
}
}
if err != nil {
return newSystemErrorWithCausef(err, "adding pid %d to cgroups", p.pid())
}
}
}
if p.intelRdtPath != "" {
// if Intel RDT "resource control" filesystem path exists
_, err := os.Stat(p.intelRdtPath)
if err == nil {
if err := intelrdt.WriteIntelRdtTasks(p.intelRdtPath, p.pid()); err != nil {
return newSystemErrorWithCausef(err, "adding pid %d to Intel RDT resource control filesystem", p.pid())
}
}
}
// set rlimits, this has to be done here because we lose permissions
// to raise the limits once we enter a user-namespace
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
return newSystemErrorWithCause(err, "setting rlimits for process")
}
if err := utils.WriteJSON(p.messageSockPair.parent, p.config); err != nil {
return newSystemErrorWithCause(err, "writing config to pipe")
}
ierr := parseSync(p.messageSockPair.parent, func(sync *syncT) error {
switch sync.Type {
case procReady:
// This shouldn't happen.
panic("unexpected procReady in setns")
case procHooks:
// This shouldn't happen.
panic("unexpected procHooks in setns")
default:
return newSystemError(errors.New("invalid JSON payload from child"))
}
})
if err := unix.Shutdown(int(p.messageSockPair.parent.Fd()), unix.SHUT_WR); err != nil {
return newSystemErrorWithCause(err, "calling shutdown on init pipe")
}
// Must be done after Shutdown so the child will exit and we can wait for it.
if ierr != nil {
p.wait()
return ierr
}
return nil
}
// execSetns runs the process that executes C code to perform the setns calls
// because setns support requires the C process to fork off a child and perform the setns
// before the go runtime boots, we wait on the process to die and receive the child's pid
// over the provided pipe.
func (p *setnsProcess) execSetns() error {
status, err := p.cmd.Process.Wait()
if err != nil {
p.cmd.Wait()
return newSystemErrorWithCause(err, "waiting on setns process to finish")
}
if !status.Success() {
p.cmd.Wait()
return newSystemError(&exec.ExitError{ProcessState: status})
}
var pid *pid
if err := json.NewDecoder(p.messageSockPair.parent).Decode(&pid); err != nil {
p.cmd.Wait()
return newSystemErrorWithCause(err, "reading pid from init pipe")
}
// Clean up the zombie parent process
// On Unix systems FindProcess always succeeds.
firstChildProcess, _ := os.FindProcess(pid.PidFirstChild)
// Ignore the error in case the child has already been reaped for any reason
_, _ = firstChildProcess.Wait()
process, err := os.FindProcess(pid.Pid)
if err != nil {
return err
}
p.cmd.Process = process
p.process.ops = p
return nil
}
// terminate sends a SIGKILL to the forked process for the setns routine then waits to
// avoid the process becoming a zombie.
func (p *setnsProcess) terminate() error {
if p.cmd.Process == nil {
return nil
}
err := p.cmd.Process.Kill()
if _, werr := p.wait(); err == nil {
err = werr
}
return err
}
func (p *setnsProcess) wait() (*os.ProcessState, error) {
err := p.cmd.Wait()
// Return actual ProcessState even on Wait error
return p.cmd.ProcessState, err
}
func (p *setnsProcess) pid() int {
return p.cmd.Process.Pid
}
func (p *setnsProcess) externalDescriptors() []string {
return p.fds
}
func (p *setnsProcess) setExternalDescriptors(newFds []string) {
p.fds = newFds
}
func (p *setnsProcess) forwardChildLogs() {
go logs.ForwardLogs(p.logFilePair.parent)
}
type initProcess struct {
cmd *exec.Cmd
messageSockPair filePair
logFilePair filePair
config *initConfig
manager cgroups.Manager
intelRdtManager intelrdt.Manager
container *linuxContainer
fds []string
process *Process
bootstrapData io.Reader
sharePidns bool
}
func (p *initProcess) pid() int {
return p.cmd.Process.Pid
}
func (p *initProcess) externalDescriptors() []string {
return p.fds
}
// getChildPid receives the final child's pid over the provided pipe.
func (p *initProcess) getChildPid() (int, error) {
var pid pid
if err := json.NewDecoder(p.messageSockPair.parent).Decode(&pid); err != nil {
p.cmd.Wait()
return -1, err
}
// Clean up the zombie parent process
// On Unix systems FindProcess always succeeds.
firstChildProcess, _ := os.FindProcess(pid.PidFirstChild)
// Ignore the error in case the child has already been reaped for any reason
_, _ = firstChildProcess.Wait()
return pid.Pid, nil
}
func (p *initProcess) waitForChildExit(childPid int) error {
status, err := p.cmd.Process.Wait()
if err != nil {
p.cmd.Wait()
return err
}
if !status.Success() {
p.cmd.Wait()
return &exec.ExitError{ProcessState: status}
}
process, err := os.FindProcess(childPid)
if err != nil {
return err
}
p.cmd.Process = process
p.process.ops = p
return nil
}
func (p *initProcess) start() (retErr error) {
defer p.messageSockPair.parent.Close()
err := p.cmd.Start()
p.process.ops = p
// close the write-side of the pipes (controlled by child)
p.messageSockPair.child.Close()
p.logFilePair.child.Close()
if err != nil {
p.process.ops = nil
return newSystemErrorWithCause(err, "starting init process command")
}
defer func() {
if retErr != nil {
// terminate the process to ensure we can remove cgroups
if err := ignoreTerminateErrors(p.terminate()); err != nil {
logrus.WithError(err).Warn("unable to terminate initProcess")
}
p.manager.Destroy()
if p.intelRdtManager != nil {
p.intelRdtManager.Destroy()
}
}
}()
// Do this before syncing with child so that no children can escape the
// cgroup. We don't need to worry about not doing this and not being root
// because we'd be using the rootless cgroup manager in that case.
if err := p.manager.Apply(p.pid()); err != nil {
return newSystemErrorWithCause(err, "applying cgroup configuration for process")
}
if p.intelRdtManager != nil {
if err := p.intelRdtManager.Apply(p.pid()); err != nil {
return newSystemErrorWithCause(err, "applying Intel RDT configuration for process")
}
}
if _, err := io.Copy(p.messageSockPair.parent, p.bootstrapData); err != nil {
return newSystemErrorWithCause(err, "copying bootstrap data to pipe")
}
childPid, err := p.getChildPid()
if err != nil {
return newSystemErrorWithCause(err, "getting the final child's pid from pipe")
}
// Save the standard descriptor names before the container process
// can potentially move them (e.g., via dup2()). If we don't do this now,
// we won't know at checkpoint time which file descriptor to look up.
fds, err := getPipeFds(childPid)
if err != nil {
return newSystemErrorWithCausef(err, "getting pipe fds for pid %d", childPid)
}
p.setExternalDescriptors(fds)
// Now it's time to setup cgroup namesapce
if p.config.Config.Namespaces.Contains(configs.NEWCGROUP) && p.config.Config.Namespaces.PathOf(configs.NEWCGROUP) == "" {
if _, err := p.messageSockPair.parent.Write([]byte{createCgroupns}); err != nil {
return newSystemErrorWithCause(err, "sending synchronization value to init process")
}
}
// Wait for our first child to exit
if err := p.waitForChildExit(childPid); err != nil {
return newSystemErrorWithCause(err, "waiting for our first child to exit")
}
if err := p.createNetworkInterfaces(); err != nil {
return newSystemErrorWithCause(err, "creating network interfaces")
}
if err := p.updateSpecState(); err != nil {
return newSystemErrorWithCause(err, "updating the spec state")
}
if err := p.sendConfig(); err != nil {
return newSystemErrorWithCause(err, "sending config to init process")
}
var (
sentRun bool
sentResume bool
)
ierr := parseSync(p.messageSockPair.parent, func(sync *syncT) error {
switch sync.Type {
case procReady:
// set rlimits, this has to be done here because we lose permissions
// to raise the limits once we enter a user-namespace
if err := setupRlimits(p.config.Rlimits, p.pid()); err != nil {
return newSystemErrorWithCause(err, "setting rlimits for ready process")
}
// call prestart and CreateRuntime hooks
if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
// Setup cgroup before the hook, so that the prestart and CreateRuntime hook could apply cgroup permissions.
if err := p.manager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting cgroup config for ready process")
}
if p.intelRdtManager != nil {
if err := p.intelRdtManager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting Intel RDT config for ready process")
}
}
if p.config.Config.Hooks != nil {
s, err := p.container.currentOCIState()
if err != nil {
return err
}
// initProcessStartTime hasn't been set yet.
s.Pid = p.cmd.Process.Pid
s.Status = specs.StateCreating
hooks := p.config.Config.Hooks
if err := hooks[configs.Prestart].RunHooks(s); err != nil {
return err
}
if err := hooks[configs.CreateRuntime].RunHooks(s); err != nil {
return err
}
}
}
// generate a timestamp indicating when the container was started
p.container.created = time.Now().UTC()
p.container.state = &createdState{
c: p.container,
}
// NOTE: If the procRun state has been synced and the
// runc-create process has been killed for some reason,
// the runc-init[2:stage] process will be leaky. And
// the runc command also fails to parse root directory
// because the container doesn't have state.json.
//
// In order to cleanup the runc-init[2:stage] by
// runc-delete/stop, we should store the status before
// procRun sync.
state, uerr := p.container.updateState(p)
if uerr != nil {
return newSystemErrorWithCause(err, "store init state")
}
p.container.initProcessStartTime = state.InitProcessStartTime
// Sync with child.
if err := writeSync(p.messageSockPair.parent, procRun); err != nil {
return newSystemErrorWithCause(err, "writing syncT 'run'")
}
sentRun = true
case procHooks:
// Setup cgroup before prestart hook, so that the prestart hook could apply cgroup permissions.
if err := p.manager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting cgroup config for procHooks process")
}
if p.intelRdtManager != nil {
if err := p.intelRdtManager.Set(p.config.Config); err != nil {
return newSystemErrorWithCause(err, "setting Intel RDT config for procHooks process")
}
}
if p.config.Config.Hooks != nil {
s, err := p.container.currentOCIState()
if err != nil {
return err
}
// initProcessStartTime hasn't been set yet.
s.Pid = p.cmd.Process.Pid
s.Status = specs.StateCreating
hooks := p.config.Config.Hooks
if err := hooks[configs.Prestart].RunHooks(s); err != nil {
return err
}
if err := hooks[configs.CreateRuntime].RunHooks(s); err != nil {
return err
}
}
// Sync with child.
if err := writeSync(p.messageSockPair.parent, procResume); err != nil {
return newSystemErrorWithCause(err, "writing syncT 'resume'")
}
sentResume = true
default:
return newSystemError(errors.New("invalid JSON payload from child"))
}
return nil
})
if !sentRun {
return newSystemErrorWithCause(ierr, "container init")
}
if p.config.Config.Namespaces.Contains(configs.NEWNS) && !sentResume {
return newSystemError(errors.New("could not synchronise after executing prestart and CreateRuntime hooks with container process"))
}
if err := unix.Shutdown(int(p.messageSockPair.parent.Fd()), unix.SHUT_WR); err != nil {
return newSystemErrorWithCause(err, "shutting down init pipe")
}
// Must be done after Shutdown so the child will exit and we can wait for it.
if ierr != nil {
p.wait()
return ierr
}
return nil
}
func (p *initProcess) wait() (*os.ProcessState, error) {
err := p.cmd.Wait()
// we should kill all processes in cgroup when init is died if we use host PID namespace
if p.sharePidns {
signalAllProcesses(p.manager, unix.SIGKILL)
}
return p.cmd.ProcessState, err
}
func (p *initProcess) terminate() error {
if p.cmd.Process == nil {
return nil
}
err := p.cmd.Process.Kill()
if _, werr := p.wait(); err == nil {
err = werr
}
return err
}
func (p *initProcess) startTime() (uint64, error) {
stat, err := system.Stat(p.pid())
return stat.StartTime, err
}
func (p *initProcess) updateSpecState() error {
s, err := p.container.currentOCIState()
if err != nil {
return err
}
p.config.SpecState = s
return nil
}
func (p *initProcess) sendConfig() error {
// send the config to the container's init process, we don't use JSON Encode
// here because there might be a problem in JSON decoder in some cases, see:
// https://github.com/docker/docker/issues/14203#issuecomment-174177790
return utils.WriteJSON(p.messageSockPair.parent, p.config)
}
func (p *initProcess) createNetworkInterfaces() error {
for _, config := range p.config.Config.Networks {
strategy, err := getStrategy(config.Type)
if err != nil {
return err
}
n := &network{
Network: *config,
}
if err := strategy.create(n, p.pid()); err != nil {
return err
}
p.config.Networks = append(p.config.Networks, n)
}
return nil
}
func (p *initProcess) signal(sig os.Signal) error {
s, ok := sig.(unix.Signal)
if !ok {
return errors.New("os: unsupported signal type")
}
return unix.Kill(p.pid(), s)
}
func (p *initProcess) setExternalDescriptors(newFds []string) {
p.fds = newFds
}
func (p *initProcess) forwardChildLogs() {
go logs.ForwardLogs(p.logFilePair.parent)
}
func getPipeFds(pid int) ([]string, error) {
fds := make([]string, 3)
dirPath := filepath.Join("/proc", strconv.Itoa(pid), "/fd")
for i := 0; i < 3; i++ {
// XXX: This breaks if the path is not a valid symlink (which can
// happen in certain particularly unlucky mount namespace setups).
f := filepath.Join(dirPath, strconv.Itoa(i))
target, err := os.Readlink(f)
if err != nil {
// Ignore permission errors, for rootless containers and other
// non-dumpable processes. if we can't get the fd for a particular
// file, there's not much we can do.
if os.IsPermission(err) {
continue
}
return fds, err
}
fds[i] = target
}
return fds, nil
}
// InitializeIO creates pipes for use with the process's stdio and returns the
// opposite side for each. Do not use this if you want to have a pseudoterminal
// set up for you by libcontainer (TODO: fix that too).
// TODO: This is mostly unnecessary, and should be handled by clients.
func (p *Process) InitializeIO(rootuid, rootgid int) (i *IO, err error) {
var fds []uintptr
i = &IO{}
// cleanup in case of an error
defer func() {
if err != nil {
for _, fd := range fds {
unix.Close(int(fd))
}
}
}()
// STDIN
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
fds = append(fds, r.Fd(), w.Fd())
p.Stdin, i.Stdin = r, w
// STDOUT
if r, w, err = os.Pipe(); err != nil {
return nil, err
}
fds = append(fds, r.Fd(), w.Fd())
p.Stdout, i.Stdout = w, r
// STDERR
if r, w, err = os.Pipe(); err != nil {
return nil, err
}
fds = append(fds, r.Fd(), w.Fd())
p.Stderr, i.Stderr = w, r
// change ownership of the pipes in case we are in a user namespace
for _, fd := range fds {
if err := unix.Fchown(int(fd), rootuid, rootgid); err != nil {
return nil, err
}
}
return i, nil
}

View File

@ -0,0 +1,129 @@
// +build linux
package libcontainer
import (
"fmt"
"os"
"os/exec"
"github.com/opencontainers/runc/libcontainer/system"
)
func newRestoredProcess(cmd *exec.Cmd, fds []string) (*restoredProcess, error) {
var (
err error
)
pid := cmd.Process.Pid
stat, err := system.Stat(pid)
if err != nil {
return nil, err
}
return &restoredProcess{
cmd: cmd,
processStartTime: stat.StartTime,
fds: fds,
}, nil
}
type restoredProcess struct {
cmd *exec.Cmd
processStartTime uint64
fds []string
}
func (p *restoredProcess) start() error {
return newGenericError(fmt.Errorf("restored process cannot be started"), SystemError)
}
func (p *restoredProcess) pid() int {
return p.cmd.Process.Pid
}
func (p *restoredProcess) terminate() error {
err := p.cmd.Process.Kill()
if _, werr := p.wait(); err == nil {
err = werr
}
return err
}
func (p *restoredProcess) wait() (*os.ProcessState, error) {
// TODO: how do we wait on the actual process?
// maybe use --exec-cmd in criu
err := p.cmd.Wait()
if err != nil {
if _, ok := err.(*exec.ExitError); !ok {
return nil, err
}
}
st := p.cmd.ProcessState
return st, nil
}
func (p *restoredProcess) startTime() (uint64, error) {
return p.processStartTime, nil
}
func (p *restoredProcess) signal(s os.Signal) error {
return p.cmd.Process.Signal(s)
}
func (p *restoredProcess) externalDescriptors() []string {
return p.fds
}
func (p *restoredProcess) setExternalDescriptors(newFds []string) {
p.fds = newFds
}
func (p *restoredProcess) forwardChildLogs() {
}
// nonChildProcess represents a process where the calling process is not
// the parent process. This process is created when a factory loads a container from
// a persisted state.
type nonChildProcess struct {
processPid int
processStartTime uint64
fds []string
}
func (p *nonChildProcess) start() error {
return newGenericError(fmt.Errorf("restored process cannot be started"), SystemError)
}
func (p *nonChildProcess) pid() int {
return p.processPid
}
func (p *nonChildProcess) terminate() error {
return newGenericError(fmt.Errorf("restored process cannot be terminated"), SystemError)
}
func (p *nonChildProcess) wait() (*os.ProcessState, error) {
return nil, newGenericError(fmt.Errorf("restored process cannot be waited on"), SystemError)
}
func (p *nonChildProcess) startTime() (uint64, error) {
return p.processStartTime, nil
}
func (p *nonChildProcess) signal(s os.Signal) error {
proc, err := os.FindProcess(p.processPid)
if err != nil {
return err
}
return proc.Signal(s)
}
func (p *nonChildProcess) externalDescriptors() []string {
return p.fds
}
func (p *nonChildProcess) setExternalDescriptors(newFds []string) {
p.fds = newFds
}
func (p *nonChildProcess) forwardChildLogs() {
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
// +build linux
package libcontainer
import (
"os"
"runtime"
"github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runc/libcontainer/keys"
"github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// linuxSetnsInit performs the container's initialization for running a new process
// inside an existing container.
type linuxSetnsInit struct {
pipe *os.File
consoleSocket *os.File
config *initConfig
}
func (l *linuxSetnsInit) getSessionRingName() string {
return "_ses." + l.config.ContainerId
}
func (l *linuxSetnsInit) Init() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if !l.config.Config.NoNewKeyring {
if err := selinux.SetKeyLabel(l.config.ProcessLabel); err != nil {
return err
}
defer selinux.SetKeyLabel("")
// Do not inherit the parent's session keyring.
if _, err := keys.JoinSessionKeyring(l.getSessionRingName()); err != nil {
// Same justification as in standart_init_linux.go as to why we
// don't bail on ENOSYS.
//
// TODO(cyphar): And we should have logging here too.
if errors.Cause(err) != unix.ENOSYS {
return errors.Wrap(err, "join session keyring")
}
}
}
if l.config.CreateConsole {
if err := setupConsole(l.consoleSocket, l.config, false); err != nil {
return err
}
if err := system.Setctty(); err != nil {
return err
}
}
if l.config.NoNewPrivileges {
if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
return err
}
}
if err := selinux.SetExecLabel(l.config.ProcessLabel); err != nil {
return err
}
defer selinux.SetExecLabel("")
// Without NoNewPrivileges seccomp is a privileged operation, so we need to
// do this before dropping capabilities; otherwise do it as late as possible
// just before execve so as few syscalls take place after it as possible.
if l.config.Config.Seccomp != nil && !l.config.NoNewPrivileges {
if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil {
return err
}
}
if err := finalizeNamespace(l.config); err != nil {
return err
}
if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
return err
}
// Set seccomp as close to execve as possible, so as few syscalls take
// place afterward (reducing the amount of syscalls that users need to
// enable in their seccomp profiles).
if l.config.Config.Seccomp != nil && l.config.NoNewPrivileges {
if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil {
return newSystemErrorWithCause(err, "init seccomp")
}
}
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
}

View File

@ -0,0 +1,27 @@
package stacktrace
import "runtime"
// Capture captures a stacktrace for the current calling go program
//
// skip is the number of frames to skip
func Capture(userSkip int) Stacktrace {
var (
skip = userSkip + 1 // add one for our own function
frames []Frame
prevPc uintptr
)
for i := skip; ; i++ {
pc, file, line, ok := runtime.Caller(i)
//detect if caller is repeated to avoid loop, gccgo
//currently runs into a loop without this check
if !ok || pc == prevPc {
break
}
frames = append(frames, NewFrame(pc, file, line))
prevPc = pc
}
return Stacktrace{
Frames: frames,
}
}

View File

@ -0,0 +1,38 @@
package stacktrace
import (
"path/filepath"
"runtime"
"strings"
)
// NewFrame returns a new stack frame for the provided information
func NewFrame(pc uintptr, file string, line int) Frame {
fn := runtime.FuncForPC(pc)
if fn == nil {
return Frame{}
}
pack, name := parseFunctionName(fn.Name())
return Frame{
Line: line,
File: filepath.Base(file),
Package: pack,
Function: name,
}
}
func parseFunctionName(name string) (string, string) {
i := strings.LastIndex(name, ".")
if i == -1 {
return "", name
}
return name[:i], name[i+1:]
}
// Frame contains all the information for a stack frame within a go program
type Frame struct {
File string
Function string
Package string
Line int
}

View File

@ -0,0 +1,5 @@
package stacktrace
type Stacktrace struct {
Frames []Frame
}

View File

@ -0,0 +1,222 @@
// +build linux
package libcontainer
import (
"os"
"os/exec"
"runtime"
"strconv"
"github.com/opencontainers/runc/libcontainer/apparmor"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/keys"
"github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
type linuxStandardInit struct {
pipe *os.File
consoleSocket *os.File
parentPid int
fifoFd int
config *initConfig
}
func (l *linuxStandardInit) getSessionRingParams() (string, uint32, uint32) {
var newperms uint32
if l.config.Config.Namespaces.Contains(configs.NEWUSER) {
// With user ns we need 'other' search permissions.
newperms = 0x8
} else {
// Without user ns we need 'UID' search permissions.
newperms = 0x80000
}
// Create a unique per session container name that we can join in setns;
// However, other containers can also join it.
return "_ses." + l.config.ContainerId, 0xffffffff, newperms
}
func (l *linuxStandardInit) Init() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if !l.config.Config.NoNewKeyring {
if err := selinux.SetKeyLabel(l.config.ProcessLabel); err != nil {
return err
}
defer selinux.SetKeyLabel("")
ringname, keepperms, newperms := l.getSessionRingParams()
// Do not inherit the parent's session keyring.
if sessKeyId, err := keys.JoinSessionKeyring(ringname); err != nil {
// If keyrings aren't supported then it is likely we are on an
// older kernel (or inside an LXC container). While we could bail,
// the security feature we are using here is best-effort (it only
// really provides marginal protection since VFS credentials are
// the only significant protection of keyrings).
//
// TODO(cyphar): Log this so people know what's going on, once we
// have proper logging in 'runc init'.
if errors.Cause(err) != unix.ENOSYS {
return errors.Wrap(err, "join session keyring")
}
} else {
// Make session keyring searcheable. If we've gotten this far we
// bail on any error -- we don't want to have a keyring with bad
// permissions.
if err := keys.ModKeyringPerm(sessKeyId, keepperms, newperms); err != nil {
return errors.Wrap(err, "mod keyring permissions")
}
}
}
if err := setupNetwork(l.config); err != nil {
return err
}
if err := setupRoute(l.config.Config); err != nil {
return err
}
// initialises the labeling system
selinux.GetEnabled()
if err := prepareRootfs(l.pipe, l.config); err != nil {
return err
}
// Set up the console. This has to be done *before* we finalize the rootfs,
// but *after* we've given the user the chance to set up all of the mounts
// they wanted.
if l.config.CreateConsole {
if err := setupConsole(l.consoleSocket, l.config, true); err != nil {
return err
}
if err := system.Setctty(); err != nil {
return errors.Wrap(err, "setctty")
}
}
// Finish the rootfs setup.
if l.config.Config.Namespaces.Contains(configs.NEWNS) {
if err := finalizeRootfs(l.config.Config); err != nil {
return err
}
}
if hostname := l.config.Config.Hostname; hostname != "" {
if err := unix.Sethostname([]byte(hostname)); err != nil {
return errors.Wrap(err, "sethostname")
}
}
if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
return errors.Wrap(err, "apply apparmor profile")
}
for key, value := range l.config.Config.Sysctl {
if err := writeSystemProperty(key, value); err != nil {
return errors.Wrapf(err, "write sysctl key %s", key)
}
}
for _, path := range l.config.Config.ReadonlyPaths {
if err := readonlyPath(path); err != nil {
return errors.Wrapf(err, "readonly path %s", path)
}
}
for _, path := range l.config.Config.MaskPaths {
if err := maskPath(path, l.config.Config.MountLabel); err != nil {
return errors.Wrapf(err, "mask path %s", path)
}
}
pdeath, err := system.GetParentDeathSignal()
if err != nil {
return errors.Wrap(err, "get pdeath signal")
}
if l.config.NoNewPrivileges {
if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
return errors.Wrap(err, "set nonewprivileges")
}
}
// Tell our parent that we're ready to Execv. This must be done before the
// Seccomp rules have been applied, because we need to be able to read and
// write to a socket.
if err := syncParentReady(l.pipe); err != nil {
return errors.Wrap(err, "sync ready")
}
if err := selinux.SetExecLabel(l.config.ProcessLabel); err != nil {
return errors.Wrap(err, "set process label")
}
defer selinux.SetExecLabel("")
// Without NoNewPrivileges seccomp is a privileged operation, so we need to
// do this before dropping capabilities; otherwise do it as late as possible
// just before execve so as few syscalls take place after it as possible.
if l.config.Config.Seccomp != nil && !l.config.NoNewPrivileges {
if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil {
return err
}
}
if err := finalizeNamespace(l.config); err != nil {
return err
}
// finalizeNamespace can change user/group which clears the parent death
// signal, so we restore it here.
if err := pdeath.Restore(); err != nil {
return errors.Wrap(err, "restore pdeath signal")
}
// Compare the parent from the initial start of the init process and make
// sure that it did not change. if the parent changes that means it died
// and we were reparented to something else so we should just kill ourself
// and not cause problems for someone else.
if unix.Getppid() != l.parentPid {
return unix.Kill(unix.Getpid(), unix.SIGKILL)
}
// Check for the arg before waiting to make sure it exists and it is
// returned as a create time error.
name, err := exec.LookPath(l.config.Args[0])
if err != nil {
return err
}
// Close the pipe to signal that we have completed our init.
l.pipe.Close()
// Wait for the FIFO to be opened on the other side before exec-ing the
// user process. We open it through /proc/self/fd/$fd, because the fd that
// was given to us was an O_PATH fd to the fifo itself. Linux allows us to
// re-open an O_PATH fd through /proc.
fd, err := unix.Open("/proc/self/fd/"+strconv.Itoa(l.fifoFd), unix.O_WRONLY|unix.O_CLOEXEC, 0)
if err != nil {
return newSystemErrorWithCause(err, "open exec fifo")
}
if _, err := unix.Write(fd, []byte("0")); err != nil {
return newSystemErrorWithCause(err, "write 0 exec fifo")
}
// Close the O_PATH fifofd fd before exec because the kernel resets
// dumpable in the wrong order. This has been fixed in newer kernels, but
// we keep this to ensure CVE-2016-9962 doesn't re-emerge on older kernels.
// N.B. the core issue itself (passing dirfds to the host filesystem) has
// since been resolved.
// https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1290-L1318
unix.Close(l.fifoFd)
// Set seccomp as close to execve as possible, so as few syscalls take
// place afterward (reducing the amount of syscalls that users need to
// enable in their seccomp profiles).
if l.config.Config.Seccomp != nil && l.config.NoNewPrivileges {
if err := seccomp.InitSeccomp(l.config.Config.Seccomp); err != nil {
return newSystemErrorWithCause(err, "init seccomp")
}
}
s := l.config.SpecState
s.Pid = unix.Getpid()
s.Status = specs.StateCreated
if err := l.config.Config.Hooks[configs.StartContainer].RunHooks(s); err != nil {
return err
}
if err := unix.Exec(name, l.config.Args[0:], os.Environ()); err != nil {
return newSystemErrorWithCause(err, "exec user process")
}
return nil
}

View File

@ -0,0 +1,245 @@
// +build linux
package libcontainer
import (
"fmt"
"os"
"path/filepath"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func newStateTransitionError(from, to containerState) error {
return &stateTransitionError{
From: from.status().String(),
To: to.status().String(),
}
}
// stateTransitionError is returned when an invalid state transition happens from one
// state to another.
type stateTransitionError struct {
From string
To string
}
func (s *stateTransitionError) Error() string {
return fmt.Sprintf("invalid state transition from %s to %s", s.From, s.To)
}
type containerState interface {
transition(containerState) error
destroy() error
status() Status
}
func destroy(c *linuxContainer) error {
if !c.config.Namespaces.Contains(configs.NEWPID) ||
c.config.Namespaces.PathOf(configs.NEWPID) != "" {
if err := signalAllProcesses(c.cgroupManager, unix.SIGKILL); err != nil {
logrus.Warn(err)
}
}
err := c.cgroupManager.Destroy()
if c.intelRdtManager != nil {
if ierr := c.intelRdtManager.Destroy(); err == nil {
err = ierr
}
}
if rerr := os.RemoveAll(c.root); err == nil {
err = rerr
}
c.initProcess = nil
if herr := runPoststopHooks(c); err == nil {
err = herr
}
c.state = &stoppedState{c: c}
return err
}
func runPoststopHooks(c *linuxContainer) error {
hooks := c.config.Hooks
if hooks == nil {
return nil
}
s, err := c.currentOCIState()
if err != nil {
return err
}
s.Status = specs.StateStopped
if err := hooks[configs.Poststop].RunHooks(s); err != nil {
return err
}
return nil
}
// stoppedState represents a container is a stopped/destroyed state.
type stoppedState struct {
c *linuxContainer
}
func (b *stoppedState) status() Status {
return Stopped
}
func (b *stoppedState) transition(s containerState) error {
switch s.(type) {
case *runningState, *restoredState:
b.c.state = s
return nil
case *stoppedState:
return nil
}
return newStateTransitionError(b, s)
}
func (b *stoppedState) destroy() error {
return destroy(b.c)
}
// runningState represents a container that is currently running.
type runningState struct {
c *linuxContainer
}
func (r *runningState) status() Status {
return Running
}
func (r *runningState) transition(s containerState) error {
switch s.(type) {
case *stoppedState:
if r.c.runType() == Running {
return newGenericError(fmt.Errorf("container still running"), ContainerNotStopped)
}
r.c.state = s
return nil
case *pausedState:
r.c.state = s
return nil
case *runningState:
return nil
}
return newStateTransitionError(r, s)
}
func (r *runningState) destroy() error {
if r.c.runType() == Running {
return newGenericError(fmt.Errorf("container is not destroyed"), ContainerNotStopped)
}
return destroy(r.c)
}
type createdState struct {
c *linuxContainer
}
func (i *createdState) status() Status {
return Created
}
func (i *createdState) transition(s containerState) error {
switch s.(type) {
case *runningState, *pausedState, *stoppedState:
i.c.state = s
return nil
case *createdState:
return nil
}
return newStateTransitionError(i, s)
}
func (i *createdState) destroy() error {
i.c.initProcess.signal(unix.SIGKILL)
return destroy(i.c)
}
// pausedState represents a container that is currently pause. It cannot be destroyed in a
// paused state and must transition back to running first.
type pausedState struct {
c *linuxContainer
}
func (p *pausedState) status() Status {
return Paused
}
func (p *pausedState) transition(s containerState) error {
switch s.(type) {
case *runningState, *stoppedState:
p.c.state = s
return nil
case *pausedState:
return nil
}
return newStateTransitionError(p, s)
}
func (p *pausedState) destroy() error {
t := p.c.runType()
if t != Running && t != Created {
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
return err
}
return destroy(p.c)
}
return newGenericError(fmt.Errorf("container is paused"), ContainerPaused)
}
// restoredState is the same as the running state but also has associated checkpoint
// information that maybe need destroyed when the container is stopped and destroy is called.
type restoredState struct {
imageDir string
c *linuxContainer
}
func (r *restoredState) status() Status {
return Running
}
func (r *restoredState) transition(s containerState) error {
switch s.(type) {
case *stoppedState, *runningState:
return nil
}
return newStateTransitionError(r, s)
}
func (r *restoredState) destroy() error {
if _, err := os.Stat(filepath.Join(r.c.root, "checkpoint")); err != nil {
if !os.IsNotExist(err) {
return err
}
}
return destroy(r.c)
}
// loadedState is used whenever a container is restored, loaded, or setting additional
// processes inside and it should not be destroyed when it is exiting.
type loadedState struct {
c *linuxContainer
s Status
}
func (n *loadedState) status() Status {
return n.s
}
func (n *loadedState) transition(s containerState) error {
n.c.state = s
return nil
}
func (n *loadedState) destroy() error {
if err := n.c.refreshState(); err != nil {
return err
}
return n.c.state.destroy()
}

View File

@ -0,0 +1,13 @@
package libcontainer
import (
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/intelrdt"
"github.com/opencontainers/runc/types"
)
type Stats struct {
Interfaces []*types.NetworkInterface
CgroupStats *cgroups.Stats
IntelRdtStats *intelrdt.Stats
}

View File

@ -0,0 +1,101 @@
package libcontainer
import (
"encoding/json"
"errors"
"fmt"
"io"
"github.com/opencontainers/runc/libcontainer/utils"
)
type syncType string
// Constants that are used for synchronisation between the parent and child
// during container setup. They come in pairs (with procError being a generic
// response which is followed by a &genericError).
//
// [ child ] <-> [ parent ]
//
// procHooks --> [run hooks]
// <-- procResume
//
// procReady --> [final setup]
// <-- procRun
const (
procError syncType = "procError"
procReady syncType = "procReady"
procRun syncType = "procRun"
procHooks syncType = "procHooks"
procResume syncType = "procResume"
)
type syncT struct {
Type syncType `json:"type"`
}
// writeSync is used to write to a synchronisation pipe. An error is returned
// if there was a problem writing the payload.
func writeSync(pipe io.Writer, sync syncType) error {
return utils.WriteJSON(pipe, syncT{sync})
}
// readSync is used to read from a synchronisation pipe. An error is returned
// if we got a genericError, the pipe was closed, or we got an unexpected flag.
func readSync(pipe io.Reader, expected syncType) error {
var procSync syncT
if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
if err == io.EOF {
return errors.New("parent closed synchronisation channel")
}
return fmt.Errorf("failed reading error from parent: %v", err)
}
if procSync.Type == procError {
var ierr genericError
if err := json.NewDecoder(pipe).Decode(&ierr); err != nil {
return fmt.Errorf("failed reading error from parent: %v", err)
}
return &ierr
}
if procSync.Type != expected {
return errors.New("invalid synchronisation flag from parent")
}
return nil
}
// parseSync runs the given callback function on each syncT received from the
// child. It will return once io.EOF is returned from the given pipe.
func parseSync(pipe io.Reader, fn func(*syncT) error) error {
dec := json.NewDecoder(pipe)
for {
var sync syncT
if err := dec.Decode(&sync); err != nil {
if err == io.EOF {
break
}
return err
}
// We handle this case outside fn for cleanliness reasons.
var ierr *genericError
if sync.Type == procError {
if err := dec.Decode(&ierr); err != nil && err != io.EOF {
return newSystemErrorWithCause(err, "decoding proc error from init")
}
if ierr != nil {
return ierr
}
// Programmer error.
panic("No error following JSON procError payload.")
}
if err := fn(&sync); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,155 @@
package types
import "github.com/opencontainers/runc/libcontainer/intelrdt"
// Event struct for encoding the event data to json.
type Event struct {
Type string `json:"type"`
ID string `json:"id"`
Data interface{} `json:"data,omitempty"`
}
// stats is the runc specific stats structure for stability when encoding and decoding stats.
type Stats struct {
CPU Cpu `json:"cpu"`
CPUSet CPUSet `json:"cpuset"`
Memory Memory `json:"memory"`
Pids Pids `json:"pids"`
Blkio Blkio `json:"blkio"`
Hugetlb map[string]Hugetlb `json:"hugetlb"`
IntelRdt IntelRdt `json:"intel_rdt"`
NetworkInterfaces []*NetworkInterface `json:"network_interfaces"`
}
type Hugetlb struct {
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
type BlkioEntry struct {
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
Op string `json:"op,omitempty"`
Value uint64 `json:"value,omitempty"`
}
type Blkio struct {
IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"`
IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"`
IoQueuedRecursive []BlkioEntry `json:"ioQueueRecursive,omitempty"`
IoServiceTimeRecursive []BlkioEntry `json:"ioServiceTimeRecursive,omitempty"`
IoWaitTimeRecursive []BlkioEntry `json:"ioWaitTimeRecursive,omitempty"`
IoMergedRecursive []BlkioEntry `json:"ioMergedRecursive,omitempty"`
IoTimeRecursive []BlkioEntry `json:"ioTimeRecursive,omitempty"`
SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"`
}
type Pids struct {
Current uint64 `json:"current,omitempty"`
Limit uint64 `json:"limit,omitempty"`
}
type Throttling struct {
Periods uint64 `json:"periods,omitempty"`
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
ThrottledTime uint64 `json:"throttledTime,omitempty"`
}
type CpuUsage struct {
// Units: nanoseconds.
Total uint64 `json:"total,omitempty"`
Percpu []uint64 `json:"percpu,omitempty"`
PercpuKernel []uint64 `json:"percpu_kernel,omitempty"`
PercpuUser []uint64 `json:"percpu_user,omitempty"`
Kernel uint64 `json:"kernel"`
User uint64 `json:"user"`
}
type Cpu struct {
Usage CpuUsage `json:"usage,omitempty"`
Throttling Throttling `json:"throttling,omitempty"`
}
type CPUSet struct {
CPUs []uint16 `json:"cpus,omitempty"`
CPUExclusive uint64 `json:"cpu_exclusive"`
Mems []uint16 `json:"mems,omitempty"`
MemHardwall uint64 `json:"mem_hardwall"`
MemExclusive uint64 `json:"mem_exclusive"`
MemoryMigrate uint64 `json:"memory_migrate"`
MemorySpreadPage uint64 `json:"memory_spread_page"`
MemorySpreadSlab uint64 `json:"memory_spread_slab"`
MemoryPressure uint64 `json:"memory_pressure"`
SchedLoadBalance uint64 `json:"sched_load_balance"`
SchedRelaxDomainLevel int64 `json:"sched_relax_domain_level"`
}
type MemoryEntry struct {
Limit uint64 `json:"limit"`
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
type Memory struct {
Cache uint64 `json:"cache,omitempty"`
Usage MemoryEntry `json:"usage,omitempty"`
Swap MemoryEntry `json:"swap,omitempty"`
Kernel MemoryEntry `json:"kernel,omitempty"`
KernelTCP MemoryEntry `json:"kernelTCP,omitempty"`
Raw map[string]uint64 `json:"raw,omitempty"`
}
type L3CacheInfo struct {
CbmMask string `json:"cbm_mask,omitempty"`
MinCbmBits uint64 `json:"min_cbm_bits,omitempty"`
NumClosids uint64 `json:"num_closids,omitempty"`
}
type MemBwInfo struct {
BandwidthGran uint64 `json:"bandwidth_gran,omitempty"`
DelayLinear uint64 `json:"delay_linear,omitempty"`
MinBandwidth uint64 `json:"min_bandwidth,omitempty"`
NumClosids uint64 `json:"num_closids,omitempty"`
}
type IntelRdt struct {
// The read-only L3 cache information
L3CacheInfo *L3CacheInfo `json:"l3_cache_info,omitempty"`
// The read-only L3 cache schema in root
L3CacheSchemaRoot string `json:"l3_cache_schema_root,omitempty"`
// The L3 cache schema in 'container_id' group
L3CacheSchema string `json:"l3_cache_schema,omitempty"`
// The read-only memory bandwidth information
MemBwInfo *MemBwInfo `json:"mem_bw_info,omitempty"`
// The read-only memory bandwidth schema in root
MemBwSchemaRoot string `json:"mem_bw_schema_root,omitempty"`
// The memory bandwidth schema in 'container_id' group
MemBwSchema string `json:"mem_bw_schema,omitempty"`
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
MBMStats *[]intelrdt.MBMNumaNodeStats `json:"mbm_stats,omitempty"`
// The cache monitoring technology statistics from NUMA nodes in 'container_id' group
CMTStats *[]intelrdt.CMTNumaNodeStats `json:"cmt_stats,omitempty"`
}
type NetworkInterface struct {
// Name is the name of the network interface.
Name string
RxBytes uint64
RxPackets uint64
RxErrors uint64
RxDropped uint64
TxBytes uint64
TxPackets uint64
TxErrors uint64
TxDropped uint64
}

View File

@ -0,0 +1,24 @@
Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,133 @@
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package capability provides utilities for manipulating POSIX capabilities.
package capability
type Capabilities interface {
// Get check whether a capability present in the given
// capabilities set. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Get(which CapType, what Cap) bool
// Empty check whether all capability bits of the given capabilities
// set are zero. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Empty(which CapType) bool
// Full check whether all capability bits of the given capabilities
// set are one. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Full(which CapType) bool
// Set sets capabilities of the given capabilities sets. The
// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Set(which CapType, caps ...Cap)
// Unset unsets capabilities of the given capabilities sets. The
// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
// PERMITTED, INHERITABLE, BOUNDING or AMBIENT.
Unset(which CapType, caps ...Cap)
// Fill sets all bits of the given capabilities kind to one. The
// 'kind' value should be one or combination (OR'ed) of CAPS,
// BOUNDS or AMBS.
Fill(kind CapType)
// Clear sets all bits of the given capabilities kind to zero. The
// 'kind' value should be one or combination (OR'ed) of CAPS,
// BOUNDS or AMBS.
Clear(kind CapType)
// String return current capabilities state of the given capabilities
// set as string. The 'which' value should be one of EFFECTIVE,
// PERMITTED, INHERITABLE BOUNDING or AMBIENT
StringCap(which CapType) string
// String return current capabilities state as string.
String() string
// Load load actual capabilities value. This will overwrite all
// outstanding changes.
Load() error
// Apply apply the capabilities settings, so all changes will take
// effect.
Apply(kind CapType) error
}
// NewPid initializes a new Capabilities object for given pid when
// it is nonzero, or for the current process if pid is 0.
//
// Deprecated: Replace with NewPid2. For example, replace:
//
// c, err := NewPid(0)
// if err != nil {
// return err
// }
//
// with:
//
// c, err := NewPid2(0)
// if err != nil {
// return err
// }
// err = c.Load()
// if err != nil {
// return err
// }
func NewPid(pid int) (Capabilities, error) {
c, err := newPid(pid)
if err != nil {
return c, err
}
err = c.Load()
return c, err
}
// NewPid2 initializes a new Capabilities object for given pid when
// it is nonzero, or for the current process if pid is 0. This
// does not load the process's current capabilities; to do that you
// must call Load explicitly.
func NewPid2(pid int) (Capabilities, error) {
return newPid(pid)
}
// NewFile initializes a new Capabilities object for given file path.
//
// Deprecated: Replace with NewFile2. For example, replace:
//
// c, err := NewFile(path)
// if err != nil {
// return err
// }
//
// with:
//
// c, err := NewFile2(path)
// if err != nil {
// return err
// }
// err = c.Load()
// if err != nil {
// return err
// }
func NewFile(path string) (Capabilities, error) {
c, err := newFile(path)
if err != nil {
return c, err
}
err = c.Load()
return c, err
}
// NewFile2 creates a new initialized Capabilities object for given
// file path. This does not load the process's current capabilities;
// to do that you must call Load explicitly.
func NewFile2(path string) (Capabilities, error) {
return newFile(path)
}

View File

@ -0,0 +1,642 @@
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package capability
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strings"
"syscall"
)
var errUnknownVers = errors.New("unknown capability version")
const (
linuxCapVer1 = 0x19980330
linuxCapVer2 = 0x20071026
linuxCapVer3 = 0x20080522
)
var (
capVers uint32
capLastCap Cap
)
func init() {
var hdr capHeader
capget(&hdr, nil)
capVers = hdr.version
if initLastCap() == nil {
CAP_LAST_CAP = capLastCap
if capLastCap > 31 {
capUpperMask = (uint32(1) << (uint(capLastCap) - 31)) - 1
} else {
capUpperMask = 0
}
}
}
func initLastCap() error {
if capLastCap != 0 {
return nil
}
f, err := os.Open("/proc/sys/kernel/cap_last_cap")
if err != nil {
return err
}
defer f.Close()
var b []byte = make([]byte, 11)
_, err = f.Read(b)
if err != nil {
return err
}
fmt.Sscanf(string(b), "%d", &capLastCap)
return nil
}
func mkStringCap(c Capabilities, which CapType) (ret string) {
for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ {
if !c.Get(which, i) {
continue
}
if first {
first = false
} else {
ret += ", "
}
ret += i.String()
}
return
}
func mkString(c Capabilities, max CapType) (ret string) {
ret = "{"
for i := CapType(1); i <= max; i <<= 1 {
ret += " " + i.String() + "=\""
if c.Empty(i) {
ret += "empty"
} else if c.Full(i) {
ret += "full"
} else {
ret += c.StringCap(i)
}
ret += "\""
}
ret += " }"
return
}
func newPid(pid int) (c Capabilities, err error) {
switch capVers {
case linuxCapVer1:
p := new(capsV1)
p.hdr.version = capVers
p.hdr.pid = int32(pid)
c = p
case linuxCapVer2, linuxCapVer3:
p := new(capsV3)
p.hdr.version = capVers
p.hdr.pid = int32(pid)
c = p
default:
err = errUnknownVers
return
}
return
}
type capsV1 struct {
hdr capHeader
data capData
}
func (c *capsV1) Get(which CapType, what Cap) bool {
if what > 32 {
return false
}
switch which {
case EFFECTIVE:
return (1<<uint(what))&c.data.effective != 0
case PERMITTED:
return (1<<uint(what))&c.data.permitted != 0
case INHERITABLE:
return (1<<uint(what))&c.data.inheritable != 0
}
return false
}
func (c *capsV1) getData(which CapType) (ret uint32) {
switch which {
case EFFECTIVE:
ret = c.data.effective
case PERMITTED:
ret = c.data.permitted
case INHERITABLE:
ret = c.data.inheritable
}
return
}
func (c *capsV1) Empty(which CapType) bool {
return c.getData(which) == 0
}
func (c *capsV1) Full(which CapType) bool {
return (c.getData(which) & 0x7fffffff) == 0x7fffffff
}
func (c *capsV1) Set(which CapType, caps ...Cap) {
for _, what := range caps {
if what > 32 {
continue
}
if which&EFFECTIVE != 0 {
c.data.effective |= 1 << uint(what)
}
if which&PERMITTED != 0 {
c.data.permitted |= 1 << uint(what)
}
if which&INHERITABLE != 0 {
c.data.inheritable |= 1 << uint(what)
}
}
}
func (c *capsV1) Unset(which CapType, caps ...Cap) {
for _, what := range caps {
if what > 32 {
continue
}
if which&EFFECTIVE != 0 {
c.data.effective &= ^(1 << uint(what))
}
if which&PERMITTED != 0 {
c.data.permitted &= ^(1 << uint(what))
}
if which&INHERITABLE != 0 {
c.data.inheritable &= ^(1 << uint(what))
}
}
}
func (c *capsV1) Fill(kind CapType) {
if kind&CAPS == CAPS {
c.data.effective = 0x7fffffff
c.data.permitted = 0x7fffffff
c.data.inheritable = 0
}
}
func (c *capsV1) Clear(kind CapType) {
if kind&CAPS == CAPS {
c.data.effective = 0
c.data.permitted = 0
c.data.inheritable = 0
}
}
func (c *capsV1) StringCap(which CapType) (ret string) {
return mkStringCap(c, which)
}
func (c *capsV1) String() (ret string) {
return mkString(c, BOUNDING)
}
func (c *capsV1) Load() (err error) {
return capget(&c.hdr, &c.data)
}
func (c *capsV1) Apply(kind CapType) error {
if kind&CAPS == CAPS {
return capset(&c.hdr, &c.data)
}
return nil
}
type capsV3 struct {
hdr capHeader
data [2]capData
bounds [2]uint32
ambient [2]uint32
}
func (c *capsV3) Get(which CapType, what Cap) bool {
var i uint
if what > 31 {
i = uint(what) >> 5
what %= 32
}
switch which {
case EFFECTIVE:
return (1<<uint(what))&c.data[i].effective != 0
case PERMITTED:
return (1<<uint(what))&c.data[i].permitted != 0
case INHERITABLE:
return (1<<uint(what))&c.data[i].inheritable != 0
case BOUNDING:
return (1<<uint(what))&c.bounds[i] != 0
case AMBIENT:
return (1<<uint(what))&c.ambient[i] != 0
}
return false
}
func (c *capsV3) getData(which CapType, dest []uint32) {
switch which {
case EFFECTIVE:
dest[0] = c.data[0].effective
dest[1] = c.data[1].effective
case PERMITTED:
dest[0] = c.data[0].permitted
dest[1] = c.data[1].permitted
case INHERITABLE:
dest[0] = c.data[0].inheritable
dest[1] = c.data[1].inheritable
case BOUNDING:
dest[0] = c.bounds[0]
dest[1] = c.bounds[1]
case AMBIENT:
dest[0] = c.ambient[0]
dest[1] = c.ambient[1]
}
}
func (c *capsV3) Empty(which CapType) bool {
var data [2]uint32
c.getData(which, data[:])
return data[0] == 0 && data[1] == 0
}
func (c *capsV3) Full(which CapType) bool {
var data [2]uint32
c.getData(which, data[:])
if (data[0] & 0xffffffff) != 0xffffffff {
return false
}
return (data[1] & capUpperMask) == capUpperMask
}
func (c *capsV3) Set(which CapType, caps ...Cap) {
for _, what := range caps {
var i uint
if what > 31 {
i = uint(what) >> 5
what %= 32
}
if which&EFFECTIVE != 0 {
c.data[i].effective |= 1 << uint(what)
}
if which&PERMITTED != 0 {
c.data[i].permitted |= 1 << uint(what)
}
if which&INHERITABLE != 0 {
c.data[i].inheritable |= 1 << uint(what)
}
if which&BOUNDING != 0 {
c.bounds[i] |= 1 << uint(what)
}
if which&AMBIENT != 0 {
c.ambient[i] |= 1 << uint(what)
}
}
}
func (c *capsV3) Unset(which CapType, caps ...Cap) {
for _, what := range caps {
var i uint
if what > 31 {
i = uint(what) >> 5
what %= 32
}
if which&EFFECTIVE != 0 {
c.data[i].effective &= ^(1 << uint(what))
}
if which&PERMITTED != 0 {
c.data[i].permitted &= ^(1 << uint(what))
}
if which&INHERITABLE != 0 {
c.data[i].inheritable &= ^(1 << uint(what))
}
if which&BOUNDING != 0 {
c.bounds[i] &= ^(1 << uint(what))
}
if which&AMBIENT != 0 {
c.ambient[i] &= ^(1 << uint(what))
}
}
}
func (c *capsV3) Fill(kind CapType) {
if kind&CAPS == CAPS {
c.data[0].effective = 0xffffffff
c.data[0].permitted = 0xffffffff
c.data[0].inheritable = 0
c.data[1].effective = 0xffffffff
c.data[1].permitted = 0xffffffff
c.data[1].inheritable = 0
}
if kind&BOUNDS == BOUNDS {
c.bounds[0] = 0xffffffff
c.bounds[1] = 0xffffffff
}
if kind&AMBS == AMBS {
c.ambient[0] = 0xffffffff
c.ambient[1] = 0xffffffff
}
}
func (c *capsV3) Clear(kind CapType) {
if kind&CAPS == CAPS {
c.data[0].effective = 0
c.data[0].permitted = 0
c.data[0].inheritable = 0
c.data[1].effective = 0
c.data[1].permitted = 0
c.data[1].inheritable = 0
}
if kind&BOUNDS == BOUNDS {
c.bounds[0] = 0
c.bounds[1] = 0
}
if kind&AMBS == AMBS {
c.ambient[0] = 0
c.ambient[1] = 0
}
}
func (c *capsV3) StringCap(which CapType) (ret string) {
return mkStringCap(c, which)
}
func (c *capsV3) String() (ret string) {
return mkString(c, BOUNDING)
}
func (c *capsV3) Load() (err error) {
err = capget(&c.hdr, &c.data[0])
if err != nil {
return
}
var status_path string
if c.hdr.pid == 0 {
status_path = fmt.Sprintf("/proc/self/status")
} else {
status_path = fmt.Sprintf("/proc/%d/status", c.hdr.pid)
}
f, err := os.Open(status_path)
if err != nil {
return
}
b := bufio.NewReader(f)
for {
line, e := b.ReadString('\n')
if e != nil {
if e != io.EOF {
err = e
}
break
}
if strings.HasPrefix(line, "CapB") {
fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0])
continue
}
if strings.HasPrefix(line, "CapA") {
fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0])
continue
}
}
f.Close()
return
}
func (c *capsV3) Apply(kind CapType) (err error) {
if kind&BOUNDS == BOUNDS {
var data [2]capData
err = capget(&c.hdr, &data[0])
if err != nil {
return
}
if (1<<uint(CAP_SETPCAP))&data[0].effective != 0 {
for i := Cap(0); i <= CAP_LAST_CAP; i++ {
if c.Get(BOUNDING, i) {
continue
}
err = prctl(syscall.PR_CAPBSET_DROP, uintptr(i), 0, 0, 0)
if err != nil {
// Ignore EINVAL since the capability may not be supported in this system.
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINVAL {
err = nil
continue
}
return
}
}
}
}
if kind&CAPS == CAPS {
err = capset(&c.hdr, &c.data[0])
if err != nil {
return
}
}
if kind&AMBS == AMBS {
for i := Cap(0); i <= CAP_LAST_CAP; i++ {
action := pr_CAP_AMBIENT_LOWER
if c.Get(AMBIENT, i) {
action = pr_CAP_AMBIENT_RAISE
}
err := prctl(pr_CAP_AMBIENT, action, uintptr(i), 0, 0)
// Ignore EINVAL as not supported on kernels before 4.3
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINVAL {
err = nil
continue
}
}
}
return
}
func newFile(path string) (c Capabilities, err error) {
c = &capsFile{path: path}
return
}
type capsFile struct {
path string
data vfscapData
}
func (c *capsFile) Get(which CapType, what Cap) bool {
var i uint
if what > 31 {
if c.data.version == 1 {
return false
}
i = uint(what) >> 5
what %= 32
}
switch which {
case EFFECTIVE:
return (1<<uint(what))&c.data.effective[i] != 0
case PERMITTED:
return (1<<uint(what))&c.data.data[i].permitted != 0
case INHERITABLE:
return (1<<uint(what))&c.data.data[i].inheritable != 0
}
return false
}
func (c *capsFile) getData(which CapType, dest []uint32) {
switch which {
case EFFECTIVE:
dest[0] = c.data.effective[0]
dest[1] = c.data.effective[1]
case PERMITTED:
dest[0] = c.data.data[0].permitted
dest[1] = c.data.data[1].permitted
case INHERITABLE:
dest[0] = c.data.data[0].inheritable
dest[1] = c.data.data[1].inheritable
}
}
func (c *capsFile) Empty(which CapType) bool {
var data [2]uint32
c.getData(which, data[:])
return data[0] == 0 && data[1] == 0
}
func (c *capsFile) Full(which CapType) bool {
var data [2]uint32
c.getData(which, data[:])
if c.data.version == 0 {
return (data[0] & 0x7fffffff) == 0x7fffffff
}
if (data[0] & 0xffffffff) != 0xffffffff {
return false
}
return (data[1] & capUpperMask) == capUpperMask
}
func (c *capsFile) Set(which CapType, caps ...Cap) {
for _, what := range caps {
var i uint
if what > 31 {
if c.data.version == 1 {
continue
}
i = uint(what) >> 5
what %= 32
}
if which&EFFECTIVE != 0 {
c.data.effective[i] |= 1 << uint(what)
}
if which&PERMITTED != 0 {
c.data.data[i].permitted |= 1 << uint(what)
}
if which&INHERITABLE != 0 {
c.data.data[i].inheritable |= 1 << uint(what)
}
}
}
func (c *capsFile) Unset(which CapType, caps ...Cap) {
for _, what := range caps {
var i uint
if what > 31 {
if c.data.version == 1 {
continue
}
i = uint(what) >> 5
what %= 32
}
if which&EFFECTIVE != 0 {
c.data.effective[i] &= ^(1 << uint(what))
}
if which&PERMITTED != 0 {
c.data.data[i].permitted &= ^(1 << uint(what))
}
if which&INHERITABLE != 0 {
c.data.data[i].inheritable &= ^(1 << uint(what))
}
}
}
func (c *capsFile) Fill(kind CapType) {
if kind&CAPS == CAPS {
c.data.effective[0] = 0xffffffff
c.data.data[0].permitted = 0xffffffff
c.data.data[0].inheritable = 0
if c.data.version == 2 {
c.data.effective[1] = 0xffffffff
c.data.data[1].permitted = 0xffffffff
c.data.data[1].inheritable = 0
}
}
}
func (c *capsFile) Clear(kind CapType) {
if kind&CAPS == CAPS {
c.data.effective[0] = 0
c.data.data[0].permitted = 0
c.data.data[0].inheritable = 0
if c.data.version == 2 {
c.data.effective[1] = 0
c.data.data[1].permitted = 0
c.data.data[1].inheritable = 0
}
}
}
func (c *capsFile) StringCap(which CapType) (ret string) {
return mkStringCap(c, which)
}
func (c *capsFile) String() (ret string) {
return mkString(c, INHERITABLE)
}
func (c *capsFile) Load() (err error) {
return getVfsCap(c.path, &c.data)
}
func (c *capsFile) Apply(kind CapType) (err error) {
if kind&CAPS == CAPS {
return setVfsCap(c.path, &c.data)
}
return
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// +build !linux
package capability
import "errors"
func newPid(pid int) (Capabilities, error) {
return nil, errors.New("not supported")
}
func newFile(path string) (Capabilities, error) {
return nil, errors.New("not supported")
}

View File

@ -0,0 +1,309 @@
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package capability
type CapType uint
func (c CapType) String() string {
switch c {
case EFFECTIVE:
return "effective"
case PERMITTED:
return "permitted"
case INHERITABLE:
return "inheritable"
case BOUNDING:
return "bounding"
case CAPS:
return "caps"
case AMBIENT:
return "ambient"
}
return "unknown"
}
const (
EFFECTIVE CapType = 1 << iota
PERMITTED
INHERITABLE
BOUNDING
AMBIENT
CAPS = EFFECTIVE | PERMITTED | INHERITABLE
BOUNDS = BOUNDING
AMBS = AMBIENT
)
//go:generate go run enumgen/gen.go
type Cap int
// POSIX-draft defined capabilities and Linux extensions.
//
// Defined in https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
const (
// In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
// overrides the restriction of changing file ownership and group
// ownership.
CAP_CHOWN = Cap(0)
// Override all DAC access, including ACL execute access if
// [_POSIX_ACL] is defined. Excluding DAC access covered by
// CAP_LINUX_IMMUTABLE.
CAP_DAC_OVERRIDE = Cap(1)
// Overrides all DAC restrictions regarding read and search on files
// and directories, including ACL restrictions if [_POSIX_ACL] is
// defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE.
CAP_DAC_READ_SEARCH = Cap(2)
// Overrides all restrictions about allowed operations on files, where
// file owner ID must be equal to the user ID, except where CAP_FSETID
// is applicable. It doesn't override MAC and DAC restrictions.
CAP_FOWNER = Cap(3)
// Overrides the following restrictions that the effective user ID
// shall match the file owner ID when setting the S_ISUID and S_ISGID
// bits on that file; that the effective group ID (or one of the
// supplementary group IDs) shall match the file owner ID when setting
// the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are
// cleared on successful return from chown(2) (not implemented).
CAP_FSETID = Cap(4)
// Overrides the restriction that the real or effective user ID of a
// process sending a signal must match the real or effective user ID
// of the process receiving the signal.
CAP_KILL = Cap(5)
// Allows setgid(2) manipulation
// Allows setgroups(2)
// Allows forged gids on socket credentials passing.
CAP_SETGID = Cap(6)
// Allows set*uid(2) manipulation (including fsuid).
// Allows forged pids on socket credentials passing.
CAP_SETUID = Cap(7)
// Linux-specific capabilities
// Without VFS support for capabilities:
// Transfer any capability in your permitted set to any pid,
// remove any capability in your permitted set from any pid
// With VFS support for capabilities (neither of above, but)
// Add any capability from current's capability bounding set
// to the current process' inheritable set
// Allow taking bits out of capability bounding set
// Allow modification of the securebits for a process
CAP_SETPCAP = Cap(8)
// Allow modification of S_IMMUTABLE and S_APPEND file attributes
CAP_LINUX_IMMUTABLE = Cap(9)
// Allows binding to TCP/UDP sockets below 1024
// Allows binding to ATM VCIs below 32
CAP_NET_BIND_SERVICE = Cap(10)
// Allow broadcasting, listen to multicast
CAP_NET_BROADCAST = Cap(11)
// Allow interface configuration
// Allow administration of IP firewall, masquerading and accounting
// Allow setting debug option on sockets
// Allow modification of routing tables
// Allow setting arbitrary process / process group ownership on
// sockets
// Allow binding to any address for transparent proxying (also via NET_RAW)
// Allow setting TOS (type of service)
// Allow setting promiscuous mode
// Allow clearing driver statistics
// Allow multicasting
// Allow read/write of device-specific registers
// Allow activation of ATM control sockets
CAP_NET_ADMIN = Cap(12)
// Allow use of RAW sockets
// Allow use of PACKET sockets
// Allow binding to any address for transparent proxying (also via NET_ADMIN)
CAP_NET_RAW = Cap(13)
// Allow locking of shared memory segments
// Allow mlock and mlockall (which doesn't really have anything to do
// with IPC)
CAP_IPC_LOCK = Cap(14)
// Override IPC ownership checks
CAP_IPC_OWNER = Cap(15)
// Insert and remove kernel modules - modify kernel without limit
CAP_SYS_MODULE = Cap(16)
// Allow ioperm/iopl access
// Allow sending USB messages to any device via /proc/bus/usb
CAP_SYS_RAWIO = Cap(17)
// Allow use of chroot()
CAP_SYS_CHROOT = Cap(18)
// Allow ptrace() of any process
CAP_SYS_PTRACE = Cap(19)
// Allow configuration of process accounting
CAP_SYS_PACCT = Cap(20)
// Allow configuration of the secure attention key
// Allow administration of the random device
// Allow examination and configuration of disk quotas
// Allow setting the domainname
// Allow setting the hostname
// Allow calling bdflush()
// Allow mount() and umount(), setting up new smb connection
// Allow some autofs root ioctls
// Allow nfsservctl
// Allow VM86_REQUEST_IRQ
// Allow to read/write pci config on alpha
// Allow irix_prctl on mips (setstacksize)
// Allow flushing all cache on m68k (sys_cacheflush)
// Allow removing semaphores
// Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores
// and shared memory
// Allow locking/unlocking of shared memory segment
// Allow turning swap on/off
// Allow forged pids on socket credentials passing
// Allow setting readahead and flushing buffers on block devices
// Allow setting geometry in floppy driver
// Allow turning DMA on/off in xd driver
// Allow administration of md devices (mostly the above, but some
// extra ioctls)
// Allow tuning the ide driver
// Allow access to the nvram device
// Allow administration of apm_bios, serial and bttv (TV) device
// Allow manufacturer commands in isdn CAPI support driver
// Allow reading non-standardized portions of pci configuration space
// Allow DDI debug ioctl on sbpcd driver
// Allow setting up serial ports
// Allow sending raw qic-117 commands
// Allow enabling/disabling tagged queuing on SCSI controllers and sending
// arbitrary SCSI commands
// Allow setting encryption key on loopback filesystem
// Allow setting zone reclaim policy
// Allow everything under CAP_BPF and CAP_PERFMON for backward compatibility
CAP_SYS_ADMIN = Cap(21)
// Allow use of reboot()
CAP_SYS_BOOT = Cap(22)
// Allow raising priority and setting priority on other (different
// UID) processes
// Allow use of FIFO and round-robin (realtime) scheduling on own
// processes and setting the scheduling algorithm used by another
// process.
// Allow setting cpu affinity on other processes
CAP_SYS_NICE = Cap(23)
// Override resource limits. Set resource limits.
// Override quota limits.
// Override reserved space on ext2 filesystem
// Modify data journaling mode on ext3 filesystem (uses journaling
// resources)
// NOTE: ext2 honors fsuid when checking for resource overrides, so
// you can override using fsuid too
// Override size restrictions on IPC message queues
// Allow more than 64hz interrupts from the real-time clock
// Override max number of consoles on console allocation
// Override max number of keymaps
// Control memory reclaim behavior
CAP_SYS_RESOURCE = Cap(24)
// Allow manipulation of system clock
// Allow irix_stime on mips
// Allow setting the real-time clock
CAP_SYS_TIME = Cap(25)
// Allow configuration of tty devices
// Allow vhangup() of tty
CAP_SYS_TTY_CONFIG = Cap(26)
// Allow the privileged aspects of mknod()
CAP_MKNOD = Cap(27)
// Allow taking of leases on files
CAP_LEASE = Cap(28)
CAP_AUDIT_WRITE = Cap(29)
CAP_AUDIT_CONTROL = Cap(30)
CAP_SETFCAP = Cap(31)
// Override MAC access.
// The base kernel enforces no MAC policy.
// An LSM may enforce a MAC policy, and if it does and it chooses
// to implement capability based overrides of that policy, this is
// the capability it should use to do so.
CAP_MAC_OVERRIDE = Cap(32)
// Allow MAC configuration or state changes.
// The base kernel requires no MAC configuration.
// An LSM may enforce a MAC policy, and if it does and it chooses
// to implement capability based checks on modifications to that
// policy or the data required to maintain it, this is the
// capability it should use to do so.
CAP_MAC_ADMIN = Cap(33)
// Allow configuring the kernel's syslog (printk behaviour)
CAP_SYSLOG = Cap(34)
// Allow triggering something that will wake the system
CAP_WAKE_ALARM = Cap(35)
// Allow preventing system suspends
CAP_BLOCK_SUSPEND = Cap(36)
// Allow reading the audit log via multicast netlink socket
CAP_AUDIT_READ = Cap(37)
// Allow system performance and observability privileged operations
// using perf_events, i915_perf and other kernel subsystems
CAP_PERFMON = Cap(38)
// CAP_BPF allows the following BPF operations:
// - Creating all types of BPF maps
// - Advanced verifier features
// - Indirect variable access
// - Bounded loops
// - BPF to BPF function calls
// - Scalar precision tracking
// - Larger complexity limits
// - Dead code elimination
// - And potentially other features
// - Loading BPF Type Format (BTF) data
// - Retrieve xlated and JITed code of BPF programs
// - Use bpf_spin_lock() helper
//
// CAP_PERFMON relaxes the verifier checks further:
// - BPF progs can use of pointer-to-integer conversions
// - speculation attack hardening measures are bypassed
// - bpf_probe_read to read arbitrary kernel memory is allowed
// - bpf_trace_printk to print kernel memory is allowed
//
// CAP_SYS_ADMIN is required to use bpf_probe_write_user.
//
// CAP_SYS_ADMIN is required to iterate system wide loaded
// programs, maps, links, BTFs and convert their IDs to file descriptors.
//
// CAP_PERFMON and CAP_BPF are required to load tracing programs.
// CAP_NET_ADMIN and CAP_BPF are required to load networking programs.
CAP_BPF = Cap(39)
// Allow checkpoint/restore related operations.
// Introduced in kernel 5.9
CAP_CHECKPOINT_RESTORE = Cap(40)
)
var (
// Highest valid capability of the running kernel.
CAP_LAST_CAP = Cap(63)
capUpperMask = ^uint32(0)
)

View File

@ -0,0 +1,138 @@
// generated file; DO NOT EDIT - use go generate in directory with source
package capability
func (c Cap) String() string {
switch c {
case CAP_CHOWN:
return "chown"
case CAP_DAC_OVERRIDE:
return "dac_override"
case CAP_DAC_READ_SEARCH:
return "dac_read_search"
case CAP_FOWNER:
return "fowner"
case CAP_FSETID:
return "fsetid"
case CAP_KILL:
return "kill"
case CAP_SETGID:
return "setgid"
case CAP_SETUID:
return "setuid"
case CAP_SETPCAP:
return "setpcap"
case CAP_LINUX_IMMUTABLE:
return "linux_immutable"
case CAP_NET_BIND_SERVICE:
return "net_bind_service"
case CAP_NET_BROADCAST:
return "net_broadcast"
case CAP_NET_ADMIN:
return "net_admin"
case CAP_NET_RAW:
return "net_raw"
case CAP_IPC_LOCK:
return "ipc_lock"
case CAP_IPC_OWNER:
return "ipc_owner"
case CAP_SYS_MODULE:
return "sys_module"
case CAP_SYS_RAWIO:
return "sys_rawio"
case CAP_SYS_CHROOT:
return "sys_chroot"
case CAP_SYS_PTRACE:
return "sys_ptrace"
case CAP_SYS_PACCT:
return "sys_pacct"
case CAP_SYS_ADMIN:
return "sys_admin"
case CAP_SYS_BOOT:
return "sys_boot"
case CAP_SYS_NICE:
return "sys_nice"
case CAP_SYS_RESOURCE:
return "sys_resource"
case CAP_SYS_TIME:
return "sys_time"
case CAP_SYS_TTY_CONFIG:
return "sys_tty_config"
case CAP_MKNOD:
return "mknod"
case CAP_LEASE:
return "lease"
case CAP_AUDIT_WRITE:
return "audit_write"
case CAP_AUDIT_CONTROL:
return "audit_control"
case CAP_SETFCAP:
return "setfcap"
case CAP_MAC_OVERRIDE:
return "mac_override"
case CAP_MAC_ADMIN:
return "mac_admin"
case CAP_SYSLOG:
return "syslog"
case CAP_WAKE_ALARM:
return "wake_alarm"
case CAP_BLOCK_SUSPEND:
return "block_suspend"
case CAP_AUDIT_READ:
return "audit_read"
case CAP_PERFMON:
return "perfmon"
case CAP_BPF:
return "bpf"
case CAP_CHECKPOINT_RESTORE:
return "checkpoint_restore"
}
return "unknown"
}
// List returns list of all supported capabilities
func List() []Cap {
return []Cap{
CAP_CHOWN,
CAP_DAC_OVERRIDE,
CAP_DAC_READ_SEARCH,
CAP_FOWNER,
CAP_FSETID,
CAP_KILL,
CAP_SETGID,
CAP_SETUID,
CAP_SETPCAP,
CAP_LINUX_IMMUTABLE,
CAP_NET_BIND_SERVICE,
CAP_NET_BROADCAST,
CAP_NET_ADMIN,
CAP_NET_RAW,
CAP_IPC_LOCK,
CAP_IPC_OWNER,
CAP_SYS_MODULE,
CAP_SYS_RAWIO,
CAP_SYS_CHROOT,
CAP_SYS_PTRACE,
CAP_SYS_PACCT,
CAP_SYS_ADMIN,
CAP_SYS_BOOT,
CAP_SYS_NICE,
CAP_SYS_RESOURCE,
CAP_SYS_TIME,
CAP_SYS_TTY_CONFIG,
CAP_MKNOD,
CAP_LEASE,
CAP_AUDIT_WRITE,
CAP_AUDIT_CONTROL,
CAP_SETFCAP,
CAP_MAC_OVERRIDE,
CAP_MAC_ADMIN,
CAP_SYSLOG,
CAP_WAKE_ALARM,
CAP_BLOCK_SUSPEND,
CAP_AUDIT_READ,
CAP_PERFMON,
CAP_BPF,
CAP_CHECKPOINT_RESTORE,
}
}

View File

@ -0,0 +1,154 @@
// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package capability
import (
"syscall"
"unsafe"
)
type capHeader struct {
version uint32
pid int32
}
type capData struct {
effective uint32
permitted uint32
inheritable uint32
}
func capget(hdr *capHeader, data *capData) (err error) {
_, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 {
err = e1
}
return
}
func capset(hdr *capHeader, data *capData) (err error) {
_, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 {
err = e1
}
return
}
// not yet in syscall
const (
pr_CAP_AMBIENT = 47
pr_CAP_AMBIENT_IS_SET = uintptr(1)
pr_CAP_AMBIENT_RAISE = uintptr(2)
pr_CAP_AMBIENT_LOWER = uintptr(3)
pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4)
)
func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
if e1 != 0 {
err = e1
}
return
}
const (
vfsXattrName = "security.capability"
vfsCapVerMask = 0xff000000
vfsCapVer1 = 0x01000000
vfsCapVer2 = 0x02000000
vfsCapFlagMask = ^vfsCapVerMask
vfsCapFlageffective = 0x000001
vfscapDataSizeV1 = 4 * (1 + 2*1)
vfscapDataSizeV2 = 4 * (1 + 2*2)
)
type vfscapData struct {
magic uint32
data [2]struct {
permitted uint32
inheritable uint32
}
effective [2]uint32
version int8
}
var (
_vfsXattrName *byte
)
func init() {
_vfsXattrName, _ = syscall.BytePtrFromString(vfsXattrName)
}
func getVfsCap(path string, dest *vfscapData) (err error) {
var _p0 *byte
_p0, err = syscall.BytePtrFromString(path)
if err != nil {
return
}
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0)
if e1 != 0 {
if e1 == syscall.ENODATA {
dest.version = 2
return
}
err = e1
}
switch dest.magic & vfsCapVerMask {
case vfsCapVer1:
dest.version = 1
if r0 != vfscapDataSizeV1 {
return syscall.EINVAL
}
dest.data[1].permitted = 0
dest.data[1].inheritable = 0
case vfsCapVer2:
dest.version = 2
if r0 != vfscapDataSizeV2 {
return syscall.EINVAL
}
default:
return syscall.EINVAL
}
if dest.magic&vfsCapFlageffective != 0 {
dest.effective[0] = dest.data[0].permitted | dest.data[0].inheritable
dest.effective[1] = dest.data[1].permitted | dest.data[1].inheritable
} else {
dest.effective[0] = 0
dest.effective[1] = 0
}
return
}
func setVfsCap(path string, data *vfscapData) (err error) {
var _p0 *byte
_p0, err = syscall.BytePtrFromString(path)
if err != nil {
return
}
var size uintptr
if data.version == 1 {
data.magic = vfsCapVer1
size = vfscapDataSizeV1
} else if data.version == 2 {
data.magic = vfsCapVer2
if data.effective[0] != 0 || data.effective[1] != 0 {
data.magic |= vfsCapFlageffective
}
size = vfscapDataSizeV2
} else {
return syscall.EINVAL
}
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0)
if e1 != 0 {
err = e1
}
return
}

View File

@ -47,6 +47,9 @@ github.com/blang/semver
github.com/blang/semver/v4
# github.com/cespare/xxhash/v2 v2.1.1
github.com/cespare/xxhash/v2
# github.com/checkpoint-restore/go-criu/v4 v4.1.0
github.com/checkpoint-restore/go-criu/v4
github.com/checkpoint-restore/go-criu/v4/rpc
# github.com/cilium/ebpf v0.4.0
github.com/cilium/ebpf
github.com/cilium/ebpf/asm
@ -102,6 +105,7 @@ github.com/containerd/containerd/namespaces
github.com/containerd/containerd/oci
github.com/containerd/containerd/pkg/cap
github.com/containerd/containerd/pkg/dialer
github.com/containerd/containerd/pkg/runtimeoptions/v1
github.com/containerd/containerd/pkg/ttrpcutil
github.com/containerd/containerd/pkg/userns
github.com/containerd/containerd/platforms
@ -280,6 +284,8 @@ github.com/mitchellh/mapstructure
github.com/moby/locker
# github.com/moby/sys/mountinfo v0.4.1
github.com/moby/sys/mountinfo
# github.com/mrunalp/fileutils v0.5.0
github.com/mrunalp/fileutils
# github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/go-digest
# github.com/opencontainers/image-spec v1.0.1
@ -289,6 +295,9 @@ github.com/opencontainers/image-spec/specs-go
github.com/opencontainers/image-spec/specs-go/v1
# github.com/opencontainers/runc v1.0.0-rc93
## explicit
github.com/opencontainers/runc/libcontainer
github.com/opencontainers/runc/libcontainer/apparmor
github.com/opencontainers/runc/libcontainer/capabilities
github.com/opencontainers/runc/libcontainer/cgroups
github.com/opencontainers/runc/libcontainer/cgroups/devices
github.com/opencontainers/runc/libcontainer/cgroups/ebpf
@ -298,13 +307,19 @@ github.com/opencontainers/runc/libcontainer/cgroups/fs2
github.com/opencontainers/runc/libcontainer/cgroups/fscommon
github.com/opencontainers/runc/libcontainer/cgroups/systemd
github.com/opencontainers/runc/libcontainer/configs
github.com/opencontainers/runc/libcontainer/configs/validate
github.com/opencontainers/runc/libcontainer/devices
github.com/opencontainers/runc/libcontainer/intelrdt
github.com/opencontainers/runc/libcontainer/keys
github.com/opencontainers/runc/libcontainer/logs
github.com/opencontainers/runc/libcontainer/seccomp
github.com/opencontainers/runc/libcontainer/seccomp/patchbpf
github.com/opencontainers/runc/libcontainer/specconv
github.com/opencontainers/runc/libcontainer/stacktrace
github.com/opencontainers/runc/libcontainer/system
github.com/opencontainers/runc/libcontainer/user
github.com/opencontainers/runc/libcontainer/utils
github.com/opencontainers/runc/types
# github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d
## explicit
github.com/opencontainers/runtime-spec/specs-go
@ -356,6 +371,8 @@ github.com/sirupsen/logrus/hooks/syslog
# github.com/stretchr/testify v1.6.1
## explicit
github.com/stretchr/testify/assert
# github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/syndtr/gocapability/capability
# github.com/urfave/cli v1.22.2
## explicit
github.com/urfave/cli

View File

@ -38,7 +38,7 @@ func V1Constraints() ([]cgroups.Subsystem, error) {
return nil, err
}
subsystems := []cgroups.Subsystem{
cgroups.NewCputset(root),
cgroups.NewCpuset(root),
cgroups.NewCpu(root),
cgroups.NewCpuacct(root),
}

View File

@ -14,6 +14,7 @@ import (
"testing"
"github.com/containerd/cgroups"
cgroupsstatsv1 "github.com/containerd/cgroups/stats/v1"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
@ -41,8 +42,8 @@ func (m *mockCgroup) MoveTo(cgroups.Cgroup) error {
return nil
}
func (m *mockCgroup) Stat(...cgroups.ErrorHandler) (*cgroups.Metrics, error) {
return &cgroups.Metrics{}, nil
func (m *mockCgroup) Stat(...cgroups.ErrorHandler) (*cgroupsstatsv1.Metrics, error) {
return &cgroupsstatsv1.Metrics{}, nil
}
func (m *mockCgroup) Update(resources *specs.LinuxResources) error {
@ -65,6 +66,10 @@ func (m *mockCgroup) OOMEventFD() (uintptr, error) {
return 0, nil
}
func (m *mockCgroup) RegisterMemoryEvent(event cgroups.MemoryEvent) (uintptr, error) {
return 0, nil
}
func (m *mockCgroup) State() cgroups.State {
return ""
}

View File

@ -1075,8 +1075,8 @@ func (k *kataAgent) constraintGRPCSpec(grpcSpec *grpc.Spec, passSeccomp bool) {
var tmpNamespaces []grpc.LinuxNamespace
for _, ns := range grpcSpec.Linux.Namespaces {
switch ns.Type {
case specs.CgroupNamespace:
case specs.NetworkNamespace:
case string(specs.CgroupNamespace):
case string(specs.NetworkNamespace):
default:
ns.Path = ""
tmpNamespaces = append(tmpNamespaces, ns)

View File

@ -553,11 +553,11 @@ func TestConstraintGRPCSpec(t *testing.T) {
Seccomp: &pb.LinuxSeccomp{},
Namespaces: []pb.LinuxNamespace{
{
Type: specs.NetworkNamespace,
Type: string(specs.NetworkNamespace),
Path: "/abc/123",
},
{
Type: specs.MountNamespace,
Type: string(specs.MountNamespace),
Path: "/abc/123",
},
},
@ -689,11 +689,11 @@ func TestHandlePidNamespace(t *testing.T) {
Linux: &pb.Linux{
Namespaces: []pb.LinuxNamespace{
{
Type: specs.NetworkNamespace,
Type: string(specs.NetworkNamespace),
Path: "/abc/123",
},
{
Type: specs.MountNamespace,
Type: string(specs.MountNamespace),
Path: "/abc/123",
},
},
@ -714,7 +714,7 @@ func TestHandlePidNamespace(t *testing.T) {
}
utsNs := pb.LinuxNamespace{
Type: specs.UTSNamespace,
Type: string(specs.UTSNamespace),
Path: "",
}

View File

@ -88,13 +88,13 @@ func (endpoint *PhysicalEndpoint) Attach(ctx context.Context, s *Sandbox) error
return err
}
c, err := cgroups.DeviceToCgroupDevice(vfioPath)
c, err := cgroups.DeviceToCgroupDeviceRule(vfioPath)
if err != nil {
return err
}
d := config.DeviceInfo{
ContainerPath: c.Path,
ContainerPath: vfioPath,
DevType: string(c.Type),
Major: c.Major,
Minor: c.Minor,

View File

@ -35,7 +35,7 @@ type CreateContainerRequest struct {
StringUser *StringUser `protobuf:"bytes,3,opt,name=string_user,json=stringUser,proto3" json:"string_user,omitempty"`
Devices []*Device `protobuf:"bytes,4,rep,name=devices,proto3" json:"devices,omitempty"`
Storages []*Storage `protobuf:"bytes,5,rep,name=storages,proto3" json:"storages,omitempty"`
OCI *Spec `protobuf:"bytes,6,opt,name=OCI,proto3" json:"OCI,omitempty"`
OCI *Spec `protobuf:"bytes,6,opt,name=OCI,json=oCI,proto3" json:"OCI,omitempty"`
// This field is used to indicate if the container needs to join
// sandbox shared pid ns or create a new namespace. This field is
// meant to override the NEWPID config settings in the OCI spec.
@ -1372,7 +1372,7 @@ func (m *DestroySandboxRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_DestroySandboxRequest proto.InternalMessageInfo
type Interfaces struct {
Interfaces []*protocols.Interface `protobuf:"bytes,1,rep,name=Interfaces,proto3" json:"Interfaces,omitempty"`
Interfaces []*protocols.Interface `protobuf:"bytes,1,rep,name=Interfaces,json=interfaces,proto3" json:"Interfaces,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1411,7 +1411,7 @@ func (m *Interfaces) XXX_DiscardUnknown() {
var xxx_messageInfo_Interfaces proto.InternalMessageInfo
type Routes struct {
Routes []*protocols.Route `protobuf:"bytes,1,rep,name=Routes,proto3" json:"Routes,omitempty"`
Routes []*protocols.Route `protobuf:"bytes,1,rep,name=Routes,json=routes,proto3" json:"Routes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1604,7 +1604,7 @@ func (m *ListRoutesRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_ListRoutesRequest proto.InternalMessageInfo
type ARPNeighbors struct {
ARPNeighbors []*protocols.ARPNeighbor `protobuf:"bytes,1,rep,name=ARPNeighbors,proto3" json:"ARPNeighbors,omitempty"`
ARPNeighbors []*protocols.ARPNeighbor `protobuf:"bytes,1,rep,name=ARPNeighbors,json=aRPNeighbors,proto3" json:"ARPNeighbors,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1948,9 +1948,9 @@ var xxx_messageInfo_MemHotplugByProbeRequest proto.InternalMessageInfo
type SetGuestDateTimeRequest struct {
// Sec the second since the Epoch.
Sec int64 `protobuf:"varint,1,opt,name=Sec,proto3" json:"Sec,omitempty"`
Sec int64 `protobuf:"varint,1,opt,name=Sec,json=sec,proto3" json:"Sec,omitempty"`
// Usec the microseconds portion of time since the Epoch.
Usec int64 `protobuf:"varint,2,opt,name=Usec,proto3" json:"Usec,omitempty"`
Usec int64 `protobuf:"varint,2,opt,name=Usec,json=usec,proto3" json:"Usec,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -2519,193 +2519,194 @@ func init() {
}
var fileDescriptor_c1460208c38ccf5e = []byte{
// 2971 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x1a, 0xcb, 0x72, 0x24, 0x47,
0xd1, 0xf3, 0x90, 0x66, 0x26, 0xe7, 0xa5, 0x69, 0x69, 0xb5, 0xb3, 0x63, 0x5b, 0xac, 0x7b, 0xed,
0xb5, 0x8c, 0xf1, 0xc8, 0x5e, 0x3b, 0x58, 0x3f, 0xc2, 0x6c, 0xac, 0x1e, 0x96, 0x64, 0x5b, 0x96,
0xdc, 0x5a, 0x85, 0x09, 0x08, 0xe8, 0x68, 0x75, 0x97, 0x66, 0xca, 0x9a, 0xee, 0x6a, 0x57, 0x57,
0x6b, 0x35, 0x26, 0x82, 0x80, 0x0b, 0xdc, 0x38, 0x72, 0xe3, 0x07, 0x08, 0x6e, 0x1c, 0xb9, 0x72,
0x70, 0x70, 0xe2, 0xc8, 0x89, 0xc0, 0xfb, 0x09, 0x7c, 0x01, 0x51, 0xaf, 0x7e, 0xcc, 0x43, 0x06,
0x85, 0x22, 0xb8, 0x4c, 0x54, 0x66, 0x65, 0xe5, 0xab, 0xaa, 0xb2, 0x32, 0xb3, 0x07, 0x3e, 0x1f,
0x60, 0x36, 0x8c, 0x4f, 0xfb, 0x2e, 0xf1, 0x37, 0xce, 0x1d, 0xe6, 0xbc, 0xe1, 0x92, 0x80, 0x39,
0x38, 0x40, 0x34, 0x9a, 0x82, 0x23, 0xea, 0x6e, 0x38, 0x03, 0x14, 0xb0, 0x8d, 0x90, 0x12, 0x46,
0x5c, 0x32, 0x8a, 0xe4, 0x28, 0x92, 0xe8, 0xbe, 0x00, 0x8c, 0xf2, 0x80, 0x86, 0x6e, 0xaf, 0x46,
0x5c, 0x2c, 0x11, 0xbd, 0x3a, 0x1b, 0x87, 0x28, 0x52, 0xc0, 0xf3, 0x03, 0x42, 0x06, 0x23, 0x24,
0x17, 0x9e, 0xc6, 0x67, 0x1b, 0xc8, 0x0f, 0xd9, 0x58, 0x4e, 0x9a, 0x7f, 0x28, 0xc2, 0xea, 0x16,
0x45, 0x0e, 0x43, 0x5b, 0x5a, 0xac, 0x85, 0xbe, 0x8a, 0x51, 0xc4, 0x8c, 0x97, 0xa0, 0x91, 0xa8,
0x62, 0x63, 0xaf, 0x5b, 0xb8, 0x5b, 0x58, 0xaf, 0x59, 0xf5, 0x04, 0xb7, 0xef, 0x19, 0xb7, 0xa1,
0x82, 0x2e, 0x91, 0xcb, 0x67, 0x8b, 0x62, 0x76, 0x91, 0x83, 0xfb, 0x9e, 0xf1, 0x16, 0xd4, 0x23,
0x46, 0x71, 0x30, 0xb0, 0xe3, 0x08, 0xd1, 0x6e, 0xe9, 0x6e, 0x61, 0xbd, 0xfe, 0x60, 0xa9, 0xcf,
0xf5, 0xec, 0x1f, 0x8b, 0x89, 0x93, 0x08, 0x51, 0x0b, 0xa2, 0x64, 0x6c, 0xdc, 0x87, 0x8a, 0x87,
0x2e, 0xb0, 0x8b, 0xa2, 0x6e, 0xf9, 0x6e, 0x69, 0xbd, 0xfe, 0xa0, 0x21, 0xc9, 0xb7, 0x05, 0xd2,
0xd2, 0x93, 0xc6, 0x6b, 0x50, 0x8d, 0x18, 0xa1, 0xce, 0x00, 0x45, 0xdd, 0x05, 0x41, 0xd8, 0xd4,
0x7c, 0x05, 0xd6, 0x4a, 0xa6, 0x8d, 0x17, 0xa0, 0x74, 0xb8, 0xb5, 0xdf, 0x5d, 0x14, 0xd2, 0x41,
0x51, 0x85, 0xc8, 0xb5, 0x38, 0xda, 0xb8, 0x07, 0xcd, 0xc8, 0x09, 0xbc, 0x53, 0x72, 0x69, 0x87,
0xd8, 0x0b, 0xa2, 0x6e, 0xe5, 0x6e, 0x61, 0xbd, 0x6a, 0x35, 0x14, 0xf2, 0x88, 0xe3, 0xcc, 0xf7,
0xe1, 0xd6, 0x31, 0x73, 0x28, 0xbb, 0x86, 0x77, 0xcc, 0x13, 0x58, 0xb5, 0x90, 0x4f, 0x2e, 0xae,
0xe5, 0xda, 0x2e, 0x54, 0x18, 0xf6, 0x11, 0x89, 0x99, 0x70, 0x6d, 0xd3, 0xd2, 0xa0, 0xf9, 0xa7,
0x02, 0x18, 0x3b, 0x97, 0xc8, 0x3d, 0xa2, 0xc4, 0x45, 0x51, 0xf4, 0x7f, 0xda, 0xae, 0x57, 0xa1,
0x12, 0x4a, 0x05, 0xba, 0x65, 0x41, 0xae, 0x76, 0x41, 0x6b, 0xa5, 0x67, 0xcd, 0x2f, 0x61, 0xe5,
0x18, 0x0f, 0x02, 0x67, 0x74, 0x83, 0xfa, 0xae, 0xc2, 0x62, 0x24, 0x78, 0x0a, 0x55, 0x9b, 0x96,
0x82, 0xcc, 0x23, 0x30, 0xbe, 0x70, 0x30, 0xbb, 0x39, 0x49, 0xe6, 0x1b, 0xb0, 0x9c, 0xe3, 0x18,
0x85, 0x24, 0x88, 0x90, 0x50, 0x80, 0x39, 0x2c, 0x8e, 0x04, 0xb3, 0x05, 0x4b, 0x41, 0x26, 0x81,
0xd5, 0x93, 0xd0, 0xbb, 0xe6, 0x6d, 0x7a, 0x00, 0x35, 0x8a, 0x22, 0x12, 0x53, 0x7e, 0x07, 0x8a,
0xc2, 0xa9, 0x2b, 0xd2, 0xa9, 0x9f, 0xe2, 0x20, 0xbe, 0xb4, 0xf4, 0x9c, 0x95, 0x92, 0xa9, 0xf3,
0xc9, 0xa2, 0xeb, 0x9c, 0xcf, 0xf7, 0xe1, 0xd6, 0x91, 0x13, 0x47, 0xd7, 0xd1, 0xd5, 0xfc, 0x80,
0x9f, 0xed, 0x28, 0xf6, 0xaf, 0xb5, 0xf8, 0x8f, 0x05, 0xa8, 0x6e, 0x85, 0xf1, 0x49, 0xe4, 0x0c,
0x90, 0xf1, 0x3d, 0xa8, 0x33, 0xc2, 0x9c, 0x91, 0x1d, 0x73, 0x50, 0x90, 0x97, 0x2d, 0x10, 0x28,
0x49, 0xf0, 0x12, 0x34, 0x42, 0x44, 0xdd, 0x30, 0x56, 0x14, 0xc5, 0xbb, 0xa5, 0xf5, 0xb2, 0x55,
0x97, 0x38, 0x49, 0xd2, 0x87, 0x65, 0x31, 0x67, 0xe3, 0xc0, 0x3e, 0x47, 0x34, 0x40, 0x23, 0x9f,
0x78, 0x48, 0x1c, 0x8e, 0xb2, 0xd5, 0x11, 0x53, 0xfb, 0xc1, 0x27, 0xc9, 0x84, 0xf1, 0x7d, 0xe8,
0x24, 0xf4, 0xfc, 0xc4, 0x0b, 0xea, 0xb2, 0xa0, 0x6e, 0x2b, 0xea, 0x13, 0x85, 0x36, 0x7f, 0x09,
0xad, 0x27, 0x43, 0x4a, 0x18, 0x1b, 0xe1, 0x60, 0xb0, 0xed, 0x30, 0x87, 0x5f, 0xcd, 0x10, 0x51,
0x4c, 0xbc, 0x48, 0x69, 0xab, 0x41, 0xe3, 0x75, 0xe8, 0x30, 0x49, 0x8b, 0x3c, 0x5b, 0xd3, 0x14,
0x05, 0xcd, 0x52, 0x32, 0x71, 0xa4, 0x88, 0x5f, 0x81, 0x56, 0x4a, 0xcc, 0x2f, 0xb7, 0xd2, 0xb7,
0x99, 0x60, 0x9f, 0x60, 0x1f, 0x99, 0x17, 0xc2, 0x57, 0x62, 0x93, 0x8d, 0xd7, 0xa1, 0x96, 0xfa,
0xa1, 0x20, 0x4e, 0x48, 0x4b, 0x9e, 0x10, 0xed, 0x4e, 0xab, 0x9a, 0x38, 0xe5, 0x43, 0x68, 0xb3,
0x44, 0x71, 0xdb, 0x73, 0x98, 0x93, 0x3f, 0x54, 0x79, 0xab, 0xac, 0x16, 0xcb, 0xc1, 0xe6, 0x07,
0x50, 0x3b, 0xc2, 0x5e, 0x24, 0x05, 0x77, 0xa1, 0xe2, 0xc6, 0x94, 0xa2, 0x80, 0x69, 0x93, 0x15,
0x68, 0xac, 0xc0, 0xc2, 0x08, 0xfb, 0x98, 0x29, 0x33, 0x25, 0x60, 0x12, 0x80, 0x03, 0xe4, 0x13,
0x3a, 0x16, 0x0e, 0x5b, 0x81, 0x85, 0xec, 0xe6, 0x4a, 0xc0, 0x78, 0x1e, 0x6a, 0xbe, 0x73, 0x99,
0x6c, 0x2a, 0x9f, 0xa9, 0xfa, 0xce, 0xa5, 0x54, 0xbe, 0x0b, 0x95, 0x33, 0x07, 0x8f, 0xdc, 0x80,
0x29, 0xaf, 0x68, 0x30, 0x15, 0x58, 0xce, 0x0a, 0xfc, 0x6b, 0x11, 0xea, 0x52, 0xa2, 0x54, 0x78,
0x05, 0x16, 0x5c, 0xc7, 0x1d, 0x26, 0x22, 0x05, 0x60, 0xdc, 0xd7, 0x8a, 0x14, 0xb3, 0x11, 0x2e,
0xd5, 0x54, 0xab, 0xb6, 0x01, 0x10, 0x3d, 0x75, 0x42, 0xa5, 0x5b, 0x69, 0x0e, 0x71, 0x8d, 0xd3,
0x48, 0x75, 0xdf, 0x86, 0x86, 0x3c, 0x77, 0x6a, 0x49, 0x79, 0xce, 0x92, 0xba, 0xa4, 0x92, 0x8b,
0xee, 0x41, 0x33, 0x8e, 0x90, 0x3d, 0xc4, 0x88, 0x3a, 0xd4, 0x1d, 0x8e, 0xbb, 0x0b, 0xf2, 0x01,
0x8a, 0x23, 0xb4, 0xa7, 0x71, 0xc6, 0x03, 0x58, 0xe0, 0xb1, 0x25, 0xea, 0x2e, 0x8a, 0xb7, 0xee,
0x85, 0x2c, 0x4b, 0x61, 0x6a, 0x5f, 0xfc, 0xee, 0x04, 0x8c, 0x8e, 0x2d, 0x49, 0xda, 0x7b, 0x17,
0x20, 0x45, 0x1a, 0x4b, 0x50, 0x3a, 0x47, 0x63, 0x75, 0x0f, 0xf9, 0x90, 0x3b, 0xe7, 0xc2, 0x19,
0xc5, 0xda, 0xeb, 0x12, 0x78, 0xbf, 0xf8, 0x6e, 0xc1, 0x74, 0xa1, 0xbd, 0x39, 0x3a, 0xc7, 0x24,
0xb3, 0x7c, 0x05, 0x16, 0x7c, 0xe7, 0x4b, 0x42, 0xb5, 0x27, 0x05, 0x20, 0xb0, 0x38, 0x20, 0x54,
0xb3, 0x10, 0x80, 0xd1, 0x82, 0x22, 0x09, 0x85, 0xbf, 0x6a, 0x56, 0x91, 0x84, 0xa9, 0xa0, 0x72,
0x46, 0x90, 0xf9, 0xcf, 0x32, 0x40, 0x2a, 0xc5, 0xb0, 0xa0, 0x87, 0x89, 0x1d, 0x21, 0xca, 0xdf,
0x77, 0xfb, 0x74, 0xcc, 0x50, 0x64, 0x53, 0xe4, 0xc6, 0x34, 0xc2, 0x17, 0x7c, 0xff, 0xb8, 0xd9,
0xb7, 0xa4, 0xd9, 0x13, 0xba, 0x59, 0xb7, 0x31, 0x39, 0x96, 0xeb, 0x36, 0xf9, 0x32, 0x4b, 0xaf,
0x32, 0xf6, 0xe1, 0x56, 0xca, 0xd3, 0xcb, 0xb0, 0x2b, 0x5e, 0xc5, 0x6e, 0x39, 0x61, 0xe7, 0xa5,
0xac, 0x76, 0x60, 0x19, 0x13, 0xfb, 0xab, 0x18, 0xc5, 0x39, 0x46, 0xa5, 0xab, 0x18, 0x75, 0x30,
0xf9, 0x5c, 0x2c, 0x48, 0xd9, 0x1c, 0xc1, 0x9d, 0x8c, 0x95, 0xfc, 0xba, 0x67, 0x98, 0x95, 0xaf,
0x62, 0xb6, 0x9a, 0x68, 0xc5, 0xe3, 0x41, 0xca, 0xf1, 0x63, 0x58, 0xc5, 0xc4, 0x7e, 0xea, 0x60,
0x36, 0xc9, 0x6e, 0xe1, 0x3b, 0x8c, 0xe4, 0x2f, 0x5a, 0x9e, 0x97, 0x34, 0xd2, 0x47, 0x74, 0x90,
0x33, 0x72, 0xf1, 0x3b, 0x8c, 0x3c, 0x10, 0x0b, 0x52, 0x36, 0x8f, 0xa1, 0x83, 0xc9, 0xa4, 0x36,
0x95, 0xab, 0x98, 0xb4, 0x31, 0xc9, 0x6b, 0xb2, 0x09, 0x9d, 0x08, 0xb9, 0x8c, 0xd0, 0xec, 0x21,
0xa8, 0x5e, 0xc5, 0x62, 0x49, 0xd1, 0x27, 0x3c, 0xcc, 0x9f, 0x42, 0x63, 0x2f, 0x1e, 0x20, 0x36,
0x3a, 0x4d, 0x82, 0xc1, 0x8d, 0xc5, 0x1f, 0xf3, 0xdf, 0x45, 0xa8, 0x6f, 0x0d, 0x28, 0x89, 0xc3,
0x5c, 0x4c, 0x96, 0x97, 0x74, 0x32, 0x26, 0x0b, 0x12, 0x11, 0x93, 0x25, 0xf1, 0x3b, 0xd0, 0xf0,
0xc5, 0xd5, 0x55, 0xf4, 0x32, 0x0e, 0x75, 0xa6, 0x2e, 0xb5, 0x55, 0xf7, 0x33, 0xc1, 0xac, 0x0f,
0x10, 0x62, 0x2f, 0x52, 0x6b, 0x64, 0x38, 0x6a, 0xab, 0x74, 0x4b, 0x87, 0x68, 0xab, 0x16, 0x26,
0xd1, 0xfa, 0x2d, 0xa8, 0x9f, 0x72, 0x27, 0xa9, 0x05, 0xb9, 0x60, 0x94, 0x7a, 0xcf, 0x82, 0xd3,
0xf4, 0x12, 0xee, 0x41, 0x73, 0x28, 0x5d, 0xa6, 0x16, 0xc9, 0x33, 0x74, 0x4f, 0x59, 0x92, 0xda,
0xdb, 0xcf, 0x7a, 0x56, 0x6e, 0x40, 0x63, 0x98, 0x41, 0xf5, 0x8e, 0xa1, 0x33, 0x45, 0x32, 0x23,
0x06, 0xad, 0x67, 0x63, 0x50, 0xfd, 0x81, 0x21, 0x05, 0x65, 0x57, 0x66, 0xe3, 0xd2, 0xef, 0x8a,
0xd0, 0xf8, 0x0c, 0xb1, 0xa7, 0x84, 0x9e, 0x4b, 0x7d, 0x0d, 0x28, 0x07, 0x8e, 0x8f, 0x14, 0x47,
0x31, 0x36, 0xee, 0x40, 0x95, 0x5e, 0xca, 0x00, 0xa2, 0xf6, 0xb3, 0x42, 0x2f, 0x45, 0x60, 0x30,
0x5e, 0x04, 0xa0, 0x97, 0x76, 0xe8, 0xb8, 0xe7, 0x48, 0x79, 0xb0, 0x6c, 0xd5, 0xe8, 0xe5, 0x91,
0x44, 0xf0, 0xa3, 0x40, 0x2f, 0x6d, 0x44, 0x29, 0xa1, 0x91, 0x8a, 0x55, 0x55, 0x7a, 0xb9, 0x23,
0x60, 0xb5, 0xd6, 0xa3, 0x24, 0x0c, 0x91, 0x27, 0x62, 0xb4, 0x58, 0xbb, 0x2d, 0x11, 0x5c, 0x2a,
0xd3, 0x52, 0x17, 0xa5, 0x54, 0x96, 0x4a, 0x65, 0xa9, 0xd4, 0x8a, 0x5c, 0xc9, 0xb2, 0x52, 0x59,
0x22, 0xb5, 0x2a, 0xa5, 0xb2, 0x8c, 0x54, 0x96, 0x4a, 0xad, 0xe9, 0xb5, 0x4a, 0xaa, 0xf9, 0xdb,
0x02, 0xac, 0x4e, 0x26, 0x7e, 0x2a, 0x37, 0x7d, 0x07, 0x1a, 0xae, 0xd8, 0xaf, 0xdc, 0x99, 0xec,
0x4c, 0xed, 0xa4, 0x55, 0x77, 0x33, 0xc7, 0xf8, 0x21, 0x34, 0x03, 0xe9, 0xe0, 0xe4, 0x68, 0x96,
0xd2, 0x7d, 0xc9, 0xfa, 0xde, 0x6a, 0x04, 0x19, 0xc8, 0xf4, 0xc0, 0xf8, 0x82, 0x62, 0x86, 0x8e,
0x19, 0x45, 0x8e, 0x7f, 0x13, 0xd9, 0xbd, 0x01, 0x65, 0x91, 0xad, 0xf0, 0x6d, 0x6a, 0x58, 0x62,
0x6c, 0xbe, 0x0a, 0xcb, 0x39, 0x29, 0xca, 0xd6, 0x25, 0x28, 0x8d, 0x50, 0x20, 0xb8, 0x37, 0x2d,
0x3e, 0x34, 0x1d, 0xe8, 0x58, 0xc8, 0xf1, 0x6e, 0x4e, 0x1b, 0x25, 0xa2, 0x94, 0x8a, 0x58, 0x07,
0x23, 0x2b, 0x42, 0xa9, 0xa2, 0xb5, 0x2e, 0x64, 0xb4, 0x3e, 0x84, 0xce, 0xd6, 0x88, 0x44, 0xe8,
0x98, 0x79, 0x38, 0xb8, 0x89, 0x72, 0xe4, 0x17, 0xb0, 0xfc, 0x84, 0x8d, 0xbf, 0xe0, 0xcc, 0x22,
0xfc, 0x35, 0xba, 0x21, 0xfb, 0x28, 0x79, 0xaa, 0xed, 0xa3, 0xe4, 0x29, 0x2f, 0x6e, 0x5c, 0x32,
0x8a, 0xfd, 0x40, 0x5c, 0x85, 0xa6, 0xa5, 0x20, 0x73, 0x13, 0x1a, 0x32, 0x87, 0x3e, 0x20, 0x5e,
0x3c, 0x42, 0x33, 0xef, 0xe0, 0x1a, 0x40, 0xe8, 0x50, 0xc7, 0x47, 0x0c, 0x51, 0x79, 0x86, 0x6a,
0x56, 0x06, 0x63, 0xfe, 0xbe, 0x08, 0x2b, 0xb2, 0xdf, 0x70, 0x2c, 0xcb, 0x6c, 0x6d, 0x42, 0x0f,
0xaa, 0x43, 0x12, 0xb1, 0x0c, 0xc3, 0x04, 0xe6, 0x2a, 0xf2, 0xfa, 0x5c, 0x72, 0xe3, 0xc3, 0x5c,
0x13, 0xa0, 0x74, 0x75, 0x13, 0x60, 0xaa, 0xcc, 0x2f, 0x4f, 0x97, 0xf9, 0xfc, 0xb6, 0x69, 0x22,
0x2c, 0xef, 0x78, 0xcd, 0xaa, 0x29, 0xcc, 0xbe, 0x67, 0xdc, 0x87, 0xf6, 0x80, 0x6b, 0x69, 0x0f,
0x09, 0x39, 0xb7, 0x43, 0x87, 0x0d, 0xc5, 0x55, 0xaf, 0x59, 0x4d, 0x81, 0xde, 0x23, 0xe4, 0xfc,
0xc8, 0x61, 0x43, 0xe3, 0x3d, 0x68, 0xa9, 0x34, 0xd0, 0x17, 0x2e, 0x8a, 0xd4, 0xe3, 0xa7, 0x6e,
0x51, 0xd6, 0x7b, 0x56, 0xf3, 0x3c, 0x03, 0x45, 0xe6, 0x6d, 0xb8, 0xb5, 0x8d, 0x22, 0x46, 0xc9,
0x38, 0xef, 0x18, 0xf3, 0x47, 0x00, 0xfb, 0x01, 0x43, 0xf4, 0xcc, 0x71, 0x51, 0x64, 0xbc, 0x99,
0x85, 0x54, 0x72, 0xb4, 0xd4, 0x97, 0xed, 0x9e, 0x64, 0xc2, 0xca, 0xd0, 0x98, 0x7d, 0x58, 0xb4,
0x48, 0xcc, 0xc3, 0xd1, 0xcb, 0x7a, 0xa4, 0xd6, 0x35, 0xd4, 0x3a, 0x81, 0xb4, 0xd4, 0x9c, 0xb9,
0xa7, 0x4b, 0xd8, 0x94, 0x9d, 0xda, 0xa2, 0x3e, 0xd4, 0xb0, 0xc6, 0xa9, 0xa8, 0x32, 0x2d, 0x3a,
0x25, 0x31, 0x3f, 0x80, 0x65, 0xc9, 0x49, 0x72, 0xd6, 0x6c, 0x5e, 0x86, 0x45, 0xaa, 0xd5, 0x28,
0xa4, 0x7d, 0x1e, 0x45, 0xa4, 0xe6, 0xb8, 0x3f, 0x3e, 0xc5, 0x11, 0x4b, 0x0d, 0xd1, 0xfe, 0x58,
0x86, 0x0e, 0x9f, 0xc8, 0xf1, 0x34, 0x3f, 0x82, 0xc6, 0x63, 0xeb, 0xe8, 0x33, 0x84, 0x07, 0xc3,
0x53, 0x1e, 0x3d, 0x7f, 0x98, 0x87, 0x95, 0xc1, 0x86, 0xd2, 0x36, 0x33, 0x65, 0xe5, 0xe8, 0xcc,
0x8f, 0x61, 0xf5, 0xb1, 0xe7, 0x65, 0x51, 0x5a, 0xeb, 0x37, 0xa1, 0x16, 0x64, 0xd8, 0x65, 0xde,
0xac, 0x1c, 0x75, 0x4a, 0x64, 0xfe, 0x0c, 0x96, 0x0f, 0x83, 0x11, 0x0e, 0xd0, 0xd6, 0xd1, 0xc9,
0x01, 0x4a, 0x62, 0x91, 0x01, 0x65, 0x9e, 0xb3, 0x09, 0x1e, 0x55, 0x4b, 0x8c, 0xf9, 0xe5, 0x0c,
0x4e, 0x6d, 0x37, 0x8c, 0x23, 0xd5, 0xec, 0x59, 0x0c, 0x4e, 0xb7, 0xc2, 0x38, 0xe2, 0x8f, 0x0b,
0x4f, 0x2e, 0x48, 0x30, 0x1a, 0x8b, 0x1b, 0x5a, 0xb5, 0x2a, 0x6e, 0x18, 0x1f, 0x06, 0xa3, 0xb1,
0xf9, 0x03, 0x51, 0x81, 0x23, 0xe4, 0x59, 0x4e, 0xe0, 0x11, 0x7f, 0x1b, 0x5d, 0x64, 0x24, 0x24,
0xd5, 0x9e, 0x8e, 0x44, 0xdf, 0x14, 0xa0, 0xf1, 0x78, 0x80, 0x02, 0xb6, 0x8d, 0x98, 0x83, 0x47,
0xa2, 0xa2, 0xbb, 0x40, 0x34, 0xc2, 0x24, 0x50, 0xd7, 0x4d, 0x83, 0xbc, 0x20, 0xc7, 0x01, 0x66,
0xb6, 0xe7, 0x20, 0x9f, 0x04, 0x82, 0x4b, 0xd5, 0x02, 0x8e, 0xda, 0x16, 0x18, 0xe3, 0x55, 0x68,
0xcb, 0x66, 0x9c, 0x3d, 0x74, 0x02, 0x6f, 0xc4, 0x2f, 0x7a, 0x49, 0x5c, 0xcd, 0x96, 0x44, 0xef,
0x29, 0xac, 0xf1, 0x1a, 0x2c, 0xa9, 0x6b, 0x98, 0x52, 0x96, 0x05, 0x65, 0x5b, 0xe1, 0x73, 0xa4,
0x71, 0x18, 0x12, 0xca, 0x22, 0x3b, 0x42, 0xae, 0x4b, 0xfc, 0x50, 0x95, 0x43, 0x6d, 0x8d, 0x3f,
0x96, 0x68, 0x73, 0x00, 0xcb, 0xbb, 0xdc, 0x4e, 0x65, 0x49, 0x7a, 0xac, 0x5a, 0x3e, 0xf2, 0xed,
0xd3, 0x11, 0x71, 0xcf, 0x6d, 0x1e, 0x1c, 0x95, 0x87, 0x79, 0xc2, 0xb5, 0xc9, 0x91, 0xc7, 0xf8,
0x6b, 0x51, 0xf9, 0x73, 0xaa, 0x21, 0x61, 0xe1, 0x28, 0x1e, 0xd8, 0x21, 0x25, 0xa7, 0x48, 0x99,
0xd8, 0xf6, 0x91, 0xbf, 0x27, 0xf1, 0x47, 0x1c, 0x6d, 0xfe, 0xa5, 0x00, 0x2b, 0x79, 0x49, 0x2a,
0xd4, 0x6f, 0xc0, 0x4a, 0x5e, 0x94, 0x7a, 0xfe, 0x65, 0x7a, 0xd9, 0xc9, 0x0a, 0x94, 0x89, 0xc0,
0x43, 0x68, 0x8a, 0x7e, 0xad, 0xed, 0x49, 0x4e, 0xf9, 0xa4, 0x27, 0xbb, 0x2f, 0x56, 0xc3, 0xc9,
0xee, 0xd2, 0x7b, 0x70, 0x47, 0x99, 0x6f, 0x4f, 0xab, 0x2d, 0x0f, 0xc4, 0xaa, 0x22, 0x38, 0x98,
0xd0, 0xfe, 0x53, 0xe8, 0xa6, 0xa8, 0xcd, 0xb1, 0x40, 0xa6, 0x87, 0x79, 0x79, 0xc2, 0xd8, 0xc7,
0x9e, 0x47, 0xc5, 0x2d, 0x29, 0x5b, 0xb3, 0xa6, 0xcc, 0x47, 0x70, 0xfb, 0x18, 0x31, 0xe9, 0x0d,
0x87, 0xa9, 0x4a, 0x44, 0x32, 0x5b, 0x82, 0xd2, 0x31, 0x72, 0x85, 0xf1, 0x25, 0x8b, 0x0f, 0xf9,
0x01, 0x3c, 0x89, 0x90, 0x2b, 0xac, 0x2c, 0x59, 0x62, 0x6c, 0xfe, 0xb9, 0x00, 0x15, 0x15, 0x9c,
0xf9, 0x03, 0xe3, 0x51, 0x7c, 0x81, 0xa8, 0x3a, 0x7a, 0x0a, 0x32, 0x5e, 0x81, 0x96, 0x1c, 0xd9,
0x24, 0x64, 0x98, 0x24, 0x21, 0xbf, 0x29, 0xb1, 0x87, 0x12, 0x29, 0x9a, 0x6f, 0xa2, 0xfd, 0xa5,
0x2a, 0x4d, 0x05, 0x71, 0xfc, 0x59, 0xc4, 0x6f, 0xb8, 0x08, 0xf1, 0x35, 0x4b, 0x41, 0xfc, 0xa8,
0x6b, 0x7e, 0x0b, 0x82, 0x9f, 0x06, 0xf9, 0x51, 0xf7, 0x49, 0x1c, 0x30, 0x3b, 0x24, 0x38, 0x60,
0x2a, 0xa6, 0x83, 0x40, 0x1d, 0x71, 0x8c, 0xf9, 0x9b, 0x02, 0x2c, 0xca, 0x06, 0x34, 0xaf, 0x6d,
0x93, 0x97, 0xb5, 0x88, 0x45, 0x96, 0x22, 0x64, 0xc9, 0xd7, 0x54, 0x8c, 0xf9, 0x3d, 0xbe, 0xf0,
0xe5, 0xfb, 0xa0, 0x54, 0xbb, 0xf0, 0xc5, 0xc3, 0xf0, 0x0a, 0xb4, 0xd2, 0x07, 0x5a, 0xcc, 0x4b,
0x15, 0x9b, 0x09, 0x56, 0x90, 0xcd, 0xd5, 0xd4, 0xfc, 0x31, 0x2f, 0xe9, 0x93, 0xe6, 0xeb, 0x12,
0x94, 0xe2, 0x44, 0x19, 0x3e, 0xe4, 0x98, 0x41, 0xf2, 0xb4, 0xf3, 0xa1, 0x71, 0x1f, 0x5a, 0x8e,
0xe7, 0x61, 0xbe, 0xdc, 0x19, 0xed, 0x62, 0x2f, 0xb9, 0xa4, 0x79, 0xac, 0xf9, 0xb7, 0x02, 0xb4,
0xb7, 0x48, 0x38, 0xfe, 0x08, 0x8f, 0x50, 0x26, 0x82, 0x08, 0x25, 0xd5, 0xcb, 0xce, 0xc7, 0x3c,
0x5b, 0x3d, 0xc3, 0x23, 0x24, 0xaf, 0x96, 0xdc, 0xd9, 0x2a, 0x47, 0x88, 0x6b, 0xa5, 0x27, 0x93,
0xb6, 0x5b, 0x53, 0x4e, 0x1e, 0x10, 0x4f, 0xe4, 0xe5, 0x1e, 0xa6, 0x76, 0xd2, 0x64, 0x6b, 0x5a,
0x15, 0x0f, 0x53, 0x31, 0xa5, 0x0c, 0x59, 0x10, 0x4d, 0xd4, 0xac, 0x21, 0x8b, 0x12, 0xc3, 0x0d,
0x59, 0x85, 0x45, 0x72, 0x76, 0x16, 0x21, 0x26, 0x32, 0xe8, 0x92, 0xa5, 0xa0, 0x24, 0xcc, 0x55,
0x33, 0x61, 0xee, 0x16, 0x2c, 0x8b, 0x76, 0xfd, 0x13, 0xea, 0xb8, 0x38, 0x18, 0xe8, 0xe7, 0x61,
0x05, 0x8c, 0x63, 0x46, 0xc2, 0x69, 0xec, 0x2e, 0x62, 0x87, 0x87, 0x07, 0x3b, 0x17, 0x28, 0x60,
0x1a, 0xfb, 0x06, 0x54, 0x35, 0xea, 0xbf, 0xe9, 0x65, 0x2e, 0x43, 0x67, 0x17, 0xb1, 0x03, 0xc4,
0x28, 0x76, 0x93, 0xe7, 0xe8, 0x1e, 0x54, 0x14, 0x86, 0x6f, 0xa9, 0x2f, 0x87, 0x3a, 0xce, 0x2a,
0xf0, 0xc1, 0xaf, 0x3b, 0x2a, 0x24, 0xab, 0xea, 0xde, 0xd8, 0x85, 0xf6, 0xc4, 0xa7, 0x18, 0x43,
0xb5, 0x7b, 0x66, 0x7f, 0xa1, 0xe9, 0xad, 0xf6, 0xe5, 0xa7, 0x9d, 0xbe, 0xfe, 0xb4, 0xd3, 0xdf,
0xf1, 0x43, 0x36, 0x36, 0x76, 0xa0, 0x95, 0xff, 0x68, 0x61, 0x3c, 0xaf, 0xb3, 0xa3, 0x19, 0x9f,
0x32, 0xe6, 0xb2, 0xd9, 0x85, 0xf6, 0xc4, 0xf7, 0x0b, 0xad, 0xcf, 0xec, 0xcf, 0x1a, 0x73, 0x19,
0x3d, 0x82, 0x7a, 0xe6, 0x83, 0x85, 0xd1, 0x95, 0x4c, 0xa6, 0xbf, 0x61, 0xcc, 0x65, 0xb0, 0x05,
0xcd, 0xdc, 0x37, 0x04, 0xa3, 0xa7, 0xec, 0x99, 0xf1, 0x61, 0x61, 0x2e, 0x93, 0x4d, 0xa8, 0x67,
0x5a, 0xf9, 0x5a, 0x8b, 0xe9, 0xef, 0x05, 0xbd, 0x3b, 0x33, 0x66, 0x54, 0xe4, 0xdf, 0x85, 0xf6,
0x44, 0x7f, 0x5f, 0xbb, 0x64, 0x76, 0xdb, 0x7f, 0xae, 0x32, 0x9f, 0x88, 0x2d, 0xca, 0x94, 0x6f,
0x99, 0x2d, 0x9a, 0xee, 0xe6, 0xf7, 0x5e, 0x98, 0x3d, 0xa9, 0xb4, 0xda, 0x81, 0x56, 0xbe, 0x91,
0xaf, 0x99, 0xcd, 0x6c, 0xef, 0x5f, 0xbd, 0xdf, 0xb9, 0x9e, 0x7e, 0xba, 0xdf, 0xb3, 0x5a, 0xfd,
0x73, 0x19, 0x3d, 0x06, 0x50, 0xc5, 0x9a, 0x87, 0x83, 0xc4, 0xd1, 0x53, 0x45, 0x62, 0xe2, 0xe8,
0x19, 0x85, 0xdd, 0x23, 0x00, 0x59, 0x63, 0x79, 0x24, 0x66, 0xc6, 0x6d, 0xad, 0xc6, 0x44, 0x61,
0xd7, 0xeb, 0x4e, 0x4f, 0x4c, 0x31, 0x40, 0x94, 0x5e, 0x87, 0xc1, 0x87, 0x00, 0x69, 0xed, 0xa6,
0x19, 0x4c, 0x55, 0x73, 0x57, 0xf8, 0xa0, 0x91, 0xad, 0xd4, 0x0c, 0x65, 0xeb, 0x8c, 0xea, 0xed,
0x0a, 0x16, 0xed, 0x89, 0x4c, 0x3c, 0x7f, 0xd8, 0x26, 0x13, 0xf4, 0xde, 0x54, 0x36, 0x6e, 0x3c,
0x84, 0x46, 0x36, 0x05, 0xd7, 0x5a, 0xcc, 0x48, 0xcb, 0x7b, 0xb9, 0x34, 0xdc, 0x78, 0x04, 0xad,
0x7c, 0xfa, 0xad, 0x8f, 0xd4, 0xcc, 0xa4, 0xbc, 0xa7, 0x9a, 0x4b, 0x19, 0xf2, 0xb7, 0x01, 0xd2,
0x34, 0x5d, 0xbb, 0x6f, 0x2a, 0x71, 0x9f, 0x90, 0xba, 0x0b, 0xed, 0x89, 0xf4, 0x5b, 0x5b, 0x3c,
0x3b, 0x2b, 0xbf, 0xca, 0xfb, 0xd9, 0x77, 0x40, 0xdb, 0x3d, 0xe3, 0x6d, 0xb8, 0x2a, 0x68, 0x65,
0xde, 0x0c, 0x7d, 0x8a, 0xa7, 0x9f, 0x91, 0xb9, 0x0c, 0xde, 0x01, 0x48, 0x5f, 0x06, 0xed, 0x81,
0xa9, 0xb7, 0xa2, 0xd7, 0xd4, 0xcd, 0x3f, 0x49, 0xb7, 0x05, 0xcd, 0x5c, 0x7d, 0xac, 0x43, 0xdd,
0xac, 0xa2, 0xf9, 0xaa, 0x07, 0x20, 0x5f, 0x4c, 0xea, 0xdd, 0x9b, 0x59, 0x62, 0x5e, 0xe5, 0xc5,
0x6c, 0x05, 0xa3, 0xbd, 0x38, 0xa3, 0xaa, 0xf9, 0x8e, 0x98, 0x92, 0xad, 0x52, 0x32, 0x31, 0x65,
0x46, 0xf1, 0x32, 0x97, 0xd1, 0x1e, 0xb4, 0x77, 0x75, 0x02, 0xaa, 0x92, 0x63, 0xa5, 0xce, 0x8c,
0x62, 0xa0, 0xd7, 0x9b, 0x35, 0xa5, 0x2e, 0xf6, 0x27, 0xd0, 0x99, 0x4a, 0x8c, 0x8d, 0xb5, 0xa4,
0x05, 0x3b, 0x33, 0x63, 0x9e, 0xab, 0xd6, 0x3e, 0x2c, 0x4d, 0xe6, 0xc5, 0xc6, 0x8b, 0xea, 0xa8,
0xcc, 0xce, 0x97, 0xe7, 0xb2, 0x7a, 0x0f, 0xaa, 0x3a, 0x0f, 0x33, 0x54, 0xab, 0x7b, 0x22, 0x2f,
0x9b, 0xbb, 0xf4, 0x21, 0xd4, 0x33, 0x99, 0x8c, 0x3e, 0xab, 0xd3, 0xc9, 0x4d, 0x4f, 0x75, 0xa6,
0x35, 0x7a, 0xf3, 0xf2, 0x9b, 0x6f, 0xd7, 0x9e, 0xfb, 0xc7, 0xb7, 0x6b, 0xcf, 0xfd, 0xea, 0xd9,
0x5a, 0xe1, 0x9b, 0x67, 0x6b, 0x85, 0xbf, 0x3f, 0x5b, 0x2b, 0xfc, 0xeb, 0xd9, 0x5a, 0xe1, 0x27,
0x3f, 0xff, 0x1f, 0xff, 0xa6, 0x42, 0xe3, 0x80, 0x61, 0x1f, 0x6d, 0x5c, 0x60, 0xca, 0x32, 0x53,
0xe1, 0xf9, 0x60, 0xea, 0x1f, 0x2c, 0x5c, 0x85, 0xd3, 0x45, 0x01, 0xbf, 0xfd, 0x9f, 0x00, 0x00,
0x00, 0xff, 0xff, 0x23, 0xa0, 0xd8, 0x6f, 0x0f, 0x23, 0x00, 0x00,
// 2982 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x1a, 0xcb, 0x6e, 0x1c, 0xc7,
0xd1, 0xfb, 0x20, 0x77, 0xb7, 0xf6, 0xc5, 0x1d, 0x52, 0xd4, 0x6a, 0x6d, 0x33, 0xf2, 0xc8, 0x96,
0xe5, 0x38, 0x5e, 0xd9, 0xb2, 0x11, 0xf9, 0x01, 0x47, 0x10, 0x29, 0x9a, 0xa4, 0x6d, 0x5a, 0xf4,
0x50, 0x82, 0x83, 0x04, 0xc9, 0x60, 0x38, 0xd3, 0xda, 0x6d, 0x73, 0x67, 0x7a, 0xdc, 0xdd, 0x43,
0x71, 0x1d, 0x20, 0x48, 0x2e, 0xc9, 0x2d, 0xc7, 0xdc, 0xf2, 0x03, 0x41, 0x6e, 0x39, 0xe6, 0x9a,
0x83, 0x91, 0x53, 0x8e, 0x39, 0x05, 0xb1, 0x3e, 0x21, 0x5f, 0x10, 0xf4, 0x6b, 0x1e, 0xfb, 0xa0,
0x13, 0x41, 0x40, 0x2e, 0x8b, 0xae, 0xea, 0xea, 0x7a, 0x75, 0x77, 0x75, 0x55, 0xcd, 0xc2, 0xe7,
0x23, 0xcc, 0xc7, 0xc9, 0xc9, 0xd0, 0x27, 0xe1, 0xcd, 0x53, 0x8f, 0x7b, 0x6f, 0xf8, 0x24, 0xe2,
0x1e, 0x8e, 0x10, 0x65, 0x73, 0x30, 0xa3, 0xfe, 0x4d, 0x6f, 0x84, 0x22, 0x7e, 0x33, 0xa6, 0x84,
0x13, 0x9f, 0x4c, 0x98, 0x1a, 0x31, 0x85, 0x1e, 0x4a, 0xc0, 0xaa, 0x8e, 0x68, 0xec, 0x0f, 0x1a,
0xc4, 0xc7, 0x0a, 0x31, 0x68, 0xf2, 0x69, 0x8c, 0x98, 0x06, 0x9e, 0x1f, 0x11, 0x32, 0x9a, 0x20,
0xb5, 0xf0, 0x24, 0x79, 0x74, 0x13, 0x85, 0x31, 0x9f, 0xaa, 0x49, 0xfb, 0x0f, 0x65, 0xd8, 0xdc,
0xa1, 0xc8, 0xe3, 0x68, 0xc7, 0x88, 0x75, 0xd0, 0x57, 0x09, 0x62, 0xdc, 0x7a, 0x09, 0x5a, 0xa9,
0x2a, 0x2e, 0x0e, 0xfa, 0xa5, 0xab, 0xa5, 0x1b, 0x0d, 0xa7, 0x99, 0xe2, 0x0e, 0x02, 0xeb, 0x32,
0xd4, 0xd0, 0x39, 0xf2, 0xc5, 0x6c, 0x59, 0xce, 0xae, 0x0a, 0xf0, 0x20, 0xb0, 0xde, 0x82, 0x26,
0xe3, 0x14, 0x47, 0x23, 0x37, 0x61, 0x88, 0xf6, 0x2b, 0x57, 0x4b, 0x37, 0x9a, 0xb7, 0xd6, 0x86,
0x42, 0xcf, 0xe1, 0xb1, 0x9c, 0x78, 0xc8, 0x10, 0x75, 0x80, 0xa5, 0x63, 0xeb, 0x3a, 0xd4, 0x02,
0x74, 0x86, 0x7d, 0xc4, 0xfa, 0xd5, 0xab, 0x95, 0x1b, 0xcd, 0x5b, 0x2d, 0x45, 0x7e, 0x4f, 0x22,
0x1d, 0x33, 0x69, 0xbd, 0x06, 0x75, 0xc6, 0x09, 0xf5, 0x46, 0x88, 0xf5, 0x57, 0x24, 0x61, 0xdb,
0xf0, 0x95, 0x58, 0x27, 0x9d, 0xb6, 0x5e, 0x80, 0xca, 0xfd, 0x9d, 0x83, 0xfe, 0xaa, 0x94, 0x0e,
0x9a, 0x2a, 0x46, 0xbe, 0x53, 0x21, 0x3b, 0x07, 0xd6, 0x35, 0x68, 0x33, 0x2f, 0x0a, 0x4e, 0xc8,
0xb9, 0x1b, 0xe3, 0x20, 0x62, 0xfd, 0xda, 0xd5, 0xd2, 0x8d, 0xba, 0xd3, 0xd2, 0xc8, 0x23, 0x81,
0xb3, 0xdf, 0x87, 0x4b, 0xc7, 0xdc, 0xa3, 0xfc, 0x29, 0xbc, 0x63, 0x3f, 0x84, 0x4d, 0x07, 0x85,
0xe4, 0xec, 0xa9, 0x5c, 0xdb, 0x87, 0x1a, 0xc7, 0x21, 0x22, 0x09, 0x97, 0xae, 0x6d, 0x3b, 0x06,
0xb4, 0xff, 0x54, 0x02, 0x6b, 0xf7, 0x1c, 0xf9, 0x47, 0x94, 0xf8, 0x88, 0xb1, 0xff, 0xd3, 0x76,
0xbd, 0x0a, 0xb5, 0x58, 0x29, 0xd0, 0xaf, 0x4a, 0x72, 0xbd, 0x0b, 0x46, 0x2b, 0x33, 0x6b, 0x7f,
0x09, 0x1b, 0xc7, 0x78, 0x14, 0x79, 0x93, 0x67, 0xa8, 0xef, 0x26, 0xac, 0x32, 0xc9, 0x53, 0xaa,
0xda, 0x76, 0x34, 0x64, 0x1f, 0x81, 0xf5, 0x85, 0x87, 0xf9, 0xb3, 0x93, 0x64, 0xbf, 0x01, 0xeb,
0x05, 0x8e, 0x2c, 0x26, 0x11, 0x43, 0x52, 0x01, 0xee, 0xf1, 0x84, 0x49, 0x66, 0x2b, 0x8e, 0x86,
0x6c, 0x02, 0x9b, 0x0f, 0xe3, 0xe0, 0x29, 0x6f, 0xd3, 0x2d, 0x68, 0x50, 0xc4, 0x48, 0x42, 0xc5,
0x1d, 0x28, 0x4b, 0xa7, 0x6e, 0x28, 0xa7, 0x7e, 0x8a, 0xa3, 0xe4, 0xdc, 0x31, 0x73, 0x4e, 0x46,
0xa6, 0xcf, 0x27, 0x67, 0x4f, 0x73, 0x3e, 0xdf, 0x87, 0x4b, 0x47, 0x5e, 0xc2, 0x9e, 0x46, 0x57,
0xfb, 0x03, 0x71, 0xb6, 0x59, 0x12, 0x3e, 0xd5, 0xe2, 0x3f, 0x96, 0xa0, 0xbe, 0x13, 0x27, 0x0f,
0x99, 0x37, 0x42, 0xd6, 0xf7, 0xa0, 0xc9, 0x09, 0xf7, 0x26, 0x6e, 0x22, 0x40, 0x49, 0x5e, 0x75,
0x40, 0xa2, 0x14, 0xc1, 0x4b, 0xd0, 0x8a, 0x11, 0xf5, 0xe3, 0x44, 0x53, 0x94, 0xaf, 0x56, 0x6e,
0x54, 0x9d, 0xa6, 0xc2, 0x29, 0x92, 0x21, 0xac, 0xcb, 0x39, 0x17, 0x47, 0xee, 0x29, 0xa2, 0x11,
0x9a, 0x84, 0x24, 0x40, 0xf2, 0x70, 0x54, 0x9d, 0x9e, 0x9c, 0x3a, 0x88, 0x3e, 0x49, 0x27, 0xac,
0xef, 0x43, 0x2f, 0xa5, 0x17, 0x27, 0x5e, 0x52, 0x57, 0x25, 0x75, 0x57, 0x53, 0x3f, 0xd4, 0x68,
0xfb, 0x97, 0xd0, 0x79, 0x30, 0xa6, 0x84, 0xf3, 0x09, 0x8e, 0x46, 0xf7, 0x3c, 0xee, 0x89, 0xab,
0x19, 0x23, 0x8a, 0x49, 0xc0, 0xb4, 0xb6, 0x06, 0xb4, 0x5e, 0x87, 0x1e, 0x57, 0xb4, 0x28, 0x70,
0x0d, 0x4d, 0x59, 0xd2, 0xac, 0xa5, 0x13, 0x47, 0x9a, 0xf8, 0x15, 0xe8, 0x64, 0xc4, 0xe2, 0x72,
0x6b, 0x7d, 0xdb, 0x29, 0xf6, 0x01, 0x0e, 0x91, 0x7d, 0x26, 0x7d, 0x25, 0x37, 0xd9, 0x7a, 0x1d,
0x1a, 0x99, 0x1f, 0x4a, 0xf2, 0x84, 0x74, 0xd4, 0x09, 0x31, 0xee, 0x74, 0xea, 0xa9, 0x53, 0x3e,
0x84, 0x2e, 0x4f, 0x15, 0x77, 0x03, 0x8f, 0x7b, 0xc5, 0x43, 0x55, 0xb4, 0xca, 0xe9, 0xf0, 0x02,
0x6c, 0x7f, 0x00, 0x8d, 0x23, 0x1c, 0x30, 0x25, 0xb8, 0x0f, 0x35, 0x3f, 0xa1, 0x14, 0x45, 0xdc,
0x98, 0xac, 0x41, 0x6b, 0x03, 0x56, 0x26, 0x38, 0xc4, 0x5c, 0x9b, 0xa9, 0x00, 0x9b, 0x00, 0x1c,
0xa2, 0x90, 0xd0, 0xa9, 0x74, 0xd8, 0x06, 0xac, 0xe4, 0x37, 0x57, 0x01, 0xd6, 0xf3, 0xd0, 0x08,
0xbd, 0xf3, 0x74, 0x53, 0xc5, 0x4c, 0x3d, 0xf4, 0xce, 0x95, 0xf2, 0x7d, 0xa8, 0x3d, 0xf2, 0xf0,
0xc4, 0x8f, 0xb8, 0xf6, 0x8a, 0x01, 0x33, 0x81, 0xd5, 0xbc, 0xc0, 0xbf, 0x96, 0xa1, 0xa9, 0x24,
0x2a, 0x85, 0x37, 0x60, 0xc5, 0xf7, 0xfc, 0x71, 0x2a, 0x52, 0x02, 0xd6, 0x75, 0xa3, 0x48, 0x39,
0x1f, 0xe1, 0x32, 0x4d, 0x8d, 0x6a, 0x37, 0x01, 0xd8, 0x63, 0x2f, 0xd6, 0xba, 0x55, 0x96, 0x10,
0x37, 0x04, 0x8d, 0x52, 0xf7, 0x6d, 0x68, 0xa9, 0x73, 0xa7, 0x97, 0x54, 0x97, 0x2c, 0x69, 0x2a,
0x2a, 0xb5, 0xe8, 0x1a, 0xb4, 0x13, 0x86, 0xdc, 0x31, 0x46, 0xd4, 0xa3, 0xfe, 0x78, 0xda, 0x5f,
0x51, 0x0f, 0x50, 0xc2, 0xd0, 0xbe, 0xc1, 0x59, 0xb7, 0x60, 0x45, 0xc4, 0x16, 0xd6, 0x5f, 0x95,
0x6f, 0xdd, 0x0b, 0x79, 0x96, 0xd2, 0xd4, 0xa1, 0xfc, 0xdd, 0x8d, 0x38, 0x9d, 0x3a, 0x8a, 0x74,
0xf0, 0x2e, 0x40, 0x86, 0xb4, 0xd6, 0xa0, 0x72, 0x8a, 0xa6, 0xfa, 0x1e, 0x8a, 0xa1, 0x70, 0xce,
0x99, 0x37, 0x49, 0x8c, 0xd7, 0x15, 0xf0, 0x7e, 0xf9, 0xdd, 0x92, 0xed, 0x43, 0x77, 0x7b, 0x72,
0x8a, 0x49, 0x6e, 0xf9, 0x06, 0xac, 0x84, 0xde, 0x97, 0x84, 0x1a, 0x4f, 0x4a, 0x40, 0x62, 0x71,
0x44, 0xa8, 0x61, 0x21, 0x01, 0xab, 0x03, 0x65, 0x12, 0x4b, 0x7f, 0x35, 0x9c, 0x32, 0x89, 0x33,
0x41, 0xd5, 0x9c, 0x20, 0xfb, 0x9f, 0x55, 0x80, 0x4c, 0x8a, 0xe5, 0xc0, 0x00, 0x13, 0x97, 0x21,
0x2a, 0xde, 0x77, 0xf7, 0x64, 0xca, 0x11, 0x73, 0x29, 0xf2, 0x13, 0xca, 0xf0, 0x99, 0xd8, 0x3f,
0x61, 0xf6, 0x25, 0x65, 0xf6, 0x8c, 0x6e, 0xce, 0x65, 0x4c, 0x8e, 0xd5, 0xba, 0x6d, 0xb1, 0xcc,
0x31, 0xab, 0xac, 0x03, 0xb8, 0x94, 0xf1, 0x0c, 0x72, 0xec, 0xca, 0x17, 0xb1, 0x5b, 0x4f, 0xd9,
0x05, 0x19, 0xab, 0x5d, 0x58, 0xc7, 0xc4, 0xfd, 0x2a, 0x41, 0x49, 0x81, 0x51, 0xe5, 0x22, 0x46,
0x3d, 0x4c, 0x3e, 0x97, 0x0b, 0x32, 0x36, 0x47, 0x70, 0x25, 0x67, 0xa5, 0xb8, 0xee, 0x39, 0x66,
0xd5, 0x8b, 0x98, 0x6d, 0xa6, 0x5a, 0x89, 0x78, 0x90, 0x71, 0xfc, 0x18, 0x36, 0x31, 0x71, 0x1f,
0x7b, 0x98, 0xcf, 0xb2, 0x5b, 0xf9, 0x0e, 0x23, 0xc5, 0x8b, 0x56, 0xe4, 0xa5, 0x8c, 0x0c, 0x11,
0x1d, 0x15, 0x8c, 0x5c, 0xfd, 0x0e, 0x23, 0x0f, 0xe5, 0x82, 0x8c, 0xcd, 0x5d, 0xe8, 0x61, 0x32,
0xab, 0x4d, 0xed, 0x22, 0x26, 0x5d, 0x4c, 0x8a, 0x9a, 0x6c, 0x43, 0x8f, 0x21, 0x9f, 0x13, 0x9a,
0x3f, 0x04, 0xf5, 0x8b, 0x58, 0xac, 0x69, 0xfa, 0x94, 0x87, 0xfd, 0x53, 0x68, 0xed, 0x27, 0x23,
0xc4, 0x27, 0x27, 0x69, 0x30, 0x78, 0x66, 0xf1, 0xc7, 0xfe, 0x77, 0x19, 0x9a, 0x3b, 0x23, 0x4a,
0x92, 0xb8, 0x10, 0x93, 0xd5, 0x25, 0x9d, 0x8d, 0xc9, 0x92, 0x44, 0xc6, 0x64, 0x45, 0xfc, 0x0e,
0xb4, 0x42, 0x79, 0x75, 0x35, 0xbd, 0x8a, 0x43, 0xbd, 0xb9, 0x4b, 0xed, 0x34, 0xc3, 0x5c, 0x30,
0x1b, 0x02, 0xc4, 0x38, 0x60, 0x7a, 0x8d, 0x0a, 0x47, 0x5d, 0x9d, 0x6e, 0x99, 0x10, 0xed, 0x34,
0xe2, 0x34, 0x5a, 0xbf, 0x05, 0xcd, 0x13, 0xe1, 0x24, 0xbd, 0xa0, 0x10, 0x8c, 0x32, 0xef, 0x39,
0x70, 0x92, 0x5d, 0xc2, 0x7d, 0x68, 0x8f, 0x95, 0xcb, 0xf4, 0x22, 0x75, 0x86, 0xae, 0x69, 0x4b,
0x32, 0x7b, 0x87, 0x79, 0xcf, 0xaa, 0x0d, 0x68, 0x8d, 0x73, 0xa8, 0xc1, 0x31, 0xf4, 0xe6, 0x48,
0x16, 0xc4, 0xa0, 0x1b, 0xf9, 0x18, 0xd4, 0xbc, 0x65, 0x29, 0x41, 0xf9, 0x95, 0xf9, 0xb8, 0xf4,
0xbb, 0x32, 0xb4, 0x3e, 0x43, 0xfc, 0x31, 0xa1, 0xa7, 0x4a, 0x5f, 0x0b, 0xaa, 0x91, 0x17, 0x22,
0xcd, 0x51, 0x8e, 0xad, 0x2b, 0x50, 0xa7, 0xe7, 0x2a, 0x80, 0xe8, 0xfd, 0xac, 0xd1, 0x73, 0x19,
0x18, 0xac, 0x17, 0x01, 0xe8, 0xb9, 0x1b, 0x7b, 0xfe, 0x29, 0xd2, 0x1e, 0xac, 0x3a, 0x0d, 0x7a,
0x7e, 0xa4, 0x10, 0xe2, 0x28, 0xd0, 0x73, 0x17, 0x51, 0x4a, 0x28, 0xd3, 0xb1, 0xaa, 0x4e, 0xcf,
0x77, 0x25, 0xac, 0xd7, 0x06, 0x94, 0xc4, 0x31, 0x0a, 0x64, 0x8c, 0x96, 0x6b, 0xef, 0x29, 0x84,
0x90, 0xca, 0x8d, 0xd4, 0x55, 0x25, 0x95, 0x67, 0x52, 0x79, 0x26, 0xb5, 0xa6, 0x56, 0xf2, 0xbc,
0x54, 0x9e, 0x4a, 0xad, 0x2b, 0xa9, 0x3c, 0x27, 0x95, 0x67, 0x52, 0x1b, 0x66, 0xad, 0x96, 0x6a,
0xff, 0xb6, 0x04, 0x9b, 0xb3, 0x89, 0x9f, 0xce, 0x4d, 0xdf, 0x81, 0x96, 0x2f, 0xf7, 0xab, 0x70,
0x26, 0x7b, 0x73, 0x3b, 0xe9, 0x34, 0xfd, 0xdc, 0x31, 0xbe, 0x0d, 0xed, 0x48, 0x39, 0x38, 0x3d,
0x9a, 0x95, 0x6c, 0x5f, 0xf2, 0xbe, 0x77, 0x5a, 0x51, 0x0e, 0xb2, 0x03, 0xb0, 0xbe, 0xa0, 0x98,
0xa3, 0x63, 0x4e, 0x91, 0x17, 0x3e, 0x8b, 0xec, 0xde, 0x82, 0xaa, 0xcc, 0x56, 0xc4, 0x36, 0xb5,
0x1c, 0x39, 0xb6, 0x5f, 0x85, 0xf5, 0x82, 0x14, 0x6d, 0xeb, 0x1a, 0x54, 0x26, 0x28, 0x92, 0xdc,
0xdb, 0x8e, 0x18, 0xda, 0x1e, 0xf4, 0x1c, 0xe4, 0x05, 0xcf, 0x4e, 0x1b, 0x2d, 0xa2, 0x92, 0x89,
0xb8, 0x01, 0x56, 0x5e, 0x84, 0x56, 0xc5, 0x68, 0x5d, 0xca, 0x69, 0x7d, 0x1f, 0x7a, 0x3b, 0x13,
0xc2, 0xd0, 0x31, 0x0f, 0x70, 0xf4, 0x2c, 0xca, 0x91, 0x5f, 0xc0, 0xfa, 0x03, 0x3e, 0xfd, 0x42,
0x30, 0x63, 0xf8, 0x6b, 0xf4, 0x8c, 0xec, 0xa3, 0xe4, 0xb1, 0xb1, 0x8f, 0x92, 0xc7, 0xa2, 0xb8,
0xf1, 0xc9, 0x24, 0x09, 0x23, 0x79, 0x15, 0xda, 0x8e, 0x86, 0xec, 0x6d, 0x68, 0xa9, 0x1c, 0xfa,
0x90, 0x04, 0xc9, 0x04, 0x2d, 0xbc, 0x83, 0x5b, 0x00, 0xb1, 0x47, 0xbd, 0x10, 0x71, 0x44, 0xd5,
0x19, 0x6a, 0x38, 0x39, 0x8c, 0xfd, 0xfb, 0x32, 0x6c, 0xa8, 0x7e, 0xc3, 0xb1, 0x2a, 0xb3, 0x8d,
0x09, 0x03, 0xa8, 0x8f, 0x09, 0xe3, 0x39, 0x86, 0x29, 0x2c, 0x54, 0x14, 0xf5, 0xb9, 0xe2, 0x26,
0x86, 0x85, 0x26, 0x40, 0xe5, 0xe2, 0x26, 0xc0, 0x5c, 0x99, 0x5f, 0x9d, 0x2f, 0xf3, 0xc5, 0x6d,
0x33, 0x44, 0x58, 0xdd, 0xf1, 0x86, 0xd3, 0xd0, 0x98, 0x83, 0xc0, 0xba, 0x0e, 0xdd, 0x91, 0xd0,
0xd2, 0x1d, 0x13, 0x72, 0xea, 0xc6, 0x1e, 0x1f, 0xcb, 0xab, 0xde, 0x70, 0xda, 0x12, 0xbd, 0x4f,
0xc8, 0xe9, 0x91, 0xc7, 0xc7, 0xd6, 0x7b, 0xd0, 0xd1, 0x69, 0x60, 0x28, 0x5d, 0xc4, 0xf4, 0xe3,
0xa7, 0x6f, 0x51, 0xde, 0x7b, 0x4e, 0xfb, 0x34, 0x07, 0x31, 0xfb, 0x32, 0x5c, 0xba, 0x87, 0x18,
0xa7, 0x64, 0x5a, 0x74, 0x8c, 0xfd, 0x23, 0x80, 0x83, 0x88, 0x23, 0xfa, 0xc8, 0xf3, 0x11, 0xb3,
0xde, 0xcc, 0x43, 0x3a, 0x39, 0x5a, 0x1b, 0xaa, 0x76, 0x4f, 0x3a, 0xe1, 0x00, 0x4e, 0x69, 0xec,
0x21, 0xac, 0x3a, 0x24, 0x11, 0xe1, 0xe8, 0x65, 0x33, 0xd2, 0xeb, 0x5a, 0x7a, 0x9d, 0x44, 0x3a,
0xab, 0x54, 0xce, 0xd9, 0xfb, 0xa6, 0x84, 0xcd, 0xd8, 0xe9, 0x2d, 0x1a, 0x42, 0x23, 0xe5, 0xab,
0xa3, 0xca, 0xbc, 0xe8, 0x8c, 0xc4, 0xfe, 0x00, 0xd6, 0x15, 0x27, 0x25, 0xd5, 0xb0, 0x79, 0x19,
0xb4, 0x28, 0xcd, 0x43, 0xf7, 0x79, 0x34, 0x91, 0x51, 0xe3, 0x32, 0x5c, 0xfa, 0x14, 0x33, 0x9e,
0x19, 0x6b, 0xfc, 0xb1, 0x0e, 0x3d, 0x31, 0x51, 0xe0, 0x69, 0x7f, 0x04, 0xad, 0xbb, 0xce, 0xd1,
0x67, 0x08, 0x8f, 0xc6, 0x27, 0x22, 0x7a, 0xfe, 0xb0, 0x08, 0x6b, 0x83, 0x2d, 0xad, 0x6d, 0x6e,
0xca, 0x69, 0x79, 0x39, 0x3a, 0xfb, 0x63, 0xd8, 0xbc, 0x1b, 0x04, 0xf9, 0xa5, 0x46, 0xeb, 0x37,
0xa1, 0x11, 0xe5, 0xd8, 0xe5, 0xde, 0xac, 0x02, 0x75, 0x46, 0x64, 0xff, 0x0c, 0xd6, 0xef, 0x47,
0x13, 0x1c, 0xa1, 0x9d, 0xa3, 0x87, 0x87, 0x28, 0x8d, 0x45, 0x16, 0x54, 0x45, 0xce, 0x26, 0x79,
0xd4, 0x1d, 0x39, 0x16, 0x97, 0x33, 0x3a, 0x71, 0xfd, 0x38, 0x61, 0xba, 0xd9, 0xb3, 0x1a, 0x9d,
0xec, 0xc4, 0x09, 0x13, 0x8f, 0x8b, 0x48, 0x2e, 0x48, 0x34, 0x99, 0xca, 0x1b, 0x5a, 0x77, 0x6a,
0x7e, 0x9c, 0xdc, 0x8f, 0x26, 0x53, 0xfb, 0x07, 0xb2, 0x02, 0x47, 0x28, 0x70, 0xbc, 0x28, 0x20,
0xe1, 0x3d, 0x74, 0x96, 0x93, 0x90, 0x56, 0x7b, 0x26, 0x12, 0x7d, 0x53, 0x82, 0xd6, 0xdd, 0x11,
0x8a, 0xf8, 0x3d, 0xc4, 0x3d, 0x3c, 0x91, 0x15, 0xdd, 0x19, 0xa2, 0x0c, 0x93, 0x48, 0x5f, 0x37,
0x03, 0x8a, 0x82, 0x1c, 0x47, 0x98, 0xbb, 0x81, 0x87, 0x42, 0x12, 0x49, 0x2e, 0x75, 0x71, 0xa2,
0x30, 0xbf, 0x27, 0x31, 0xd6, 0xab, 0xd0, 0x55, 0xcd, 0x38, 0x77, 0xec, 0x45, 0xc1, 0x44, 0x5c,
0xf4, 0x8a, 0xbc, 0x9a, 0x1d, 0x85, 0xde, 0xd7, 0x58, 0xeb, 0x35, 0x58, 0xd3, 0xd7, 0x30, 0xa3,
0xac, 0x4a, 0xca, 0xae, 0xc6, 0x17, 0x48, 0x93, 0x38, 0x26, 0x94, 0x33, 0x97, 0x21, 0xdf, 0x27,
0x61, 0xac, 0xcb, 0xa1, 0xae, 0xc1, 0x1f, 0x2b, 0xb4, 0x3d, 0x82, 0xf5, 0x3d, 0x61, 0xa7, 0xb6,
0x24, 0x3b, 0x56, 0x9d, 0x10, 0x85, 0xee, 0xc9, 0x84, 0xf8, 0xa7, 0xae, 0x08, 0x8e, 0xda, 0xc3,
0x22, 0xe1, 0xda, 0x16, 0xc8, 0x63, 0xfc, 0xb5, 0xac, 0xfc, 0x05, 0xd5, 0x98, 0xf0, 0x78, 0x92,
0x8c, 0xdc, 0x98, 0x92, 0x13, 0xa4, 0x4d, 0xec, 0x86, 0x28, 0xdc, 0x57, 0xf8, 0x23, 0x81, 0xb6,
0xff, 0x52, 0x82, 0x8d, 0xa2, 0x24, 0x1d, 0xea, 0x6f, 0xc2, 0x46, 0x51, 0x94, 0x7e, 0xfe, 0x55,
0x7a, 0xd9, 0xcb, 0x0b, 0x54, 0x89, 0xc0, 0x6d, 0x68, 0xcb, 0x7e, 0xad, 0x1b, 0x28, 0x4e, 0xc5,
0xa4, 0x27, 0xbf, 0x2f, 0x4e, 0xcb, 0xcb, 0xef, 0xd2, 0x7b, 0x70, 0x45, 0x9b, 0xef, 0xce, 0xab,
0xad, 0x0e, 0xc4, 0xa6, 0x26, 0x38, 0x9c, 0xd1, 0xfe, 0x53, 0xe8, 0x67, 0xa8, 0xed, 0xa9, 0x44,
0x66, 0x87, 0x79, 0x7d, 0xc6, 0xd8, 0xbb, 0x41, 0x40, 0xe5, 0x2d, 0xa9, 0x3a, 0x8b, 0xa6, 0xec,
0x3b, 0x70, 0xf9, 0x18, 0x71, 0xe5, 0x0d, 0x8f, 0xeb, 0x4a, 0x44, 0x31, 0x5b, 0x83, 0xca, 0x31,
0xf2, 0xa5, 0xf1, 0x15, 0xa7, 0xc2, 0x90, 0x2f, 0x0e, 0xe0, 0x43, 0x86, 0x7c, 0x69, 0x65, 0xc5,
0xa9, 0x26, 0x0c, 0xf9, 0xf6, 0x9f, 0x4b, 0x50, 0xd3, 0xc1, 0x59, 0x3c, 0x30, 0x01, 0xc5, 0x67,
0x88, 0xea, 0xa3, 0xa7, 0x21, 0xeb, 0x15, 0xe8, 0xa8, 0x91, 0x4b, 0x62, 0x8e, 0x49, 0x1a, 0xf2,
0xdb, 0x0a, 0x7b, 0x5f, 0x21, 0x65, 0xf3, 0x4d, 0xb6, 0xbf, 0x74, 0xa5, 0xa9, 0x21, 0x81, 0x7f,
0xc4, 0xc4, 0x0d, 0x97, 0x21, 0xbe, 0xe1, 0x68, 0x48, 0x1c, 0x75, 0xc3, 0x6f, 0x45, 0xf2, 0x33,
0xa0, 0x38, 0xea, 0x21, 0x49, 0x22, 0xee, 0xc6, 0x04, 0x47, 0x5c, 0xc7, 0x74, 0x90, 0xa8, 0x23,
0x81, 0xb1, 0x7f, 0x53, 0x82, 0x55, 0xd5, 0x80, 0x16, 0xb5, 0x6d, 0xfa, 0xb2, 0x96, 0xb1, 0xcc,
0x52, 0xa4, 0x2c, 0xf5, 0x9a, 0xca, 0xb1, 0xb8, 0xc7, 0x67, 0xa1, 0x7a, 0x1f, 0xb4, 0x6a, 0x67,
0xa1, 0x7c, 0x18, 0x5e, 0x81, 0x4e, 0xf6, 0x40, 0xcb, 0x79, 0xa5, 0x62, 0x3b, 0xc5, 0x4a, 0xb2,
0xa5, 0x9a, 0xda, 0x3f, 0x16, 0x25, 0x7d, 0xda, 0x7c, 0x5d, 0x83, 0x4a, 0x92, 0x2a, 0x23, 0x86,
0x02, 0x33, 0x4a, 0x9f, 0x76, 0x31, 0xb4, 0xae, 0x43, 0xc7, 0x0b, 0x02, 0x2c, 0x96, 0x7b, 0x93,
0x3d, 0x1c, 0xa4, 0x97, 0xb4, 0x88, 0xb5, 0xff, 0x56, 0x82, 0xee, 0x0e, 0x89, 0xa7, 0x1f, 0xe1,
0x09, 0xca, 0x45, 0x10, 0xa9, 0xa4, 0x7e, 0xd9, 0xc5, 0x58, 0x64, 0xab, 0x8f, 0xf0, 0x04, 0xa9,
0xab, 0xa5, 0x76, 0xb6, 0x2e, 0x10, 0xf2, 0x5a, 0x99, 0xc9, 0xb4, 0xed, 0xd6, 0x56, 0x93, 0x87,
0x24, 0x90, 0x79, 0x79, 0x80, 0xa9, 0x9b, 0x36, 0xd9, 0xda, 0x4e, 0x2d, 0xc0, 0x54, 0x4e, 0x69,
0x43, 0x56, 0x64, 0x13, 0x35, 0x6f, 0xc8, 0xaa, 0xc2, 0x08, 0x43, 0x36, 0x61, 0x95, 0x3c, 0x7a,
0xc4, 0x10, 0x97, 0x19, 0x74, 0xc5, 0xd1, 0x50, 0x1a, 0xe6, 0xea, 0xb9, 0x30, 0x77, 0x09, 0xd6,
0x65, 0xbb, 0xfe, 0x01, 0xf5, 0x7c, 0x1c, 0x8d, 0xcc, 0xf3, 0xb0, 0x01, 0xd6, 0x31, 0x27, 0xf1,
0x3c, 0x76, 0x0f, 0xf1, 0xfb, 0xf7, 0x0f, 0x77, 0xcf, 0x50, 0xc4, 0x0d, 0xf6, 0x0d, 0xa8, 0x1b,
0xd4, 0x7f, 0xd3, 0xcb, 0x5c, 0x87, 0xde, 0x1e, 0xe2, 0x87, 0x88, 0x53, 0xec, 0xa7, 0xcf, 0xd1,
0x35, 0xa8, 0x69, 0x8c, 0xd8, 0xd2, 0x50, 0x0d, 0x4d, 0x9c, 0xd5, 0xe0, 0xad, 0x5f, 0xf7, 0x74,
0x48, 0xd6, 0xd5, 0xbd, 0xb5, 0x07, 0xdd, 0x99, 0x4f, 0x31, 0x96, 0x6e, 0xf7, 0x2c, 0xfe, 0x42,
0x33, 0xd8, 0x1c, 0xaa, 0x4f, 0x3b, 0x43, 0xf3, 0x69, 0x67, 0xb8, 0x1b, 0xc6, 0x7c, 0x6a, 0xed,
0x42, 0xa7, 0xf8, 0xd1, 0xc2, 0x7a, 0xde, 0x64, 0x47, 0x0b, 0x3e, 0x65, 0x2c, 0x65, 0xb3, 0x07,
0xdd, 0x99, 0xef, 0x17, 0x46, 0x9f, 0xc5, 0x9f, 0x35, 0x96, 0x32, 0xba, 0x03, 0xcd, 0xdc, 0x07,
0x0b, 0xab, 0xaf, 0x98, 0xcc, 0x7f, 0xc3, 0x58, 0xca, 0x60, 0x07, 0xda, 0x85, 0x6f, 0x08, 0xd6,
0x40, 0xdb, 0xb3, 0xe0, 0xc3, 0xc2, 0x52, 0x26, 0xdb, 0xd0, 0xcc, 0xb5, 0xf2, 0x8d, 0x16, 0xf3,
0xdf, 0x0b, 0x06, 0x57, 0x16, 0xcc, 0xe8, 0xc8, 0xbf, 0x07, 0xdd, 0x99, 0xfe, 0xbe, 0x71, 0xc9,
0xe2, 0xb6, 0xff, 0x52, 0x65, 0x3e, 0x91, 0x5b, 0x94, 0x2b, 0xdf, 0x72, 0x5b, 0x34, 0xdf, 0xcd,
0x1f, 0xbc, 0xb0, 0x78, 0x52, 0x6b, 0xb5, 0x0b, 0x9d, 0x62, 0x23, 0xdf, 0x30, 0x5b, 0xd8, 0xde,
0xbf, 0x78, 0xbf, 0x0b, 0x3d, 0xfd, 0x6c, 0xbf, 0x17, 0xb5, 0xfa, 0x97, 0x32, 0xba, 0x0b, 0xa0,
0x8b, 0xb5, 0x00, 0x47, 0xa9, 0xa3, 0xe7, 0x8a, 0xc4, 0xd4, 0xd1, 0x0b, 0x0a, 0xbb, 0x3b, 0x00,
0xaa, 0xc6, 0x0a, 0x48, 0xc2, 0xad, 0xcb, 0x46, 0x8d, 0x99, 0xc2, 0x6e, 0xd0, 0x9f, 0x9f, 0x98,
0x63, 0x80, 0x28, 0x7d, 0x1a, 0x06, 0x1f, 0x02, 0x64, 0xb5, 0x9b, 0x61, 0x30, 0x57, 0xcd, 0x5d,
0xe0, 0x83, 0x56, 0xbe, 0x52, 0xb3, 0xb4, 0xad, 0x0b, 0xaa, 0xb7, 0x0b, 0x58, 0x74, 0x67, 0x32,
0xf1, 0xe2, 0x61, 0x9b, 0x4d, 0xd0, 0x07, 0x73, 0xd9, 0xb8, 0x75, 0x1b, 0x5a, 0xf9, 0x14, 0xdc,
0x68, 0xb1, 0x20, 0x2d, 0x1f, 0x14, 0xd2, 0x70, 0xeb, 0x0e, 0x74, 0x8a, 0xe9, 0xb7, 0x39, 0x52,
0x0b, 0x93, 0xf2, 0x81, 0x6e, 0x2e, 0xe5, 0xc8, 0xdf, 0x06, 0xc8, 0xd2, 0x74, 0xe3, 0xbe, 0xb9,
0xc4, 0x7d, 0x46, 0xea, 0x1e, 0x74, 0x67, 0xd2, 0x6f, 0x63, 0xf1, 0xe2, 0xac, 0xfc, 0x22, 0xef,
0xe7, 0xdf, 0x01, 0x63, 0xf7, 0x82, 0xb7, 0xe1, 0xa2, 0xa0, 0x95, 0x7b, 0x33, 0xcc, 0x29, 0x9e,
0x7f, 0x46, 0x96, 0x32, 0x78, 0x07, 0x20, 0x7b, 0x19, 0x8c, 0x07, 0xe6, 0xde, 0x8a, 0x41, 0xdb,
0x34, 0xff, 0x14, 0xdd, 0x0e, 0xb4, 0x0b, 0xf5, 0xb1, 0x09, 0x75, 0x8b, 0x8a, 0xe6, 0x8b, 0x1e,
0x80, 0x62, 0x31, 0x69, 0x76, 0x6f, 0x61, 0x89, 0x79, 0x91, 0x17, 0xf3, 0x15, 0x8c, 0xf1, 0xe2,
0x82, 0xaa, 0xe6, 0x3b, 0x62, 0x4a, 0xbe, 0x4a, 0xc9, 0xc5, 0x94, 0x05, 0xc5, 0xcb, 0x52, 0x46,
0xfb, 0xd0, 0xdd, 0x33, 0x09, 0xa8, 0x4e, 0x8e, 0xb5, 0x3a, 0x0b, 0x8a, 0x81, 0xc1, 0x60, 0xd1,
0x94, 0xbe, 0xd8, 0x9f, 0x40, 0x6f, 0x2e, 0x31, 0xb6, 0xb6, 0xd2, 0x16, 0xec, 0xc2, 0x8c, 0x79,
0xa9, 0x5a, 0x07, 0xb0, 0x36, 0x9b, 0x17, 0x5b, 0x2f, 0xea, 0xa3, 0xb2, 0x38, 0x5f, 0x5e, 0xca,
0xea, 0x3d, 0xa8, 0x9b, 0x3c, 0xcc, 0xd2, 0xad, 0xee, 0x99, 0xbc, 0x6c, 0xe9, 0xd2, 0xdb, 0xd0,
0xcc, 0x65, 0x32, 0xe6, 0xac, 0xce, 0x27, 0x37, 0x03, 0xdd, 0x99, 0x36, 0xe8, 0xed, 0xf3, 0x6f,
0xbe, 0xdd, 0x7a, 0xee, 0x1f, 0xdf, 0x6e, 0x3d, 0xf7, 0xab, 0x27, 0x5b, 0xa5, 0x6f, 0x9e, 0x6c,
0x95, 0xfe, 0xfe, 0x64, 0xab, 0xf4, 0xaf, 0x27, 0x5b, 0xa5, 0x9f, 0xfc, 0xfc, 0x7f, 0xfc, 0x9b,
0x0a, 0x4d, 0x22, 0x8e, 0x43, 0x74, 0xf3, 0x0c, 0x53, 0x9e, 0x9b, 0x8a, 0x4f, 0x47, 0x73, 0xff,
0x60, 0x11, 0x2a, 0x9c, 0xac, 0x4a, 0xf8, 0xed, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x22, 0x03,
0xd8, 0x19, 0x0f, 0x23, 0x00, 0x00,
}
func (m *CreateContainerRequest) Marshal() (dAtA []byte, err error) {

View File

@ -29,25 +29,25 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Spec struct {
// Version of the Open Container Initiative Runtime Specification with which the bundle complies.
Version string `protobuf:"bytes,1,opt,name=Version,proto3" json:"Version,omitempty"`
Version string `protobuf:"bytes,1,opt,name=Version,json=version,proto3" json:"Version,omitempty"`
// Process configures the container process.
Process *Process `protobuf:"bytes,2,opt,name=Process,proto3" json:"Process,omitempty"`
Process *Process `protobuf:"bytes,2,opt,name=Process,json=process,proto3" json:"Process,omitempty"`
// Root configures the container's root filesystem.
Root *Root `protobuf:"bytes,3,opt,name=Root,proto3" json:"Root,omitempty"`
Root *Root `protobuf:"bytes,3,opt,name=Root,json=root,proto3" json:"Root,omitempty"`
// Hostname configures the container's hostname.
Hostname string `protobuf:"bytes,4,opt,name=Hostname,proto3" json:"Hostname,omitempty"`
Hostname string `protobuf:"bytes,4,opt,name=Hostname,json=hostname,proto3" json:"Hostname,omitempty"`
// Mounts configures additional mounts (on top of Root).
Mounts []Mount `protobuf:"bytes,5,rep,name=Mounts,proto3" json:"Mounts"`
Mounts []Mount `protobuf:"bytes,5,rep,name=Mounts,json=mounts,proto3" json:"Mounts"`
// Hooks configures callbacks for container lifecycle events.
Hooks *Hooks `protobuf:"bytes,6,opt,name=Hooks,proto3" json:"Hooks,omitempty"`
Hooks *Hooks `protobuf:"bytes,6,opt,name=Hooks,json=hooks,proto3" json:"Hooks,omitempty"`
// Annotations contains arbitrary metadata for the container.
Annotations map[string]string `protobuf:"bytes,7,rep,name=Annotations,proto3" json:"Annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Annotations map[string]string `protobuf:"bytes,7,rep,name=Annotations,json=annotations,proto3" json:"Annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Linux is platform-specific configuration for Linux based containers.
Linux *Linux `protobuf:"bytes,8,opt,name=Linux,proto3" json:"Linux,omitempty"`
Linux *Linux `protobuf:"bytes,8,opt,name=Linux,json=linux,proto3" json:"Linux,omitempty"`
// Solaris is platform-specific configuration for Solaris based containers.
Solaris *Solaris `protobuf:"bytes,9,opt,name=Solaris,proto3" json:"Solaris,omitempty"`
Solaris *Solaris `protobuf:"bytes,9,opt,name=Solaris,json=solaris,proto3" json:"Solaris,omitempty"`
// Windows is platform-specific configuration for Windows based containers.
Windows *Windows `protobuf:"bytes,10,opt,name=Windows,proto3" json:"Windows,omitempty"`
Windows *Windows `protobuf:"bytes,10,opt,name=Windows,json=windows,proto3" json:"Windows,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -87,30 +87,30 @@ var xxx_messageInfo_Spec proto.InternalMessageInfo
type Process struct {
// Terminal creates an interactive terminal for the container.
Terminal bool `protobuf:"varint,1,opt,name=Terminal,proto3" json:"Terminal,omitempty"`
Terminal bool `protobuf:"varint,1,opt,name=Terminal,json=terminal,proto3" json:"Terminal,omitempty"`
// ConsoleSize specifies the size of the console.
ConsoleSize *Box `protobuf:"bytes,2,opt,name=ConsoleSize,proto3" json:"ConsoleSize,omitempty"`
ConsoleSize *Box `protobuf:"bytes,2,opt,name=ConsoleSize,json=consoleSize,proto3" json:"ConsoleSize,omitempty"`
// User specifies user information for the process.
User User `protobuf:"bytes,3,opt,name=User,proto3" json:"User"`
User User `protobuf:"bytes,3,opt,name=User,json=user,proto3" json:"User"`
// Args specifies the binary and arguments for the application to execute.
Args []string `protobuf:"bytes,4,rep,name=Args,proto3" json:"Args,omitempty"`
Args []string `protobuf:"bytes,4,rep,name=Args,json=args,proto3" json:"Args,omitempty"`
// Env populates the process environment for the process.
Env []string `protobuf:"bytes,5,rep,name=Env,proto3" json:"Env,omitempty"`
Env []string `protobuf:"bytes,5,rep,name=Env,json=env,proto3" json:"Env,omitempty"`
// Cwd is the current working directory for the process and must be
// relative to the container's root.
Cwd string `protobuf:"bytes,6,opt,name=Cwd,proto3" json:"Cwd,omitempty"`
Cwd string `protobuf:"bytes,6,opt,name=Cwd,json=cwd,proto3" json:"Cwd,omitempty"`
// Capabilities are Linux capabilities that are kept for the process.
Capabilities *LinuxCapabilities `protobuf:"bytes,7,opt,name=Capabilities,proto3" json:"Capabilities,omitempty"`
Capabilities *LinuxCapabilities `protobuf:"bytes,7,opt,name=Capabilities,json=capabilities,proto3" json:"Capabilities,omitempty"`
// Rlimits specifies rlimit options to apply to the process.
Rlimits []POSIXRlimit `protobuf:"bytes,8,rep,name=Rlimits,proto3" json:"Rlimits"`
Rlimits []POSIXRlimit `protobuf:"bytes,8,rep,name=Rlimits,json=rlimits,proto3" json:"Rlimits"`
// NoNewPrivileges controls whether additional privileges could be gained by processes in the container.
NoNewPrivileges bool `protobuf:"varint,9,opt,name=NoNewPrivileges,proto3" json:"NoNewPrivileges,omitempty"`
NoNewPrivileges bool `protobuf:"varint,9,opt,name=NoNewPrivileges,json=noNewPrivileges,proto3" json:"NoNewPrivileges,omitempty"`
// ApparmorProfile specifies the apparmor profile for the container.
ApparmorProfile string `protobuf:"bytes,10,opt,name=ApparmorProfile,proto3" json:"ApparmorProfile,omitempty"`
ApparmorProfile string `protobuf:"bytes,10,opt,name=ApparmorProfile,json=apparmorProfile,proto3" json:"ApparmorProfile,omitempty"`
// Specify an oom_score_adj for the container.
OOMScoreAdj int64 `protobuf:"varint,11,opt,name=OOMScoreAdj,proto3" json:"OOMScoreAdj,omitempty"`
OOMScoreAdj int64 `protobuf:"varint,11,opt,name=OOMScoreAdj,json=oOMScoreAdj,proto3" json:"OOMScoreAdj,omitempty"`
// SelinuxLabel specifies the selinux context that the container process is run as.
SelinuxLabel string `protobuf:"bytes,12,opt,name=SelinuxLabel,proto3" json:"SelinuxLabel,omitempty"`
SelinuxLabel string `protobuf:"bytes,12,opt,name=SelinuxLabel,json=selinuxLabel,proto3" json:"SelinuxLabel,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -150,9 +150,9 @@ var xxx_messageInfo_Process proto.InternalMessageInfo
type Box struct {
// Height is the vertical dimension of a box.
Height uint32 `protobuf:"varint,1,opt,name=Height,proto3" json:"Height,omitempty"`
Height uint32 `protobuf:"varint,1,opt,name=Height,json=height,proto3" json:"Height,omitempty"`
// Width is the horizontal dimension of a box.
Width uint32 `protobuf:"varint,2,opt,name=Width,proto3" json:"Width,omitempty"`
Width uint32 `protobuf:"varint,2,opt,name=Width,json=width,proto3" json:"Width,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -192,13 +192,13 @@ var xxx_messageInfo_Box proto.InternalMessageInfo
type User struct {
// UID is the user id.
UID uint32 `protobuf:"varint,1,opt,name=UID,proto3" json:"UID,omitempty"`
UID uint32 `protobuf:"varint,1,opt,name=UID,json=uID,proto3" json:"UID,omitempty"`
// GID is the group id.
GID uint32 `protobuf:"varint,2,opt,name=GID,proto3" json:"GID,omitempty"`
GID uint32 `protobuf:"varint,2,opt,name=GID,json=gID,proto3" json:"GID,omitempty"`
// AdditionalGids are additional group ids set for the container's process.
AdditionalGids []uint32 `protobuf:"varint,3,rep,packed,name=AdditionalGids,proto3" json:"AdditionalGids,omitempty"`
AdditionalGids []uint32 `protobuf:"varint,3,rep,packed,name=AdditionalGids,json=additionalGids,proto3" json:"AdditionalGids,omitempty"`
// Username is the user name.
Username string `protobuf:"bytes,4,opt,name=Username,proto3" json:"Username,omitempty"`
Username string `protobuf:"bytes,4,opt,name=Username,json=username,proto3" json:"Username,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -238,15 +238,15 @@ var xxx_messageInfo_User proto.InternalMessageInfo
type LinuxCapabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string `protobuf:"bytes,1,rep,name=Bounding,proto3" json:"Bounding,omitempty"`
Bounding []string `protobuf:"bytes,1,rep,name=Bounding,json=bounding,proto3" json:"Bounding,omitempty"`
// Effective is the set of capabilities checked by the kernel.
Effective []string `protobuf:"bytes,2,rep,name=Effective,proto3" json:"Effective,omitempty"`
Effective []string `protobuf:"bytes,2,rep,name=Effective,json=effective,proto3" json:"Effective,omitempty"`
// Inheritable is the capabilities preserved across execve.
Inheritable []string `protobuf:"bytes,3,rep,name=Inheritable,proto3" json:"Inheritable,omitempty"`
Inheritable []string `protobuf:"bytes,3,rep,name=Inheritable,json=inheritable,proto3" json:"Inheritable,omitempty"`
// Permitted is the limiting superset for effective capabilities.
Permitted []string `protobuf:"bytes,4,rep,name=Permitted,proto3" json:"Permitted,omitempty"`
Permitted []string `protobuf:"bytes,4,rep,name=Permitted,json=permitted,proto3" json:"Permitted,omitempty"`
// Ambient is the ambient set of capabilities that are kept.
Ambient []string `protobuf:"bytes,5,rep,name=Ambient,proto3" json:"Ambient,omitempty"`
Ambient []string `protobuf:"bytes,5,rep,name=Ambient,json=ambient,proto3" json:"Ambient,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -286,11 +286,11 @@ var xxx_messageInfo_LinuxCapabilities proto.InternalMessageInfo
type POSIXRlimit struct {
// Type of the rlimit to set
Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"`
Type string `protobuf:"bytes,1,opt,name=Type,json=type,proto3" json:"Type,omitempty"`
// Hard is the hard limit for the specified type
Hard uint64 `protobuf:"varint,2,opt,name=Hard,proto3" json:"Hard,omitempty"`
Hard uint64 `protobuf:"varint,2,opt,name=Hard,json=hard,proto3" json:"Hard,omitempty"`
// Soft is the soft limit for the specified type
Soft uint64 `protobuf:"varint,3,opt,name=Soft,proto3" json:"Soft,omitempty"`
Soft uint64 `protobuf:"varint,3,opt,name=Soft,json=soft,proto3" json:"Soft,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -377,9 +377,9 @@ var xxx_messageInfo_Mount proto.InternalMessageInfo
type Root struct {
// Path is the absolute path to the container's root filesystem.
Path string `protobuf:"bytes,1,opt,name=Path,proto3" json:"Path,omitempty"`
Path string `protobuf:"bytes,1,opt,name=Path,json=path,proto3" json:"Path,omitempty"`
// Readonly makes the root filesystem for the container readonly before the process is executed.
Readonly bool `protobuf:"varint,2,opt,name=Readonly,proto3" json:"Readonly,omitempty"`
Readonly bool `protobuf:"varint,2,opt,name=Readonly,json=readonly,proto3" json:"Readonly,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -419,11 +419,11 @@ var xxx_messageInfo_Root proto.InternalMessageInfo
type Hooks struct {
// Prestart is a list of hooks to be run before the container process is executed.
Prestart []Hook `protobuf:"bytes,1,rep,name=Prestart,proto3" json:"Prestart"`
Prestart []Hook `protobuf:"bytes,1,rep,name=Prestart,json=prestart,proto3" json:"Prestart"`
// Poststart is a list of hooks to be run after the container process is started.
Poststart []Hook `protobuf:"bytes,2,rep,name=Poststart,proto3" json:"Poststart"`
Poststart []Hook `protobuf:"bytes,2,rep,name=Poststart,json=poststart,proto3" json:"Poststart"`
// Poststop is a list of hooks to be run after the container process exits.
Poststop []Hook `protobuf:"bytes,3,rep,name=Poststop,proto3" json:"Poststop"`
Poststop []Hook `protobuf:"bytes,3,rep,name=Poststop,json=poststop,proto3" json:"Poststop"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -462,10 +462,10 @@ func (m *Hooks) XXX_DiscardUnknown() {
var xxx_messageInfo_Hooks proto.InternalMessageInfo
type Hook struct {
Path string `protobuf:"bytes,1,opt,name=Path,proto3" json:"Path,omitempty"`
Args []string `protobuf:"bytes,2,rep,name=Args,proto3" json:"Args,omitempty"`
Env []string `protobuf:"bytes,3,rep,name=Env,proto3" json:"Env,omitempty"`
Timeout int64 `protobuf:"varint,4,opt,name=Timeout,proto3" json:"Timeout,omitempty"`
Path string `protobuf:"bytes,1,opt,name=Path,json=path,proto3" json:"Path,omitempty"`
Args []string `protobuf:"bytes,2,rep,name=Args,json=args,proto3" json:"Args,omitempty"`
Env []string `protobuf:"bytes,3,rep,name=Env,json=env,proto3" json:"Env,omitempty"`
Timeout int64 `protobuf:"varint,4,opt,name=Timeout,json=timeout,proto3" json:"Timeout,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -505,35 +505,35 @@ var xxx_messageInfo_Hook proto.InternalMessageInfo
type Linux struct {
// UIDMapping specifies user mappings for supporting user namespaces.
UIDMappings []LinuxIDMapping `protobuf:"bytes,1,rep,name=UIDMappings,proto3" json:"UIDMappings"`
UIDMappings []LinuxIDMapping `protobuf:"bytes,1,rep,name=UIDMappings,json=uIDMappings,proto3" json:"UIDMappings"`
// GIDMapping specifies group mappings for supporting user namespaces.
GIDMappings []LinuxIDMapping `protobuf:"bytes,2,rep,name=GIDMappings,proto3" json:"GIDMappings"`
GIDMappings []LinuxIDMapping `protobuf:"bytes,2,rep,name=GIDMappings,json=gIDMappings,proto3" json:"GIDMappings"`
// Sysctl are a set of key value pairs that are set for the container on start
Sysctl map[string]string `protobuf:"bytes,3,rep,name=Sysctl,proto3" json:"Sysctl,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Sysctl map[string]string `protobuf:"bytes,3,rep,name=Sysctl,json=sysctl,proto3" json:"Sysctl,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Resources contain cgroup information for handling resource constraints
// for the container
Resources *LinuxResources `protobuf:"bytes,4,opt,name=Resources,proto3" json:"Resources,omitempty"`
Resources *LinuxResources `protobuf:"bytes,4,opt,name=Resources,json=resources,proto3" json:"Resources,omitempty"`
// CgroupsPath specifies the path to cgroups that are created and/or joined by the container.
// The path is expected to be relative to the cgroups mountpoint.
// If resources are specified, the cgroups at CgroupsPath will be updated based on resources.
CgroupsPath string `protobuf:"bytes,5,opt,name=CgroupsPath,proto3" json:"CgroupsPath,omitempty"`
CgroupsPath string `protobuf:"bytes,5,opt,name=CgroupsPath,json=cgroupsPath,proto3" json:"CgroupsPath,omitempty"`
// Namespaces contains the namespaces that are created and/or joined by the container
Namespaces []LinuxNamespace `protobuf:"bytes,6,rep,name=Namespaces,proto3" json:"Namespaces"`
Namespaces []LinuxNamespace `protobuf:"bytes,6,rep,name=Namespaces,json=namespaces,proto3" json:"Namespaces"`
// Devices are a list of device nodes that are created for the container
Devices []LinuxDevice `protobuf:"bytes,7,rep,name=Devices,proto3" json:"Devices"`
Devices []LinuxDevice `protobuf:"bytes,7,rep,name=Devices,json=devices,proto3" json:"Devices"`
// Seccomp specifies the seccomp security settings for the container.
Seccomp *LinuxSeccomp `protobuf:"bytes,8,opt,name=Seccomp,proto3" json:"Seccomp,omitempty"`
Seccomp *LinuxSeccomp `protobuf:"bytes,8,opt,name=Seccomp,json=seccomp,proto3" json:"Seccomp,omitempty"`
// RootfsPropagation is the rootfs mount propagation mode for the container.
RootfsPropagation string `protobuf:"bytes,9,opt,name=RootfsPropagation,proto3" json:"RootfsPropagation,omitempty"`
RootfsPropagation string `protobuf:"bytes,9,opt,name=RootfsPropagation,json=rootfsPropagation,proto3" json:"RootfsPropagation,omitempty"`
// MaskedPaths masks over the provided paths inside the container.
MaskedPaths []string `protobuf:"bytes,10,rep,name=MaskedPaths,proto3" json:"MaskedPaths,omitempty"`
MaskedPaths []string `protobuf:"bytes,10,rep,name=MaskedPaths,json=maskedPaths,proto3" json:"MaskedPaths,omitempty"`
// ReadonlyPaths sets the provided paths as RO inside the container.
ReadonlyPaths []string `protobuf:"bytes,11,rep,name=ReadonlyPaths,proto3" json:"ReadonlyPaths,omitempty"`
ReadonlyPaths []string `protobuf:"bytes,11,rep,name=ReadonlyPaths,json=readonlyPaths,proto3" json:"ReadonlyPaths,omitempty"`
// MountLabel specifies the selinux context for the mounts in the container.
MountLabel string `protobuf:"bytes,12,opt,name=MountLabel,proto3" json:"MountLabel,omitempty"`
MountLabel string `protobuf:"bytes,12,opt,name=MountLabel,json=mountLabel,proto3" json:"MountLabel,omitempty"`
// IntelRdt contains Intel Resource Director Technology (RDT) information
// for handling resource constraints (e.g., L3 cache) for the container
IntelRdt *LinuxIntelRdt `protobuf:"bytes,13,opt,name=IntelRdt,proto3" json:"IntelRdt,omitempty"`
IntelRdt *LinuxIntelRdt `protobuf:"bytes,13,opt,name=IntelRdt,json=intelRdt,proto3" json:"IntelRdt,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -653,11 +653,11 @@ var xxx_messageInfo_Solaris proto.InternalMessageInfo
type LinuxIDMapping struct {
// HostID is the starting UID/GID on the host to be mapped to 'ContainerID'
HostID uint32 `protobuf:"varint,1,opt,name=HostID,proto3" json:"HostID,omitempty"`
HostID uint32 `protobuf:"varint,1,opt,name=HostID,json=hostID,proto3" json:"HostID,omitempty"`
// ContainerID is the starting UID/GID in the container
ContainerID uint32 `protobuf:"varint,2,opt,name=ContainerID,proto3" json:"ContainerID,omitempty"`
ContainerID uint32 `protobuf:"varint,2,opt,name=ContainerID,json=containerID,proto3" json:"ContainerID,omitempty"`
// Size is the number of IDs to be mapped
Size_ uint32 `protobuf:"varint,3,opt,name=Size,proto3" json:"Size,omitempty"`
Size_ uint32 `protobuf:"varint,3,opt,name=Size,json=size,proto3" json:"Size,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -697,10 +697,10 @@ var xxx_messageInfo_LinuxIDMapping proto.InternalMessageInfo
type LinuxNamespace struct {
// Type is the type of namespace
Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"`
Type string `protobuf:"bytes,1,opt,name=Type,json=type,proto3" json:"Type,omitempty"`
// Path is a path to an existing namespace persisted on disk that can be joined
// and is of the same type
Path string `protobuf:"bytes,2,opt,name=Path,proto3" json:"Path,omitempty"`
Path string `protobuf:"bytes,2,opt,name=Path,json=path,proto3" json:"Path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -740,19 +740,19 @@ var xxx_messageInfo_LinuxNamespace proto.InternalMessageInfo
type LinuxDevice struct {
// Path to the device.
Path string `protobuf:"bytes,1,opt,name=Path,proto3" json:"Path,omitempty"`
Path string `protobuf:"bytes,1,opt,name=Path,json=path,proto3" json:"Path,omitempty"`
// Device type, block, char, etc.
Type string `protobuf:"bytes,2,opt,name=Type,proto3" json:"Type,omitempty"`
Type string `protobuf:"bytes,2,opt,name=Type,json=type,proto3" json:"Type,omitempty"`
// Major is the device's major number.
Major int64 `protobuf:"varint,3,opt,name=Major,proto3" json:"Major,omitempty"`
Major int64 `protobuf:"varint,3,opt,name=Major,json=major,proto3" json:"Major,omitempty"`
// Minor is the device's minor number.
Minor int64 `protobuf:"varint,4,opt,name=Minor,proto3" json:"Minor,omitempty"`
Minor int64 `protobuf:"varint,4,opt,name=Minor,json=minor,proto3" json:"Minor,omitempty"`
// FileMode permission bits for the device.
FileMode uint32 `protobuf:"varint,5,opt,name=FileMode,proto3" json:"FileMode,omitempty"`
FileMode uint32 `protobuf:"varint,5,opt,name=FileMode,json=fileMode,proto3" json:"FileMode,omitempty"`
// UID of the device.
UID uint32 `protobuf:"varint,6,opt,name=UID,proto3" json:"UID,omitempty"`
UID uint32 `protobuf:"varint,6,opt,name=UID,json=uID,proto3" json:"UID,omitempty"`
// Gid of the device.
GID uint32 `protobuf:"varint,7,opt,name=GID,proto3" json:"GID,omitempty"`
GID uint32 `protobuf:"varint,7,opt,name=GID,json=gID,proto3" json:"GID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -792,19 +792,19 @@ var xxx_messageInfo_LinuxDevice proto.InternalMessageInfo
type LinuxResources struct {
// Devices configures the device whitelist.
Devices []LinuxDeviceCgroup `protobuf:"bytes,1,rep,name=Devices,proto3" json:"Devices"`
Devices []LinuxDeviceCgroup `protobuf:"bytes,1,rep,name=Devices,json=devices,proto3" json:"Devices"`
// Memory restriction configuration
Memory *LinuxMemory `protobuf:"bytes,2,opt,name=Memory,proto3" json:"Memory,omitempty"`
Memory *LinuxMemory `protobuf:"bytes,2,opt,name=Memory,json=memory,proto3" json:"Memory,omitempty"`
// CPU resource restriction configuration
CPU *LinuxCPU `protobuf:"bytes,3,opt,name=CPU,proto3" json:"CPU,omitempty"`
CPU *LinuxCPU `protobuf:"bytes,3,opt,name=CPU,json=cPU,proto3" json:"CPU,omitempty"`
// Task resource restriction configuration.
Pids *LinuxPids `protobuf:"bytes,4,opt,name=Pids,proto3" json:"Pids,omitempty"`
Pids *LinuxPids `protobuf:"bytes,4,opt,name=Pids,json=pids,proto3" json:"Pids,omitempty"`
// BlockIO restriction configuration
BlockIO *LinuxBlockIO `protobuf:"bytes,5,opt,name=BlockIO,proto3" json:"BlockIO,omitempty"`
BlockIO *LinuxBlockIO `protobuf:"bytes,5,opt,name=BlockIO,json=blockIO,proto3" json:"BlockIO,omitempty"`
// Hugetlb limit (in bytes)
HugepageLimits []LinuxHugepageLimit `protobuf:"bytes,6,rep,name=HugepageLimits,proto3" json:"HugepageLimits"`
HugepageLimits []LinuxHugepageLimit `protobuf:"bytes,6,rep,name=HugepageLimits,json=hugepageLimits,proto3" json:"HugepageLimits"`
// Network restriction configuration
Network *LinuxNetwork `protobuf:"bytes,7,opt,name=Network,proto3" json:"Network,omitempty"`
Network *LinuxNetwork `protobuf:"bytes,7,opt,name=Network,json=network,proto3" json:"Network,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -844,19 +844,19 @@ var xxx_messageInfo_LinuxResources proto.InternalMessageInfo
type LinuxMemory struct {
// Memory limit (in bytes).
Limit int64 `protobuf:"varint,1,opt,name=Limit,proto3" json:"Limit,omitempty"`
Limit int64 `protobuf:"varint,1,opt,name=Limit,json=limit,proto3" json:"Limit,omitempty"`
// Memory reservation or soft_limit (in bytes).
Reservation int64 `protobuf:"varint,2,opt,name=Reservation,proto3" json:"Reservation,omitempty"`
Reservation int64 `protobuf:"varint,2,opt,name=Reservation,json=reservation,proto3" json:"Reservation,omitempty"`
// Total memory limit (memory + swap).
Swap int64 `protobuf:"varint,3,opt,name=Swap,proto3" json:"Swap,omitempty"`
Swap int64 `protobuf:"varint,3,opt,name=Swap,json=swap,proto3" json:"Swap,omitempty"`
// Kernel memory limit (in bytes).
Kernel int64 `protobuf:"varint,4,opt,name=Kernel,proto3" json:"Kernel,omitempty"`
Kernel int64 `protobuf:"varint,4,opt,name=Kernel,json=kernel,proto3" json:"Kernel,omitempty"`
// Kernel memory limit for tcp (in bytes)
KernelTCP int64 `protobuf:"varint,5,opt,name=KernelTCP,proto3" json:"KernelTCP,omitempty"`
KernelTCP int64 `protobuf:"varint,5,opt,name=KernelTCP,json=kernelTCP,proto3" json:"KernelTCP,omitempty"`
// How aggressive the kernel will swap memory pages.
Swappiness uint64 `protobuf:"varint,6,opt,name=Swappiness,proto3" json:"Swappiness,omitempty"`
Swappiness uint64 `protobuf:"varint,6,opt,name=Swappiness,json=swappiness,proto3" json:"Swappiness,omitempty"`
// DisableOOMKiller disables the OOM killer for out of memory conditions
DisableOOMKiller bool `protobuf:"varint,7,opt,name=DisableOOMKiller,proto3" json:"DisableOOMKiller,omitempty"`
DisableOOMKiller bool `protobuf:"varint,7,opt,name=DisableOOMKiller,json=disableOOMKiller,proto3" json:"DisableOOMKiller,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -896,19 +896,19 @@ var xxx_messageInfo_LinuxMemory proto.InternalMessageInfo
type LinuxCPU struct {
// CPU shares (relative weight (ratio) vs. other cgroups with cpu shares).
Shares uint64 `protobuf:"varint,1,opt,name=Shares,proto3" json:"Shares,omitempty"`
Shares uint64 `protobuf:"varint,1,opt,name=Shares,json=shares,proto3" json:"Shares,omitempty"`
// CPU hardcap limit (in usecs). Allowed cpu time in a given period.
Quota int64 `protobuf:"varint,2,opt,name=Quota,proto3" json:"Quota,omitempty"`
Quota int64 `protobuf:"varint,2,opt,name=Quota,json=quota,proto3" json:"Quota,omitempty"`
// CPU period to be used for hardcapping (in usecs).
Period uint64 `protobuf:"varint,3,opt,name=Period,proto3" json:"Period,omitempty"`
Period uint64 `protobuf:"varint,3,opt,name=Period,json=period,proto3" json:"Period,omitempty"`
// How much time realtime scheduling may use (in usecs).
RealtimeRuntime int64 `protobuf:"varint,4,opt,name=RealtimeRuntime,proto3" json:"RealtimeRuntime,omitempty"`
RealtimeRuntime int64 `protobuf:"varint,4,opt,name=RealtimeRuntime,json=realtimeRuntime,proto3" json:"RealtimeRuntime,omitempty"`
// CPU period to be used for realtime scheduling (in usecs).
RealtimePeriod uint64 `protobuf:"varint,5,opt,name=RealtimePeriod,proto3" json:"RealtimePeriod,omitempty"`
RealtimePeriod uint64 `protobuf:"varint,5,opt,name=RealtimePeriod,json=realtimePeriod,proto3" json:"RealtimePeriod,omitempty"`
// CPUs to use within the cpuset. Default is to use any CPU available.
Cpus string `protobuf:"bytes,6,opt,name=Cpus,proto3" json:"Cpus,omitempty"`
Cpus string `protobuf:"bytes,6,opt,name=Cpus,json=cpus,proto3" json:"Cpus,omitempty"`
// List of memory nodes in the cpuset. Default is to use any available memory node.
Mems string `protobuf:"bytes,7,opt,name=Mems,proto3" json:"Mems,omitempty"`
Mems string `protobuf:"bytes,7,opt,name=Mems,json=mems,proto3" json:"Mems,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -948,13 +948,13 @@ var xxx_messageInfo_LinuxCPU proto.InternalMessageInfo
type LinuxWeightDevice struct {
// Major is the device's major number.
Major int64 `protobuf:"varint,1,opt,name=Major,proto3" json:"Major,omitempty"`
Major int64 `protobuf:"varint,1,opt,name=Major,json=major,proto3" json:"Major,omitempty"`
// Minor is the device's minor number.
Minor int64 `protobuf:"varint,2,opt,name=Minor,proto3" json:"Minor,omitempty"`
Minor int64 `protobuf:"varint,2,opt,name=Minor,json=minor,proto3" json:"Minor,omitempty"`
// Weight is the bandwidth rate for the device.
Weight uint32 `protobuf:"varint,3,opt,name=Weight,proto3" json:"Weight,omitempty"`
Weight uint32 `protobuf:"varint,3,opt,name=Weight,json=weight,proto3" json:"Weight,omitempty"`
// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only
LeafWeight uint32 `protobuf:"varint,4,opt,name=LeafWeight,proto3" json:"LeafWeight,omitempty"`
LeafWeight uint32 `protobuf:"varint,4,opt,name=LeafWeight,json=leafWeight,proto3" json:"LeafWeight,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -994,11 +994,11 @@ var xxx_messageInfo_LinuxWeightDevice proto.InternalMessageInfo
type LinuxThrottleDevice struct {
// Major is the device's major number.
Major int64 `protobuf:"varint,1,opt,name=Major,proto3" json:"Major,omitempty"`
Major int64 `protobuf:"varint,1,opt,name=Major,json=major,proto3" json:"Major,omitempty"`
// Minor is the device's minor number.
Minor int64 `protobuf:"varint,2,opt,name=Minor,proto3" json:"Minor,omitempty"`
Minor int64 `protobuf:"varint,2,opt,name=Minor,json=minor,proto3" json:"Minor,omitempty"`
// Rate is the IO rate limit per cgroup per device
Rate uint64 `protobuf:"varint,3,opt,name=Rate,proto3" json:"Rate,omitempty"`
Rate uint64 `protobuf:"varint,3,opt,name=Rate,json=rate,proto3" json:"Rate,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1038,19 +1038,19 @@ var xxx_messageInfo_LinuxThrottleDevice proto.InternalMessageInfo
type LinuxBlockIO struct {
// Specifies per cgroup weight
Weight uint32 `protobuf:"varint,1,opt,name=Weight,proto3" json:"Weight,omitempty"`
Weight uint32 `protobuf:"varint,1,opt,name=Weight,json=weight,proto3" json:"Weight,omitempty"`
// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, CFQ scheduler only
LeafWeight uint32 `protobuf:"varint,2,opt,name=LeafWeight,proto3" json:"LeafWeight,omitempty"`
LeafWeight uint32 `protobuf:"varint,2,opt,name=LeafWeight,json=leafWeight,proto3" json:"LeafWeight,omitempty"`
// Weight per cgroup per device, can override BlkioWeight
WeightDevice []LinuxWeightDevice `protobuf:"bytes,3,rep,name=WeightDevice,proto3" json:"WeightDevice"`
WeightDevice []LinuxWeightDevice `protobuf:"bytes,3,rep,name=WeightDevice,json=weightDevice,proto3" json:"WeightDevice"`
// IO read rate limit per cgroup per device, bytes per second
ThrottleReadBpsDevice []LinuxThrottleDevice `protobuf:"bytes,4,rep,name=ThrottleReadBpsDevice,proto3" json:"ThrottleReadBpsDevice"`
ThrottleReadBpsDevice []LinuxThrottleDevice `protobuf:"bytes,4,rep,name=ThrottleReadBpsDevice,json=throttleReadBpsDevice,proto3" json:"ThrottleReadBpsDevice"`
// IO write rate limit per cgroup per device, bytes per second
ThrottleWriteBpsDevice []LinuxThrottleDevice `protobuf:"bytes,5,rep,name=ThrottleWriteBpsDevice,proto3" json:"ThrottleWriteBpsDevice"`
ThrottleWriteBpsDevice []LinuxThrottleDevice `protobuf:"bytes,5,rep,name=ThrottleWriteBpsDevice,json=throttleWriteBpsDevice,proto3" json:"ThrottleWriteBpsDevice"`
// IO read rate limit per cgroup per device, IO per second
ThrottleReadIOPSDevice []LinuxThrottleDevice `protobuf:"bytes,6,rep,name=ThrottleReadIOPSDevice,proto3" json:"ThrottleReadIOPSDevice"`
ThrottleReadIOPSDevice []LinuxThrottleDevice `protobuf:"bytes,6,rep,name=ThrottleReadIOPSDevice,json=throttleReadIOPSDevice,proto3" json:"ThrottleReadIOPSDevice"`
// IO write rate limit per cgroup per device, IO per second
ThrottleWriteIOPSDevice []LinuxThrottleDevice `protobuf:"bytes,7,rep,name=ThrottleWriteIOPSDevice,proto3" json:"ThrottleWriteIOPSDevice"`
ThrottleWriteIOPSDevice []LinuxThrottleDevice `protobuf:"bytes,7,rep,name=ThrottleWriteIOPSDevice,json=throttleWriteIOPSDevice,proto3" json:"ThrottleWriteIOPSDevice"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1090,7 +1090,7 @@ var xxx_messageInfo_LinuxBlockIO proto.InternalMessageInfo
type LinuxPids struct {
// Maximum number of PIDs. Default is "no limit".
Limit int64 `protobuf:"varint,1,opt,name=Limit,proto3" json:"Limit,omitempty"`
Limit int64 `protobuf:"varint,1,opt,name=Limit,json=limit,proto3" json:"Limit,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1130,15 +1130,15 @@ var xxx_messageInfo_LinuxPids proto.InternalMessageInfo
type LinuxDeviceCgroup struct {
// Allow or deny
Allow bool `protobuf:"varint,1,opt,name=Allow,proto3" json:"Allow,omitempty"`
Allow bool `protobuf:"varint,1,opt,name=Allow,json=allow,proto3" json:"Allow,omitempty"`
// Device type, block, char, etc.
Type string `protobuf:"bytes,2,opt,name=Type,proto3" json:"Type,omitempty"`
Type string `protobuf:"bytes,2,opt,name=Type,json=type,proto3" json:"Type,omitempty"`
// Major is the device's major number.
Major int64 `protobuf:"varint,3,opt,name=Major,proto3" json:"Major,omitempty"`
Major int64 `protobuf:"varint,3,opt,name=Major,json=major,proto3" json:"Major,omitempty"`
// Minor is the device's minor number.
Minor int64 `protobuf:"varint,4,opt,name=Minor,proto3" json:"Minor,omitempty"`
Minor int64 `protobuf:"varint,4,opt,name=Minor,json=minor,proto3" json:"Minor,omitempty"`
// Cgroup access permissions format, rwm.
Access string `protobuf:"bytes,5,opt,name=Access,proto3" json:"Access,omitempty"`
Access string `protobuf:"bytes,5,opt,name=Access,json=access,proto3" json:"Access,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1178,9 +1178,9 @@ var xxx_messageInfo_LinuxDeviceCgroup proto.InternalMessageInfo
type LinuxNetwork struct {
// Set class identifier for container's network packets
ClassID uint32 `protobuf:"varint,1,opt,name=ClassID,proto3" json:"ClassID,omitempty"`
ClassID uint32 `protobuf:"varint,1,opt,name=ClassID,json=classID,proto3" json:"ClassID,omitempty"`
// Set priority of network traffic for container
Priorities []LinuxInterfacePriority `protobuf:"bytes,2,rep,name=Priorities,proto3" json:"Priorities"`
Priorities []LinuxInterfacePriority `protobuf:"bytes,2,rep,name=Priorities,json=priorities,proto3" json:"Priorities"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1220,9 +1220,9 @@ var xxx_messageInfo_LinuxNetwork proto.InternalMessageInfo
type LinuxHugepageLimit struct {
// Pagesize is the hugepage size
Pagesize string `protobuf:"bytes,1,opt,name=Pagesize,proto3" json:"Pagesize,omitempty"`
Pagesize string `protobuf:"bytes,1,opt,name=Pagesize,json=pagesize,proto3" json:"Pagesize,omitempty"`
// Limit is the limit of "hugepagesize" hugetlb usage
Limit uint64 `protobuf:"varint,2,opt,name=Limit,proto3" json:"Limit,omitempty"`
Limit uint64 `protobuf:"varint,2,opt,name=Limit,json=limit,proto3" json:"Limit,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1262,9 +1262,9 @@ var xxx_messageInfo_LinuxHugepageLimit proto.InternalMessageInfo
type LinuxInterfacePriority struct {
// Name is the name of the network interface
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
Name string `protobuf:"bytes,1,opt,name=Name,json=name,proto3" json:"Name,omitempty"`
// Priority for the interface
Priority uint32 `protobuf:"varint,2,opt,name=Priority,proto3" json:"Priority,omitempty"`
Priority uint32 `protobuf:"varint,2,opt,name=Priority,json=priority,proto3" json:"Priority,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1303,10 +1303,10 @@ func (m *LinuxInterfacePriority) XXX_DiscardUnknown() {
var xxx_messageInfo_LinuxInterfacePriority proto.InternalMessageInfo
type LinuxSeccomp struct {
DefaultAction string `protobuf:"bytes,1,opt,name=DefaultAction,proto3" json:"DefaultAction,omitempty"`
Architectures []string `protobuf:"bytes,2,rep,name=Architectures,proto3" json:"Architectures,omitempty"`
Flags []string `protobuf:"bytes,3,rep,name=Flags,proto3" json:"Flags,omitempty"`
Syscalls []LinuxSyscall `protobuf:"bytes,4,rep,name=Syscalls,proto3" json:"Syscalls"`
DefaultAction string `protobuf:"bytes,1,opt,name=DefaultAction,json=defaultAction,proto3" json:"DefaultAction,omitempty"`
Architectures []string `protobuf:"bytes,2,rep,name=Architectures,json=architectures,proto3" json:"Architectures,omitempty"`
Flags []string `protobuf:"bytes,3,rep,name=Flags,json=flags,proto3" json:"Flags,omitempty"`
Syscalls []LinuxSyscall `protobuf:"bytes,4,rep,name=Syscalls,json=syscalls,proto3" json:"Syscalls"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1345,10 +1345,10 @@ func (m *LinuxSeccomp) XXX_DiscardUnknown() {
var xxx_messageInfo_LinuxSeccomp proto.InternalMessageInfo
type LinuxSeccompArg struct {
Index uint64 `protobuf:"varint,1,opt,name=Index,proto3" json:"Index,omitempty"`
Value uint64 `protobuf:"varint,2,opt,name=Value,proto3" json:"Value,omitempty"`
ValueTwo uint64 `protobuf:"varint,3,opt,name=ValueTwo,proto3" json:"ValueTwo,omitempty"`
Op string `protobuf:"bytes,4,opt,name=Op,proto3" json:"Op,omitempty"`
Index uint64 `protobuf:"varint,1,opt,name=Index,json=index,proto3" json:"Index,omitempty"`
Value uint64 `protobuf:"varint,2,opt,name=Value,json=value,proto3" json:"Value,omitempty"`
ValueTwo uint64 `protobuf:"varint,3,opt,name=ValueTwo,json=valueTwo,proto3" json:"ValueTwo,omitempty"`
Op string `protobuf:"bytes,4,opt,name=Op,json=op,proto3" json:"Op,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1387,12 +1387,12 @@ func (m *LinuxSeccompArg) XXX_DiscardUnknown() {
var xxx_messageInfo_LinuxSeccompArg proto.InternalMessageInfo
type LinuxSyscall struct {
Names []string `protobuf:"bytes,1,rep,name=Names,proto3" json:"Names,omitempty"`
Action string `protobuf:"bytes,2,opt,name=Action,proto3" json:"Action,omitempty"`
Names []string `protobuf:"bytes,1,rep,name=Names,json=names,proto3" json:"Names,omitempty"`
Action string `protobuf:"bytes,2,opt,name=Action,json=action,proto3" json:"Action,omitempty"`
// Types that are valid to be assigned to ErrnoRet:
// *LinuxSyscall_Errnoret
ErrnoRet isLinuxSyscall_ErrnoRet `protobuf_oneof:"ErrnoRet"`
Args []LinuxSeccompArg `protobuf:"bytes,4,rep,name=Args,proto3" json:"Args"`
Args []LinuxSeccompArg `protobuf:"bytes,4,rep,name=Args,json=args,proto3" json:"Args"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1467,7 +1467,7 @@ func (*LinuxSyscall) XXX_OneofWrappers() []interface{} {
type LinuxIntelRdt struct {
// The schema for L3 cache id and capacity bitmask (CBM)
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
L3CacheSchema string `protobuf:"bytes,1,opt,name=L3CacheSchema,proto3" json:"L3CacheSchema,omitempty"`
L3CacheSchema string `protobuf:"bytes,1,opt,name=L3CacheSchema,json=l3CacheSchema,proto3" json:"L3CacheSchema,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -1546,140 +1546,150 @@ func init() {
}
var fileDescriptor_e42fef2823778fc8 = []byte{
// 2118 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcb, 0x73, 0x5b, 0x49,
0xd5, 0xcf, 0x95, 0x64, 0x59, 0x6a, 0xc5, 0x79, 0xf4, 0x64, 0x32, 0xf7, 0xcb, 0x97, 0xd2, 0x78,
0x2e, 0x29, 0x30, 0x10, 0xec, 0x22, 0xe1, 0x31, 0x0c, 0x8f, 0x2a, 0xd9, 0x4e, 0x62, 0xd5, 0xc4,
0x91, 0x68, 0xd9, 0x13, 0x98, 0xc5, 0x54, 0xb5, 0xaf, 0xda, 0x52, 0x8f, 0xaf, 0x6e, 0xdf, 0xea,
0xdb, 0xb2, 0x63, 0x56, 0xb0, 0x63, 0xcf, 0x82, 0x35, 0x1b, 0x1e, 0xff, 0x01, 0xc5, 0x8a, 0x1d,
0x29, 0x56, 0x2c, 0xa9, 0xa2, 0x0a, 0x88, 0xf7, 0xec, 0x59, 0x52, 0xa7, 0xfb, 0xdc, 0xab, 0x96,
0xe4, 0xc0, 0x04, 0x56, 0xea, 0xf3, 0x3b, 0x8f, 0x7e, 0x9c, 0xe7, 0x15, 0xe9, 0x8d, 0xa4, 0x19,
0x4f, 0x8f, 0x36, 0x63, 0x35, 0xd9, 0x3a, 0xe1, 0x86, 0x7f, 0x25, 0x56, 0xa9, 0xe1, 0x32, 0x15,
0x3a, 0x5f, 0xa2, 0x73, 0x1d, 0x6f, 0xf1, 0x91, 0x48, 0xcd, 0x56, 0xa6, 0x95, 0x51, 0xb1, 0x4a,
0x72, 0xb7, 0xca, 0xb7, 0x54, 0x2c, 0x37, 0xed, 0x92, 0xd6, 0x46, 0x3a, 0x8b, 0xef, 0x44, 0x23,
0x35, 0x52, 0x8e, 0x79, 0x34, 0x3d, 0xde, 0x02, 0xca, 0x12, 0x76, 0xe5, 0x24, 0xa3, 0x3f, 0x54,
0x49, 0x6d, 0x90, 0x89, 0x98, 0x86, 0x64, 0xf5, 0x23, 0xa1, 0x73, 0xa9, 0xd2, 0x30, 0x58, 0x0f,
0x36, 0x9a, 0xac, 0x20, 0xe9, 0x17, 0xc8, 0x6a, 0x5f, 0xab, 0x58, 0xe4, 0x79, 0x58, 0x59, 0x0f,
0x36, 0x5a, 0x0f, 0xd6, 0x36, 0xc1, 0xfc, 0x26, 0x82, 0xac, 0xe0, 0xd2, 0x36, 0xa9, 0x31, 0xa5,
0x4c, 0x58, 0xb5, 0x52, 0xc4, 0x49, 0x01, 0xc2, 0x2c, 0x4e, 0xef, 0x90, 0xc6, 0x9e, 0xca, 0x4d,
0xca, 0x27, 0x22, 0xac, 0xd9, 0x3d, 0x4a, 0x9a, 0x7e, 0x91, 0xd4, 0xf7, 0xd5, 0x34, 0x35, 0x79,
0xb8, 0xb2, 0x5e, 0xdd, 0x68, 0x3d, 0x68, 0x39, 0x6d, 0x8b, 0x6d, 0xd7, 0x5e, 0xfe, 0xf5, 0xdd,
0x2b, 0x0c, 0x05, 0xe8, 0x7b, 0x64, 0x65, 0x4f, 0xa9, 0x93, 0x3c, 0xac, 0xdb, 0x7d, 0x50, 0xd2,
0x42, 0xcc, 0x71, 0xe8, 0x77, 0x49, 0xab, 0x93, 0xa6, 0xca, 0x70, 0x23, 0x55, 0x9a, 0x87, 0xab,
0xd6, 0xe4, 0xff, 0x3b, 0x41, 0xb8, 0xed, 0xa6, 0xc7, 0x7d, 0x94, 0x1a, 0x7d, 0xce, 0x7c, 0x79,
0xd8, 0xe1, 0xa9, 0x4c, 0xa7, 0x2f, 0xc2, 0x86, 0xbf, 0x83, 0x85, 0x98, 0xe3, 0xc0, 0xa3, 0x0c,
0x54, 0xc2, 0xb5, 0xcc, 0xc3, 0xa6, 0xff, 0x28, 0x08, 0xb2, 0x82, 0x0b, 0x82, 0xcf, 0x65, 0x3a,
0x54, 0x67, 0x79, 0x48, 0x7c, 0x41, 0x04, 0x59, 0xc1, 0xbd, 0xf3, 0x3d, 0x72, 0x63, 0xf1, 0x54,
0xf4, 0x06, 0xa9, 0x9e, 0x88, 0x73, 0x74, 0x08, 0x2c, 0xe9, 0x2d, 0xb2, 0x72, 0xca, 0x93, 0xa9,
0xb0, 0xae, 0x68, 0x32, 0x47, 0x7c, 0x50, 0x79, 0x3f, 0x88, 0x7e, 0x57, 0x2d, 0xfd, 0x04, 0x2f,
0x7d, 0x20, 0xf4, 0x44, 0xa6, 0x3c, 0xb1, 0xca, 0x0d, 0x56, 0xd2, 0xf4, 0xcb, 0xa4, 0xb5, 0xa3,
0xd2, 0x5c, 0x25, 0x62, 0x20, 0x7f, 0x24, 0xd0, 0xa5, 0x4d, 0x77, 0xa8, 0x6d, 0xf5, 0x82, 0xf9,
0x5c, 0x7a, 0x8f, 0xd4, 0x0e, 0x73, 0xa1, 0xe7, 0x5d, 0x0a, 0x08, 0xfa, 0xc4, 0x72, 0x29, 0x25,
0xb5, 0x8e, 0x1e, 0xe5, 0x61, 0x6d, 0xbd, 0xba, 0xd1, 0x64, 0x76, 0x0d, 0x47, 0x7f, 0x94, 0x9e,
0x5a, 0x6f, 0x36, 0x19, 0x2c, 0x01, 0xd9, 0x39, 0x1b, 0x5a, 0xaf, 0x35, 0x19, 0x2c, 0xe9, 0xb7,
0xc9, 0xd5, 0x1d, 0x9e, 0xf1, 0x23, 0x99, 0x48, 0x23, 0x05, 0xf8, 0x09, 0x76, 0x79, 0xc7, 0x7b,
0x6e, 0x9f, 0xcd, 0xe6, 0x84, 0xe9, 0x57, 0xc9, 0x2a, 0x4b, 0xe4, 0x44, 0x9a, 0x3c, 0x6c, 0x58,
0xff, 0xde, 0xc4, 0xb0, 0xec, 0x0d, 0xba, 0x3f, 0x70, 0x1c, 0x3c, 0x64, 0x21, 0x47, 0x37, 0xc8,
0xf5, 0x67, 0xea, 0x99, 0x38, 0xeb, 0x6b, 0x79, 0x2a, 0x13, 0x31, 0x12, 0xce, 0x79, 0x0d, 0xb6,
0x08, 0x83, 0x64, 0x27, 0xcb, 0xb8, 0x9e, 0x28, 0xdd, 0xd7, 0xea, 0x58, 0x26, 0xc2, 0x7a, 0xaf,
0xc9, 0x16, 0x61, 0xba, 0x4e, 0x5a, 0xbd, 0xde, 0xfe, 0x20, 0x56, 0x5a, 0x74, 0x86, 0x9f, 0x86,
0xad, 0xf5, 0x60, 0xa3, 0xca, 0x7c, 0x88, 0x46, 0xe4, 0xea, 0x40, 0x24, 0x70, 0x9b, 0xa7, 0xfc,
0x48, 0x24, 0xe1, 0x55, 0x6b, 0x68, 0x0e, 0x8b, 0x1e, 0x92, 0xea, 0xb6, 0x7a, 0x41, 0x6f, 0x93,
0xfa, 0x9e, 0x90, 0xa3, 0xb1, 0xb1, 0x5e, 0x5b, 0x63, 0x48, 0x81, 0xd7, 0x9f, 0xcb, 0xa1, 0x19,
0x5b, 0x6f, 0xad, 0x31, 0x47, 0x44, 0xa9, 0x73, 0x0e, 0x3c, 0xec, 0x61, 0x77, 0x17, 0x55, 0x60,
0x09, 0xc8, 0x93, 0xee, 0x2e, 0x4a, 0xc3, 0x92, 0x7e, 0x9e, 0x5c, 0xeb, 0x0c, 0x87, 0x12, 0x62,
0x8b, 0x27, 0x4f, 0xe4, 0x30, 0x0f, 0xab, 0xeb, 0xd5, 0x8d, 0x35, 0xb6, 0x80, 0x42, 0xe4, 0x80,
0x4d, 0x3f, 0x47, 0x0b, 0x3a, 0xfa, 0x55, 0x40, 0x6e, 0x2e, 0x79, 0x05, 0x34, 0xb6, 0xd5, 0x34,
0x1d, 0xca, 0x74, 0x14, 0x06, 0xd6, 0xdb, 0x25, 0x4d, 0xef, 0x92, 0xe6, 0xa3, 0xe3, 0x63, 0x11,
0x1b, 0x79, 0x0a, 0x91, 0x06, 0xcc, 0x19, 0x00, 0x4f, 0xd7, 0x4d, 0xc7, 0x42, 0x4b, 0xc3, 0x8f,
0x12, 0x61, 0x0f, 0xd4, 0x64, 0x3e, 0x04, 0xfa, 0x7d, 0x88, 0x5b, 0x63, 0xc4, 0x10, 0xa3, 0x6b,
0x06, 0x40, 0xc9, 0xea, 0x4c, 0x8e, 0xa4, 0x48, 0x0d, 0x86, 0x59, 0x41, 0x46, 0x5d, 0xd2, 0xf2,
0xc2, 0x00, 0xe2, 0xf3, 0xe0, 0x3c, 0x13, 0x98, 0x47, 0x76, 0x0d, 0xd8, 0x1e, 0xd7, 0x43, 0xfb,
0x46, 0x35, 0x66, 0xd7, 0x80, 0x0d, 0xd4, 0xb1, 0x2b, 0x60, 0x35, 0x66, 0xd7, 0x91, 0x22, 0x2b,
0xb6, 0xee, 0xc0, 0x69, 0x87, 0x22, 0x37, 0x32, 0xb5, 0x09, 0x8a, 0xb6, 0x7c, 0x08, 0xbc, 0x97,
0xab, 0xa9, 0x8e, 0x8b, 0xe4, 0x44, 0x0a, 0xcc, 0x1a, 0xd8, 0xbe, 0xea, 0xb6, 0x87, 0x35, 0x9c,
0x5d, 0x65, 0xae, 0x3a, 0xb9, 0x7b, 0x15, 0x64, 0xf4, 0x0d, 0x57, 0x45, 0x41, 0xab, 0xcf, 0xcd,
0xb8, 0x38, 0x34, 0xac, 0xe1, 0xad, 0x99, 0xe0, 0x43, 0x95, 0x26, 0xe7, 0x76, 0x8f, 0x06, 0x2b,
0xe9, 0xe8, 0x67, 0x01, 0xd6, 0x45, 0x7a, 0x9f, 0x34, 0xfa, 0x5a, 0xe4, 0x86, 0x6b, 0x63, 0x3d,
0x52, 0x26, 0x2e, 0xb0, 0x31, 0x27, 0x4a, 0x09, 0xba, 0x49, 0x9a, 0x7d, 0x95, 0x1b, 0x27, 0x5e,
0x79, 0x8d, 0xf8, 0x4c, 0xc4, 0x5a, 0xb7, 0x84, 0xca, 0xac, 0xcb, 0x2e, 0xb7, 0x8e, 0x12, 0xd1,
0xc7, 0xa4, 0x06, 0xf8, 0xa5, 0xb7, 0x29, 0xca, 0x46, 0x65, 0xb9, 0x6c, 0x54, 0x67, 0x65, 0x23,
0x24, 0xab, 0x07, 0x72, 0x22, 0xd4, 0xd4, 0xd8, 0x80, 0xac, 0xb2, 0x82, 0x8c, 0x7e, 0xb3, 0x82,
0x75, 0x9a, 0x7e, 0x87, 0xb4, 0x0e, 0xbb, 0xbb, 0xfb, 0x3c, 0xcb, 0x64, 0x3a, 0xca, 0xf1, 0xd2,
0xb7, 0xbc, 0x3a, 0x52, 0x32, 0xf1, 0x80, 0xbe, 0x38, 0x68, 0x3f, 0xf1, 0xb4, 0x2b, 0xff, 0x59,
0xdb, 0x13, 0xa7, 0x5b, 0xa4, 0x3e, 0x38, 0xcf, 0x63, 0x93, 0xe0, 0x6b, 0xf8, 0xe5, 0x6b, 0xd3,
0x71, 0x5c, 0x8b, 0x41, 0x31, 0xfa, 0x80, 0x34, 0x99, 0x70, 0xa1, 0x91, 0xdb, 0x2b, 0xcd, 0x6f,
0x56, 0xf2, 0xd8, 0x4c, 0x0c, 0x82, 0x6f, 0x67, 0xa4, 0xd5, 0x34, 0xcb, 0xed, 0x2b, 0xae, 0xb8,
0xe0, 0xf3, 0x20, 0xfa, 0x01, 0x21, 0xcf, 0xf8, 0x44, 0xe4, 0x19, 0x07, 0xb3, 0xf5, 0xa5, 0x3b,
0x94, 0x4c, 0xbc, 0x83, 0x27, 0x0d, 0xa5, 0x74, 0x57, 0x9c, 0xca, 0x58, 0x14, 0xad, 0xf2, 0xa6,
0xa7, 0xe8, 0x38, 0x45, 0x29, 0x45, 0x39, 0x7a, 0x9f, 0xac, 0x0e, 0x44, 0x1c, 0xab, 0x49, 0x86,
0x4d, 0x92, 0x7a, 0x2a, 0xc8, 0x61, 0x85, 0x08, 0xbd, 0x4f, 0x6e, 0x42, 0x4c, 0x1f, 0xe7, 0x7d,
0xad, 0x32, 0x3e, 0x72, 0x19, 0xd4, 0xb4, 0x97, 0x58, 0x66, 0xc0, 0x65, 0xf7, 0x79, 0x7e, 0x22,
0x86, 0x70, 0x31, 0x68, 0x9b, 0xb6, 0x2e, 0x78, 0x10, 0xbd, 0x47, 0xd6, 0x8a, 0xb8, 0x77, 0x32,
0x2d, 0x2b, 0x33, 0x0f, 0xd2, 0x36, 0x21, 0x36, 0x75, 0xfd, 0xb2, 0xeb, 0x21, 0x74, 0x8b, 0x34,
0xba, 0xa9, 0x11, 0x09, 0x1b, 0x9a, 0x70, 0xcd, 0x5e, 0xe2, 0x2d, 0xdf, 0xe9, 0xc8, 0x62, 0xa5,
0xd0, 0x9d, 0x6f, 0x91, 0x96, 0xe7, 0xd0, 0x37, 0xea, 0xce, 0xef, 0x96, 0x63, 0x00, 0x08, 0x0d,
0xa7, 0x93, 0x49, 0xa1, 0xe8, 0x08, 0x10, 0x28, 0x46, 0x86, 0xcb, 0x05, 0x3e, 0x21, 0xd7, 0xe6,
0x83, 0xd1, 0x76, 0x0b, 0x95, 0x9b, 0xb2, 0xf4, 0x23, 0x65, 0x83, 0xa5, 0x18, 0x18, 0xcb, 0x2e,
0xe0, 0x43, 0xb6, 0xd0, 0x41, 0xf3, 0xaf, 0x5a, 0x96, 0x5d, 0x47, 0xef, 0xa3, 0xfd, 0x32, 0x2e,
0x5e, 0x57, 0x36, 0x6d, 0x04, 0x56, 0x66, 0x79, 0x1c, 0xfd, 0x22, 0x20, 0x2d, 0x2f, 0x54, 0x5e,
0x97, 0xeb, 0xd6, 0x56, 0xc5, 0xb3, 0x75, 0x8b, 0xac, 0xec, 0xf3, 0x4f, 0x95, 0x9b, 0x2e, 0xaa,
0xcc, 0x11, 0x16, 0x95, 0xa9, 0xd2, 0x98, 0xed, 0x8e, 0x80, 0xca, 0xf7, 0x58, 0x26, 0x62, 0x5f,
0x0d, 0x85, 0x8d, 0xfe, 0x35, 0x56, 0xd2, 0x45, 0xff, 0xab, 0x2f, 0xf5, 0xbf, 0xd5, 0xb2, 0xff,
0x45, 0x7f, 0xab, 0xe0, 0xf5, 0x66, 0x39, 0xf5, 0xcd, 0x59, 0xd4, 0x07, 0x4b, 0x99, 0xeb, 0x38,
0x2e, 0xc1, 0x16, 0x63, 0x1f, 0x66, 0x55, 0x31, 0x51, 0xfa, 0x1c, 0x87, 0x27, 0x3f, 0x5b, 0x1c,
0x83, 0xa1, 0x00, 0x5d, 0x27, 0xd5, 0x9d, 0xfe, 0x21, 0x8e, 0x4f, 0xd7, 0xfc, 0xc1, 0xa6, 0x7f,
0xc8, 0x80, 0x45, 0x3f, 0x47, 0x6a, 0x7d, 0x68, 0xc7, 0xae, 0x10, 0x5c, 0xf7, 0x44, 0x00, 0x66,
0x96, 0x09, 0xd9, 0xb6, 0x9d, 0xa8, 0xf8, 0xa4, 0xdb, 0xb3, 0x97, 0x9f, 0xcf, 0x36, 0xe4, 0xb0,
0x42, 0x84, 0x3e, 0x26, 0xd7, 0xf6, 0xa6, 0x23, 0x91, 0xf1, 0x91, 0x78, 0xea, 0x06, 0x24, 0x57,
0x0e, 0x42, 0x4f, 0x69, 0x4e, 0x00, 0x2f, 0xb8, 0xa0, 0x05, 0xbb, 0x3e, 0x13, 0xe6, 0x4c, 0xe9,
0x13, 0x9c, 0xcc, 0xfc, 0x5d, 0x91, 0xc3, 0x0a, 0x91, 0xe8, 0x2f, 0x45, 0x14, 0xe0, 0xd5, 0x6f,
0x41, 0x71, 0x9e, 0x48, 0x37, 0xca, 0x54, 0x99, 0x23, 0x20, 0x36, 0x99, 0xc8, 0x85, 0x3e, 0x75,
0x35, 0xa0, 0xe2, 0xc6, 0x25, 0x0f, 0xb2, 0xb1, 0x79, 0xc6, 0x33, 0x0c, 0x0a, 0xbb, 0x86, 0x48,
0xff, 0x50, 0xe8, 0x54, 0x24, 0x18, 0x14, 0x48, 0xc1, 0x7c, 0xe0, 0x56, 0x07, 0x3b, 0x7d, 0xfb,
0x32, 0x55, 0x36, 0x03, 0x20, 0xff, 0x41, 0x3b, 0x93, 0x29, 0x7c, 0xbb, 0xd4, 0x6d, 0x53, 0xf7,
0x10, 0xfa, 0x25, 0x72, 0x63, 0x57, 0xe6, 0x30, 0x68, 0xf4, 0x7a, 0xfb, 0x1f, 0xca, 0x24, 0x11,
0xda, 0x5e, 0xb4, 0xc1, 0x96, 0xf0, 0xe8, 0x8f, 0x01, 0x69, 0x14, 0x8e, 0x83, 0xe3, 0x0c, 0xc6,
0x5c, 0xdb, 0xc0, 0x01, 0xa3, 0x48, 0xc1, 0x95, 0xbf, 0x3f, 0x55, 0x86, 0xe3, 0xb5, 0x1c, 0x01,
0xd2, 0x7d, 0xa1, 0xa5, 0x1a, 0xe2, 0x5c, 0x81, 0x14, 0xcc, 0x98, 0x4c, 0xf0, 0xc4, 0xc8, 0x89,
0x60, 0xd3, 0x14, 0x7e, 0xf0, 0x76, 0x8b, 0x30, 0x0c, 0x6f, 0x05, 0x84, 0x96, 0x56, 0xac, 0xa5,
0x05, 0x14, 0x9e, 0x6e, 0x27, 0x9b, 0xe6, 0x38, 0x62, 0xdb, 0x35, 0x60, 0xfb, 0x62, 0xe2, 0x66,
0xeb, 0x26, 0xb3, 0xeb, 0xe8, 0x0c, 0xe7, 0xb8, 0xe7, 0x76, 0xba, 0xc4, 0xac, 0x2d, 0xb3, 0x31,
0xb8, 0x34, 0x1b, 0x2b, 0x7e, 0x36, 0xde, 0x26, 0x75, 0xa7, 0x8b, 0x15, 0x04, 0x29, 0x78, 0xf1,
0xa7, 0x82, 0x1f, 0x23, 0xaf, 0x66, 0x79, 0x1e, 0x12, 0x1d, 0x92, 0xb7, 0xec, 0xc6, 0x07, 0x63,
0xad, 0x8c, 0x49, 0xc4, 0x7f, 0xb1, 0x35, 0x25, 0x35, 0xc6, 0x8d, 0x28, 0x66, 0x34, 0x58, 0x47,
0xff, 0xa8, 0x92, 0xab, 0x7e, 0x2a, 0x78, 0xe7, 0x0b, 0xfe, 0xcd, 0xf9, 0x2a, 0x8b, 0xe7, 0xa3,
0x1d, 0x72, 0xd5, 0x7f, 0x93, 0x4b, 0x3a, 0xba, 0xcf, 0xc6, 0xb4, 0x99, 0x53, 0xa1, 0x87, 0xe4,
0xed, 0xe2, 0x76, 0xd0, 0x8d, 0xb6, 0xb3, 0x1c, 0x6d, 0xd5, 0xac, 0xad, 0xff, 0xf3, 0x6c, 0xcd,
0xbf, 0x02, 0x5a, 0xbb, 0x5c, 0x9b, 0x3e, 0x27, 0xb7, 0x0b, 0xc6, 0x73, 0x2d, 0x8d, 0x98, 0xd9,
0x5d, 0xf9, 0x6c, 0x76, 0x5f, 0xa3, 0xee, 0x1b, 0x86, 0x1d, 0xbb, 0xbd, 0xfe, 0x00, 0x0d, 0xd7,
0xdf, 0xd0, 0xf0, 0xbc, 0x3a, 0xfd, 0x21, 0x79, 0x67, 0x6e, 0x4b, 0xcf, 0xf2, 0xea, 0x67, 0xb3,
0xfc, 0x3a, 0xfd, 0xe8, 0x3d, 0xd2, 0x2c, 0x2b, 0xe4, 0xe5, 0x75, 0x26, 0xfa, 0x49, 0xf1, 0xad,
0xe2, 0x17, 0x72, 0x90, 0xed, 0x24, 0x89, 0x3a, 0xc3, 0x8f, 0x62, 0x47, 0xfc, 0xcf, 0xbd, 0xe9,
0x36, 0xa9, 0x77, 0x62, 0xfb, 0xff, 0x88, 0x9b, 0xcb, 0x90, 0x8a, 0x12, 0x8c, 0x4a, 0xac, 0x90,
0x30, 0xc9, 0xee, 0x24, 0x3c, 0xcf, 0xcb, 0x86, 0x5d, 0x90, 0x74, 0x9b, 0x90, 0xbe, 0x96, 0x4a,
0xbb, 0xcf, 0x60, 0x37, 0x80, 0xde, 0x5d, 0x98, 0x45, 0xf4, 0x31, 0x8f, 0x05, 0x4a, 0x9d, 0x17,
0x43, 0xdc, 0x4c, 0x2b, 0x7a, 0x4c, 0xe8, 0x72, 0x65, 0x87, 0xbe, 0xd9, 0xe7, 0x23, 0x91, 0x43,
0xb7, 0x77, 0xfd, 0xb8, 0xa4, 0x67, 0x2f, 0xe7, 0xbe, 0x81, 0xf0, 0xe5, 0xf6, 0xc8, 0xed, 0xcb,
0xf7, 0x84, 0x77, 0x82, 0xe1, 0xa0, 0xe8, 0xeb, 0xb0, 0xb6, 0xf6, 0x91, 0x8f, 0xf9, 0x54, 0xd2,
0xd1, 0x2f, 0x03, 0x7c, 0x80, 0x62, 0x0c, 0xbc, 0x47, 0xd6, 0x76, 0xc5, 0x31, 0x9f, 0x26, 0xa6,
0x13, 0x7b, 0x1f, 0x51, 0xf3, 0x20, 0x48, 0x75, 0x74, 0x3c, 0x96, 0x46, 0xc4, 0x66, 0xaa, 0x45,
0xf1, 0x7d, 0x30, 0x0f, 0xc2, 0xe1, 0x1f, 0x27, 0x7c, 0x94, 0xe3, 0xa7, 0x82, 0x23, 0xe8, 0xd7,
0x48, 0x03, 0x26, 0x34, 0x9e, 0x24, 0x39, 0x26, 0xdc, 0xdc, 0x5c, 0xea, 0x58, 0xc5, 0x47, 0x4a,
0x21, 0x19, 0x49, 0x72, 0xdd, 0x3f, 0x67, 0x47, 0x8f, 0xc0, 0x7c, 0x37, 0x1d, 0x8a, 0x17, 0x58,
0xe1, 0x1d, 0x01, 0xe8, 0x47, 0xe5, 0x7c, 0x57, 0x63, 0x8e, 0x80, 0x37, 0xb0, 0x8b, 0x83, 0x33,
0x85, 0x65, 0xa9, 0xa4, 0xe9, 0x35, 0x52, 0xe9, 0x65, 0xf8, 0x25, 0x5d, 0xe9, 0x65, 0xd1, 0xcf,
0xcb, 0x37, 0x71, 0x9b, 0x83, 0x49, 0x3b, 0x71, 0xe1, 0xb7, 0xb3, 0x23, 0x5c, 0x48, 0x95, 0x1d,
0xd2, 0x86, 0x94, 0x7d, 0x9b, 0xbb, 0xa4, 0x21, 0xb4, 0x4e, 0x95, 0x16, 0x58, 0x7a, 0xf7, 0xae,
0xb0, 0x12, 0xa1, 0x5b, 0xde, 0xff, 0x30, 0xad, 0x07, 0x6f, 0x2f, 0x4f, 0xe4, 0x1d, 0x5d, 0x7c,
0xc2, 0x58, 0xc1, 0x6d, 0x42, 0x1a, 0x8f, 0x40, 0x99, 0x09, 0x13, 0x7d, 0x9d, 0xac, 0xcd, 0xcd,
0xbd, 0xe0, 0x87, 0xa7, 0x0f, 0x77, 0x78, 0x3c, 0x16, 0x83, 0x78, 0x2c, 0x26, 0xbc, 0xf0, 0xd6,
0x1c, 0xb8, 0xfd, 0xd3, 0xe0, 0xe5, 0xab, 0xf6, 0x95, 0x3f, 0xbf, 0x6a, 0x5f, 0xf9, 0xe7, 0xab,
0x76, 0xf0, 0xe3, 0x8b, 0x76, 0xf0, 0xeb, 0x8b, 0x76, 0xf0, 0xdb, 0x8b, 0x76, 0xf0, 0xfb, 0x8b,
0x76, 0xf0, 0xf2, 0xa2, 0x1d, 0xfc, 0xe9, 0xa2, 0x1d, 0xfc, 0xfd, 0xa2, 0x1d, 0x7c, 0xfc, 0xc9,
0x1b, 0xfe, 0xcd, 0xa9, 0x5d, 0xfb, 0xdb, 0x3a, 0x95, 0xda, 0x78, 0xac, 0xec, 0x64, 0xb4, 0xf4,
0x0f, 0x28, 0xdc, 0xf4, 0xa8, 0x6e, 0xe9, 0x87, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x3e,
0xc9, 0x10, 0x4f, 0x15, 0x00, 0x00,
// 2282 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0xcd, 0x73, 0x23, 0x47,
0xd9, 0xdf, 0xd1, 0x8c, 0xa4, 0x51, 0xcb, 0x1f, 0xbb, 0x9d, 0xc4, 0xd1, 0xbb, 0x6f, 0x4a, 0x71,
0x86, 0x14, 0x18, 0x58, 0xec, 0x62, 0xc3, 0x47, 0x08, 0x1f, 0x55, 0xb2, 0xbd, 0x1b, 0xab, 0xb2,
0x5e, 0x8b, 0x96, 0x9d, 0x85, 0x1c, 0x52, 0xd5, 0x9e, 0x69, 0x49, 0x13, 0x8f, 0xa6, 0x87, 0xee,
0x96, 0xb5, 0xce, 0x09, 0x6e, 0xdc, 0x39, 0x70, 0xe6, 0xc2, 0xc7, 0x7f, 0x40, 0x71, 0xe2, 0xc6,
0x16, 0x27, 0x8e, 0x54, 0x51, 0x05, 0xac, 0xef, 0xdc, 0x39, 0x52, 0x4f, 0x77, 0xcf, 0xa8, 0x65,
0x7b, 0x21, 0x0b, 0x27, 0xcd, 0xf3, 0x7b, 0x9e, 0x7e, 0xba, 0xfb, 0xf9, 0x6e, 0xa1, 0xa3, 0x71,
0xaa, 0x26, 0xb3, 0xd3, 0xed, 0x98, 0x4f, 0x77, 0xce, 0xa8, 0xa2, 0x5f, 0x89, 0x79, 0xae, 0x68,
0x9a, 0x33, 0x21, 0xaf, 0xd1, 0x52, 0xc4, 0x3b, 0x74, 0xcc, 0x72, 0xb5, 0x53, 0x08, 0xae, 0x78,
0xcc, 0x33, 0x69, 0xbe, 0xe4, 0x0e, 0x8f, 0xd3, 0x6d, 0xfd, 0x89, 0x83, 0xb1, 0x28, 0xe2, 0xbb,
0xd1, 0x98, 0x8f, 0xb9, 0x61, 0x9e, 0xce, 0x46, 0x3b, 0x40, 0x69, 0x42, 0x7f, 0x19, 0xc9, 0xe8,
0x0f, 0x3e, 0x0a, 0x86, 0x05, 0x8b, 0x71, 0x07, 0x35, 0x3f, 0x64, 0x42, 0xa6, 0x3c, 0xef, 0x78,
0x9b, 0xde, 0x56, 0x8b, 0x34, 0xcf, 0x0d, 0x89, 0xbf, 0x80, 0x9a, 0x03, 0xc1, 0x63, 0x26, 0x65,
0xa7, 0xb6, 0xe9, 0x6d, 0xb5, 0xef, 0xaf, 0x6e, 0x83, 0xfa, 0x6d, 0x0b, 0x92, 0x66, 0x61, 0x3e,
0x70, 0x17, 0x05, 0x84, 0x73, 0xd5, 0xf1, 0xb5, 0x14, 0x32, 0x52, 0x80, 0x90, 0x40, 0x70, 0xae,
0xf0, 0x5d, 0x14, 0x1e, 0x70, 0xa9, 0x72, 0x3a, 0x65, 0x9d, 0x40, 0xef, 0x11, 0x4e, 0x2c, 0x8d,
0xbf, 0x88, 0x1a, 0x87, 0x7c, 0x96, 0x2b, 0xd9, 0xa9, 0x6f, 0xfa, 0x5b, 0xed, 0xfb, 0x6d, 0xb3,
0x5a, 0x63, 0xbb, 0xc1, 0xb3, 0xbf, 0xbe, 0x79, 0x8b, 0x34, 0xa6, 0x5a, 0x00, 0xbf, 0x85, 0xea,
0x07, 0x9c, 0x9f, 0xc9, 0x4e, 0x43, 0xef, 0x63, 0x25, 0x35, 0x44, 0xea, 0x13, 0xf8, 0xc1, 0xdf,
0x45, 0xed, 0x5e, 0x9e, 0x73, 0x45, 0x55, 0xca, 0x73, 0xd9, 0x69, 0x6a, 0x95, 0xff, 0x6f, 0x04,
0xe1, 0xb6, 0xdb, 0x0e, 0xf7, 0x41, 0xae, 0xc4, 0x05, 0x69, 0xd3, 0x05, 0x02, 0x3b, 0x3c, 0x4a,
0xf3, 0xd9, 0xd3, 0x4e, 0xe8, 0xee, 0xa0, 0x21, 0x52, 0xcf, 0xe0, 0x07, 0x8c, 0x32, 0xe4, 0x19,
0x15, 0xa9, 0xec, 0xb4, 0x5c, 0xa3, 0x58, 0x90, 0x34, 0xa5, 0xf9, 0x00, 0xc1, 0x27, 0x69, 0x9e,
0xf0, 0xb9, 0xec, 0x20, 0x57, 0xd0, 0x82, 0xa4, 0x39, 0x37, 0x1f, 0x77, 0xbf, 0x87, 0x6e, 0x5f,
0x3d, 0x15, 0xbe, 0x8d, 0xfc, 0x33, 0x76, 0x61, 0x1d, 0x02, 0x9f, 0xf8, 0x55, 0x54, 0x3f, 0xa7,
0xd9, 0x8c, 0x69, 0x57, 0xb4, 0x88, 0x21, 0xde, 0xab, 0xbd, 0xeb, 0x45, 0xbf, 0xf3, 0x2b, 0x3f,
0x81, 0xa5, 0x8f, 0x99, 0x98, 0xa6, 0x39, 0xcd, 0xf4, 0xe2, 0x90, 0x84, 0xca, 0xd2, 0xf8, 0xcb,
0xa8, 0xbd, 0xc7, 0x73, 0xc9, 0x33, 0x36, 0x4c, 0x3f, 0x65, 0xd6, 0xa5, 0x2d, 0x73, 0xa8, 0x5d,
0xfe, 0x94, 0xb4, 0xe3, 0x05, 0x17, 0xbf, 0x8d, 0x82, 0x13, 0xc9, 0xc4, 0xb2, 0x4b, 0x01, 0xb1,
0x3e, 0x09, 0x66, 0x92, 0x09, 0x8c, 0x51, 0xd0, 0x13, 0x63, 0xd9, 0x09, 0x36, 0xfd, 0xad, 0x16,
0x09, 0xa8, 0x18, 0x4b, 0x38, 0xfa, 0x83, 0xfc, 0x5c, 0x7b, 0xb3, 0x45, 0x7c, 0x96, 0x9f, 0x03,
0xb2, 0x37, 0x4f, 0xb4, 0xd7, 0x5a, 0xc4, 0x8f, 0xe7, 0x09, 0xfe, 0x36, 0x5a, 0xd9, 0xa3, 0x05,
0x3d, 0x4d, 0xb3, 0x54, 0xa5, 0x0c, 0xfc, 0x04, 0xbb, 0xbc, 0xee, 0x98, 0xdb, 0x65, 0x93, 0x95,
0xd8, 0xa1, 0xf0, 0x57, 0x51, 0x93, 0x64, 0xe9, 0x34, 0x55, 0xb2, 0x13, 0x6a, 0xff, 0xde, 0xb1,
0x61, 0x79, 0x34, 0xec, 0xff, 0xc0, 0x70, 0xec, 0x21, 0x9b, 0xc2, 0xc8, 0xe1, 0x2d, 0xb4, 0xfe,
0x98, 0x3f, 0x66, 0xf3, 0x81, 0x48, 0xcf, 0xd3, 0x8c, 0x8d, 0x99, 0x71, 0x5e, 0x48, 0xd6, 0xf3,
0x65, 0x18, 0x24, 0x7b, 0x45, 0x41, 0xc5, 0x94, 0x8b, 0x81, 0xe0, 0xa3, 0x34, 0x63, 0xda, 0x7b,
0x2d, 0xb2, 0x4e, 0x97, 0x61, 0xbc, 0x89, 0xda, 0x47, 0x47, 0x87, 0xc3, 0x98, 0x0b, 0xd6, 0x4b,
0x3e, 0xe9, 0xb4, 0x37, 0xbd, 0x2d, 0x9f, 0xb4, 0xf9, 0x02, 0xc2, 0x11, 0x5a, 0x19, 0x32, 0x1d,
0x35, 0x8f, 0xe8, 0x29, 0xcb, 0x3a, 0x2b, 0x5a, 0xd1, 0x8a, 0x74, 0xb0, 0xe8, 0x1d, 0xe4, 0xef,
0xf2, 0xa7, 0x78, 0x03, 0x35, 0x0e, 0x58, 0x3a, 0x9e, 0x28, 0xed, 0xb5, 0x55, 0xd2, 0x98, 0x68,
0x0a, 0xbc, 0xfe, 0x24, 0x4d, 0xd4, 0x44, 0x7b, 0x6b, 0x95, 0xd4, 0xe7, 0x40, 0x44, 0xb9, 0x71,
0x0e, 0x18, 0xf6, 0xa4, 0xbf, 0x6f, 0x97, 0xf8, 0xb3, 0xfe, 0x3e, 0x20, 0xef, 0xf7, 0xf7, 0xad,
0xb4, 0x3f, 0xee, 0xef, 0xe3, 0xcf, 0xa3, 0xb5, 0x5e, 0x92, 0xa4, 0x10, 0x5b, 0x34, 0x7b, 0x3f,
0x4d, 0x64, 0xc7, 0xdf, 0xf4, 0xb7, 0x56, 0xc9, 0x1a, 0x5d, 0x42, 0x21, 0x72, 0x40, 0xa7, 0x9b,
0xa3, 0x33, 0x4b, 0x47, 0xbf, 0xf2, 0xd0, 0x9d, 0x6b, 0x5e, 0x81, 0x15, 0xbb, 0x7c, 0x96, 0x27,
0x69, 0x3e, 0xee, 0x78, 0xda, 0xdb, 0xe1, 0xa9, 0xa5, 0xf1, 0x1b, 0xa8, 0xf5, 0x60, 0x34, 0x62,
0xb1, 0x4a, 0xcf, 0x21, 0xd2, 0x80, 0xd9, 0x62, 0x25, 0x00, 0xa6, 0xeb, 0xe7, 0x13, 0x26, 0x52,
0x45, 0x4f, 0x33, 0xa6, 0x0f, 0xd4, 0x22, 0xed, 0x74, 0x01, 0xc1, 0xfa, 0x01, 0xc4, 0xad, 0x52,
0x2c, 0xb1, 0xd1, 0xd5, 0x2a, 0x4a, 0x00, 0x4a, 0x56, 0x6f, 0x7a, 0x9a, 0xb2, 0x5c, 0xd9, 0x30,
0x6b, 0x52, 0x43, 0x46, 0x7d, 0xd4, 0x76, 0xc2, 0x00, 0xe2, 0xf3, 0xf8, 0xa2, 0x60, 0x36, 0x8f,
0x02, 0x75, 0x51, 0x30, 0xc0, 0x0e, 0xa8, 0x48, 0xb4, 0x8d, 0x02, 0x12, 0x4c, 0xa8, 0x48, 0x00,
0x1b, 0xf2, 0x91, 0x29, 0x60, 0x01, 0x09, 0x24, 0x1f, 0xa9, 0x88, 0xa3, 0xba, 0x2e, 0x42, 0x70,
0xda, 0x84, 0x49, 0x95, 0xe6, 0x3a, 0x41, 0xad, 0x2e, 0x17, 0x02, 0xef, 0x49, 0x3e, 0x13, 0x71,
0x99, 0x9c, 0x96, 0x02, 0xb5, 0xb0, 0xa5, 0x56, 0x5b, 0x6e, 0xdf, 0x41, 0x4d, 0x5e, 0x98, 0xea,
0x64, 0xee, 0x55, 0x92, 0xd1, 0x37, 0x4c, 0x15, 0x85, 0x55, 0x03, 0xaa, 0x26, 0xe5, 0xa1, 0x0b,
0xaa, 0x26, 0x60, 0x6b, 0xc2, 0x68, 0xc2, 0xf3, 0xec, 0x42, 0xef, 0x11, 0x92, 0x50, 0x58, 0x3a,
0xfa, 0x99, 0x67, 0xeb, 0x22, 0xbe, 0x87, 0xc2, 0x81, 0x60, 0x52, 0x51, 0xa1, 0xb4, 0x47, 0xaa,
0xc4, 0x05, 0xb6, 0xcd, 0x89, 0xb0, 0xb0, 0x12, 0x78, 0x1b, 0xb5, 0x06, 0x5c, 0x2a, 0x23, 0x5e,
0x7b, 0x81, 0x78, 0xab, 0x28, 0x45, 0xb4, 0x76, 0x4d, 0xf0, 0x42, 0xbb, 0xec, 0x66, 0xed, 0x56,
0x22, 0xfa, 0x08, 0x05, 0x80, 0xdf, 0x78, 0x9b, 0xb2, 0x6c, 0xd4, 0xae, 0x97, 0x0d, 0x7f, 0x51,
0x36, 0x3a, 0xa8, 0x79, 0x9c, 0x4e, 0x19, 0x9f, 0x29, 0x1d, 0x90, 0x3e, 0x69, 0x2a, 0x43, 0x46,
0xbf, 0xa9, 0xdb, 0x3a, 0x8d, 0xbf, 0x83, 0xda, 0x27, 0xfd, 0xfd, 0x43, 0x5a, 0x14, 0x69, 0x3e,
0x96, 0xf6, 0xd2, 0xaf, 0x3a, 0x75, 0xa4, 0x62, 0xda, 0x03, 0xb6, 0x67, 0x0b, 0x71, 0x58, 0xfd,
0xbe, 0xb3, 0xba, 0xf6, 0x9f, 0x57, 0x8f, 0x9d, 0xd5, 0x3b, 0xa8, 0x31, 0xbc, 0x90, 0xb1, 0xca,
0xac, 0x35, 0xdc, 0xf2, 0xb5, 0x6d, 0x38, 0xa6, 0xc5, 0x34, 0xa4, 0x26, 0xf0, 0x7d, 0xd4, 0x22,
0xcc, 0x84, 0x86, 0xd4, 0x57, 0x5a, 0xde, 0xac, 0xe2, 0x91, 0x96, 0x28, 0x3f, 0x21, 0xf8, 0xf6,
0xc6, 0x82, 0xcf, 0x0a, 0xa9, 0xad, 0x58, 0x37, 0xc1, 0x17, 0x2f, 0x20, 0xfc, 0x1e, 0x42, 0x8f,
0xe9, 0x94, 0xc9, 0x82, 0x82, 0xda, 0xc6, 0xb5, 0x3b, 0x54, 0x4c, 0x7b, 0x07, 0x94, 0x57, 0xd2,
0x50, 0x4a, 0xf7, 0xd9, 0x79, 0x1a, 0xb3, 0xb2, 0x55, 0xde, 0x71, 0x16, 0x1a, 0x4e, 0x59, 0x4a,
0x13, 0x23, 0x87, 0xef, 0xa1, 0xe6, 0x90, 0xc5, 0x31, 0x9f, 0x16, 0xb6, 0x49, 0x62, 0x67, 0x89,
0xe5, 0x90, 0xa6, 0x34, 0x1f, 0xf8, 0x1e, 0xba, 0x03, 0x31, 0x3d, 0x92, 0x03, 0xc1, 0x0b, 0x3a,
0x36, 0x19, 0xd4, 0xd2, 0x97, 0xb8, 0x23, 0xae, 0x32, 0xe0, 0xb2, 0x87, 0x54, 0x9e, 0xb1, 0x04,
0x2e, 0x06, 0x6d, 0x53, 0xd7, 0x85, 0xe9, 0x02, 0xc2, 0x6f, 0xa3, 0xd5, 0x32, 0x0f, 0x8c, 0x4c,
0x5b, 0xcb, 0xac, 0x0a, 0x17, 0xc4, 0x5d, 0x84, 0x74, 0xea, 0xba, 0x65, 0x17, 0x4d, 0x2b, 0x04,
0xef, 0xa0, 0xb0, 0x9f, 0x2b, 0x96, 0x91, 0x44, 0x75, 0x56, 0xf5, 0x25, 0x5e, 0x71, 0x9d, 0x6e,
0x59, 0x24, 0x4c, 0xed, 0xd7, 0xdd, 0x6f, 0xa1, 0xb6, 0xe3, 0xd0, 0x97, 0xea, 0xce, 0x6f, 0x56,
0x63, 0x00, 0x08, 0x25, 0xb3, 0xe9, 0xb4, 0x5c, 0x68, 0x08, 0x10, 0xb0, 0xb3, 0xc3, 0x0b, 0x04,
0x3e, 0x46, 0x6b, 0xcb, 0xc1, 0xa8, 0xbb, 0x05, 0x97, 0xaa, 0x2a, 0xfd, 0x8d, 0x89, 0xa6, 0x74,
0xb0, 0x94, 0x03, 0x63, 0xd5, 0x05, 0xda, 0xf1, 0x02, 0xd2, 0x85, 0x0e, 0x9a, 0xbf, 0xaf, 0x59,
0x81, 0x4c, 0x3f, 0x65, 0xd1, 0xbb, 0x56, 0x7f, 0x15, 0x28, 0x2f, 0x2a, 0x9b, 0x3a, 0x02, 0x6b,
0x8b, 0x3c, 0x8e, 0x7e, 0xe1, 0xa1, 0xb6, 0x13, 0x2a, 0x2f, 0xca, 0x75, 0xad, 0xab, 0xe6, 0xe8,
0x7a, 0x15, 0xd5, 0x0f, 0xe9, 0x27, 0xdc, 0x4c, 0x17, 0x3e, 0xa9, 0x4f, 0x81, 0xd0, 0x68, 0x9a,
0x73, 0x61, 0xb3, 0xbd, 0x3e, 0x05, 0x02, 0x2a, 0xdf, 0xc3, 0x34, 0x63, 0x87, 0x3c, 0x61, 0x3a,
0xfa, 0x57, 0x49, 0x38, 0xb2, 0x74, 0xd9, 0xff, 0x1a, 0xd7, 0xfa, 0x5f, 0xb3, 0xea, 0x7f, 0xd1,
0xdf, 0x6a, 0xf6, 0x7a, 0x55, 0x7a, 0xe1, 0x6f, 0x2e, 0xa2, 0xde, 0xbb, 0x96, 0xb9, 0x86, 0x63,
0x72, 0xee, 0x6a, 0xec, 0xc3, 0xac, 0xca, 0xa6, 0x5c, 0x5c, 0xd8, 0xe1, 0xc9, 0xcd, 0x16, 0xc3,
0x20, 0x8d, 0xa9, 0xfe, 0xc5, 0x9b, 0xc8, 0xdf, 0x1b, 0x9c, 0xd8, 0xf1, 0x69, 0xcd, 0x1d, 0x6c,
0x06, 0x27, 0xc4, 0x8f, 0x07, 0x27, 0xf8, 0x73, 0x28, 0x18, 0x40, 0x3b, 0x36, 0x85, 0x60, 0xdd,
0x11, 0x01, 0x98, 0x04, 0x05, 0x74, 0xe5, 0x7b, 0xa8, 0xb9, 0x9b, 0xf1, 0xf8, 0xac, 0x7f, 0xa4,
0x2f, 0xbf, 0x9c, 0x6d, 0x96, 0x43, 0x9a, 0xa7, 0xe6, 0x03, 0x3f, 0x44, 0x6b, 0x07, 0xb3, 0x31,
0x2b, 0xe8, 0x98, 0x3d, 0x32, 0x03, 0x92, 0x29, 0x07, 0x1d, 0x67, 0xd1, 0x92, 0x80, 0xbd, 0xe0,
0xda, 0x64, 0x69, 0x15, 0xec, 0xfa, 0x98, 0xa9, 0x39, 0x17, 0x67, 0x76, 0x32, 0x73, 0x77, 0xb5,
0x1c, 0xd2, 0xcc, 0xcd, 0x47, 0xf4, 0x97, 0x32, 0x0a, 0x8c, 0x09, 0xc0, 0x8f, 0x5a, 0x8f, 0x0e,
0x03, 0x1f, 0xe6, 0x66, 0x68, 0xc5, 0x9b, 0xa8, 0x4d, 0x98, 0x64, 0xe2, 0xdc, 0xd4, 0x80, 0x9a,
0x19, 0x97, 0xc4, 0x02, 0xd2, 0xb1, 0x39, 0xa7, 0x85, 0x0d, 0x8a, 0x40, 0xce, 0x69, 0x01, 0x91,
0xfe, 0x01, 0x13, 0x39, 0xcb, 0x6c, 0x50, 0x34, 0xce, 0x34, 0x05, 0xf3, 0x81, 0xc1, 0x8f, 0xf7,
0x06, 0xda, 0x32, 0x3e, 0x69, 0x9d, 0x95, 0x00, 0xe4, 0x3f, 0x68, 0x2a, 0xd2, 0x1c, 0xde, 0x2e,
0x0d, 0xdd, 0xd4, 0x91, 0xac, 0x10, 0xfc, 0x25, 0x74, 0x7b, 0x3f, 0x95, 0x30, 0x68, 0x1c, 0x1d,
0x1d, 0x7e, 0x90, 0x66, 0x19, 0x13, 0xfa, 0xa2, 0x21, 0xb9, 0x9d, 0x5c, 0xc1, 0xa3, 0x3f, 0x7a,
0x28, 0x2c, 0x1d, 0x07, 0xc7, 0x19, 0x4e, 0xa8, 0xd0, 0x81, 0x03, 0x4a, 0x1b, 0x52, 0x53, 0x70,
0xe5, 0xef, 0xcf, 0xb8, 0xa2, 0xf6, 0x5a, 0xf5, 0x1f, 0x01, 0x01, 0xd2, 0x03, 0x26, 0x52, 0x9e,
0xd8, 0xb9, 0xa2, 0x51, 0x68, 0x0a, 0x66, 0x4c, 0xc2, 0x68, 0x06, 0xdd, 0x8c, 0xcc, 0x72, 0xf8,
0xb1, 0xb7, 0x5b, 0x17, 0xcb, 0x30, 0x0c, 0x6f, 0xa5, 0xa4, 0xd5, 0x54, 0xd7, 0x9a, 0xd6, 0xc4,
0x12, 0x0a, 0xa6, 0xdb, 0x2b, 0x66, 0xd2, 0x8e, 0xd8, 0x41, 0x5c, 0xcc, 0x24, 0x60, 0x87, 0x6c,
0x6a, 0x66, 0xeb, 0x16, 0x09, 0xa6, 0x6c, 0x2a, 0xa3, 0xb9, 0x9d, 0xe3, 0x9e, 0xe8, 0xe9, 0xd2,
0x66, 0x6d, 0x95, 0x8d, 0xde, 0x8d, 0xd9, 0x58, 0x73, 0xb3, 0x71, 0x03, 0x35, 0xcc, 0x5a, 0x5b,
0x41, 0x1a, 0x73, 0x33, 0xa7, 0x76, 0x11, 0x7a, 0xc4, 0xe8, 0xc8, 0xf2, 0x02, 0xcd, 0x43, 0x59,
0x85, 0x44, 0x27, 0xe8, 0x15, 0xbd, 0xf1, 0xf1, 0x44, 0x70, 0xa5, 0x32, 0xf6, 0x5f, 0x6c, 0x8d,
0x51, 0x40, 0xa8, 0x62, 0xe5, 0x8c, 0x26, 0xa8, 0x62, 0xd1, 0x3f, 0x7c, 0xb4, 0xe2, 0xa6, 0x82,
0x73, 0x3e, 0xef, 0xdf, 0x9c, 0xaf, 0x76, 0xf5, 0x7c, 0xb8, 0x87, 0x56, 0x5c, 0x9b, 0xdc, 0xd0,
0xd1, 0x5d, 0xb6, 0x4d, 0x9b, 0x95, 0xb9, 0x6b, 0xc6, 0x13, 0xf4, 0x5a, 0x79, 0x3b, 0x68, 0x51,
0xbb, 0x85, 0xb4, 0xba, 0x02, 0xad, 0xeb, 0xff, 0x1c, 0x5d, 0xcb, 0x56, 0xb0, 0xda, 0x5e, 0x53,
0x37, 0xad, 0xc6, 0x4f, 0xd0, 0x46, 0x29, 0xfe, 0x44, 0xa4, 0x8a, 0x2d, 0xf4, 0xd6, 0x3f, 0x9b,
0xde, 0x0d, 0x75, 0xe3, 0x72, 0x57, 0x31, 0xec, 0xd8, 0x3f, 0x1a, 0x0c, 0xad, 0xe2, 0xc6, 0x4b,
0x2a, 0x5e, 0x5e, 0x8e, 0x7f, 0x88, 0x5e, 0x5f, 0x3a, 0xb1, 0xa3, 0xb9, 0xf9, 0xd9, 0x34, 0xbf,
0xae, 0x6e, 0x5e, 0x1f, 0xbd, 0x85, 0x5a, 0x55, 0x85, 0xbc, 0xb9, 0xce, 0x44, 0x3f, 0x29, 0xdf,
0x2a, 0x6e, 0x21, 0x07, 0xd9, 0x5e, 0x96, 0xf1, 0xb9, 0x7d, 0x14, 0xd7, 0x29, 0x10, 0xff, 0x73,
0x6f, 0xda, 0x40, 0x8d, 0x5e, 0xac, 0xff, 0x1f, 0x31, 0x73, 0x59, 0x83, 0x6a, 0x2a, 0xca, 0x6c,
0x54, 0xda, 0x52, 0x09, 0x93, 0xec, 0x5e, 0x46, 0xa5, 0xac, 0x1a, 0x76, 0x33, 0x36, 0x24, 0xde,
0x45, 0x68, 0x20, 0x52, 0x2e, 0xcc, 0x33, 0xd8, 0x0c, 0xa0, 0x6f, 0x5c, 0x99, 0x45, 0xc4, 0x88,
0xc6, 0xcc, 0x4a, 0x5d, 0x94, 0x43, 0x5c, 0x51, 0xad, 0x8a, 0x1e, 0x22, 0x7c, 0xbd, 0xb2, 0x43,
0xdf, 0x1c, 0xd0, 0x31, 0x83, 0x0e, 0x6f, 0xfb, 0x71, 0x58, 0x58, 0x7a, 0x61, 0x39, 0xf3, 0x06,
0xb2, 0x96, 0x3b, 0x40, 0x1b, 0x37, 0xef, 0x09, 0x76, 0x82, 0xe1, 0xa0, 0xec, 0xeb, 0xfa, 0x7f,
0x1b, 0xd0, 0x6f, 0xf9, 0x36, 0x9f, 0x42, 0x7b, 0xa6, 0x8b, 0xe8, 0x97, 0x9e, 0x35, 0x80, 0x9d,
0x07, 0x61, 0x6c, 0xdb, 0x67, 0x23, 0x3a, 0xcb, 0x54, 0x2f, 0x76, 0x1e, 0x51, 0xab, 0x89, 0x0b,
0x82, 0x54, 0x4f, 0xc4, 0x93, 0x54, 0xb1, 0x58, 0xcd, 0x04, 0x2b, 0xdf, 0x07, 0xab, 0xd4, 0x05,
0xe1, 0xf0, 0x0f, 0x33, 0x3a, 0x96, 0xf6, 0xa9, 0x50, 0x1f, 0x01, 0x81, 0xbf, 0x86, 0x42, 0x98,
0xd0, 0x68, 0x96, 0x49, 0x9b, 0x70, 0x4b, 0x73, 0xa9, 0x61, 0x95, 0x8f, 0x14, 0x69, 0x25, 0xa3,
0x14, 0xad, 0xbb, 0xe7, 0xec, 0x89, 0x31, 0xa8, 0xef, 0xe7, 0x09, 0x7b, 0x6a, 0x2b, 0x7c, 0x3d,
0x05, 0x02, 0xd0, 0x0f, 0xab, 0xf9, 0x2e, 0xb0, 0xf3, 0x1d, 0xd8, 0x40, 0xa3, 0xc7, 0x73, 0x6e,
0xcb, 0x52, 0x78, 0x6e, 0x69, 0xbc, 0x86, 0x6a, 0x47, 0x85, 0x7d, 0x49, 0xd7, 0x78, 0x11, 0xfd,
0xbc, 0xb2, 0x89, 0xd9, 0x1c, 0x54, 0xea, 0x89, 0xcb, 0xbe, 0x9d, 0xeb, 0x7a, 0x2c, 0x37, 0x21,
0x55, 0x75, 0x48, 0x1d, 0x52, 0xda, 0x36, 0x6f, 0xa0, 0x90, 0x09, 0x91, 0x73, 0xc1, 0x6c, 0xe9,
0x3d, 0xb8, 0x45, 0x2a, 0x04, 0xef, 0x38, 0xff, 0xc3, 0xb4, 0xef, 0xbf, 0x76, 0x7d, 0x22, 0xef,
0x89, 0xf2, 0x09, 0xa3, 0x5f, 0x5b, 0xbb, 0x08, 0x85, 0x0f, 0x60, 0x31, 0x61, 0x2a, 0xfa, 0x3a,
0x5a, 0x5d, 0x9a, 0x7b, 0xc1, 0x0f, 0x8f, 0xde, 0xd9, 0xa3, 0xf1, 0x84, 0x0d, 0xe3, 0x09, 0x9b,
0xd2, 0xd2, 0x5b, 0x99, 0x0b, 0xee, 0xfe, 0xd4, 0x7b, 0xf6, 0xbc, 0x7b, 0xeb, 0xcf, 0xcf, 0xbb,
0xb7, 0xfe, 0xf9, 0xbc, 0xeb, 0xfd, 0xf8, 0xb2, 0xeb, 0xfd, 0xfa, 0xb2, 0xeb, 0xfd, 0xf6, 0xb2,
0xeb, 0xfd, 0xfe, 0xb2, 0xeb, 0x3d, 0xbb, 0xec, 0x7a, 0x7f, 0xba, 0xec, 0x7a, 0x7f, 0xbf, 0xec,
0x7a, 0x1f, 0x7d, 0xfc, 0x92, 0x7f, 0x73, 0x0a, 0xd3, 0xfe, 0x76, 0xce, 0x53, 0xa1, 0x1c, 0x56,
0x71, 0x36, 0xbe, 0xf6, 0x0f, 0x28, 0xdc, 0xf4, 0xb4, 0xa1, 0xe9, 0x77, 0xfe, 0x15, 0x00, 0x00,
0xff, 0xff, 0x63, 0x45, 0x7b, 0x17, 0x4f, 0x15, 0x00, 0x00,
}
func (this *Spec) Equal(that interface{}) bool {

View File

@ -239,15 +239,6 @@ func OCItoGRPC(ociSpec *specs.Spec) (*Spec, error) {
return s, err
}
// GRPCtoOCI converts a gRPC specification back into an OCI representation
func GRPCtoOCI(grpcSpec *Spec) (*specs.Spec, error) {
s := &specs.Spec{}
err := copyStruct(s, grpcSpec)
return s, err
}
// ProcessOCItoGRPC converts an OCI process specification into its gRPC
// representation
func ProcessOCItoGRPC(ociProcess *specs.Process) (*Process, error) {

View File

@ -93,26 +93,6 @@ func TestOCItoGRPC(t *testing.T) {
assertIsEqual(t, &ociSpec, spec)
}
func TestGRPCtoOCI(t *testing.T) {
assert := assert.New(t)
var ociSpec specs.Spec
configJSONBytes, err := ioutil.ReadFile(ociConfigFile)
assert.NoError(err, "Could not open OCI config file")
err = json.Unmarshal(configJSONBytes, &ociSpec)
assert.NoError(err, "Could not unmarshall OCI config file")
grpcSpec, err := OCItoGRPC(&ociSpec)
assert.NoError(err, "Could not convert OCI config file")
newOciSpec, err := GRPCtoOCI(grpcSpec)
assert.NoError(err, "Could not convert gRPC structure")
assertIsEqual(t, newOciSpec, grpcSpec)
}
func TestProcessOCItoGRPC(t *testing.T) {
assert := assert.New(t)
var ociSpec specs.Spec

View File

@ -93,7 +93,7 @@ var xxx_messageInfo_IPAddress proto.InternalMessageInfo
type Interface struct {
Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
IPAddresses []*IPAddress `protobuf:"bytes,3,rep,name=IPAddresses,proto3" json:"IPAddresses,omitempty"`
IPAddresses []*IPAddress `protobuf:"bytes,3,rep,name=IPAddresses,json=iPAddresses,proto3" json:"IPAddresses,omitempty"`
Mtu uint64 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"`
HwAddr string `protobuf:"bytes,5,opt,name=hwAddr,proto3" json:"hwAddr,omitempty"`
// PCI path for the device (see the pci::Path (Rust) or types.PciPath (Go) type for format details)
@ -240,37 +240,37 @@ func init() {
}
var fileDescriptor_b0c68e5b9fabcd81 = []byte{
// 472 bytes of a gzipped FileDescriptorProto
// 474 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x31, 0x8f, 0xd3, 0x30,
0x18, 0x86, 0xeb, 0xa6, 0xc9, 0x35, 0xae, 0x0e, 0x2a, 0x0b, 0x9d, 0x2c, 0x90, 0xa2, 0xa8, 0x0b,
0x11, 0x12, 0x8d, 0x54, 0x10, 0xfb, 0x31, 0x9c, 0xd4, 0x05, 0x15, 0x6f, 0xb0, 0x20, 0xd7, 0x75,
0xd3, 0xa8, 0x49, 0x1c, 0xd9, 0x4e, 0xab, 0x8a, 0x85, 0x3f, 0xc2, 0xff, 0xb9, 0x91, 0x91, 0xf1,
0xae, 0xbf, 0x04, 0xd9, 0x4e, 0xab, 0x50, 0x58, 0x6e, 0xca, 0xfb, 0x7c, 0xb6, 0xf3, 0xbd, 0xdf,
0x1b, 0x07, 0x7e, 0xce, 0x72, 0xbd, 0x69, 0x96, 0x53, 0x26, 0xca, 0x74, 0x4b, 0x35, 0x7d, 0xcb,
0x44, 0xa5, 0x69, 0x5e, 0x71, 0xa9, 0xfe, 0x61, 0x25, 0x59, 0x4a, 0x33, 0x5e, 0xe9, 0xb4, 0x96,
0x42, 0x0b, 0x26, 0x0a, 0xe5, 0x94, 0x4a, 0xf5, 0xa1, 0xe6, 0x6a, 0x6a, 0x01, 0xf9, 0x16, 0x26,
0x4b, 0x18, 0xce, 0x17, 0xb7, 0xab, 0x95, 0xe4, 0x4a, 0xa1, 0xd7, 0x30, 0x58, 0xd3, 0x32, 0x2f,
0x0e, 0x18, 0xc4, 0x20, 0x79, 0x36, 0x7b, 0x3e, 0x75, 0x27, 0xe6, 0x8b, 0x3b, 0x5b, 0x26, 0xed,
0x32, 0xc2, 0xf0, 0x8a, 0xba, 0x33, 0xb8, 0x1f, 0x83, 0x24, 0x24, 0x27, 0x44, 0x08, 0x0e, 0x4a,
0xaa, 0xb6, 0xd8, 0xb3, 0x65, 0xab, 0x27, 0x0f, 0x00, 0x86, 0xf3, 0x4a, 0x73, 0xb9, 0xa6, 0x8c,
0xa3, 0x1b, 0x18, 0xac, 0xf8, 0x2e, 0x67, 0xdc, 0x36, 0x09, 0x49, 0x4b, 0xe6, 0x64, 0x45, 0x4b,
0xde, 0xbe, 0xd0, 0x6a, 0x34, 0x83, 0xa3, 0xb3, 0x3b, 0xae, 0xb0, 0x17, 0x7b, 0xc9, 0x68, 0x36,
0x3e, 0xbb, 0x6a, 0x57, 0x48, 0x77, 0x13, 0x1a, 0x43, 0xaf, 0xd4, 0x0d, 0x1e, 0xc4, 0x20, 0x19,
0x10, 0x23, 0x4d, 0xc7, 0xcd, 0xde, 0x6c, 0xc0, 0xbe, 0xeb, 0xe8, 0xc8, 0x4c, 0x51, 0xb3, 0x7c,
0x41, 0xf5, 0x06, 0x07, 0x6e, 0x8a, 0x16, 0x8d, 0x17, 0xd3, 0x03, 0x5f, 0x39, 0x2f, 0x46, 0xa3,
0x57, 0x30, 0x94, 0x74, 0xff, 0x6d, 0x5d, 0xd0, 0x4c, 0xe1, 0x61, 0x0c, 0x92, 0x6b, 0x32, 0x94,
0x74, 0x7f, 0x67, 0x78, 0xf2, 0x1d, 0xfa, 0x44, 0x34, 0xda, 0x4e, 0xb1, 0xe2, 0x4a, 0xb7, 0xb3,
0x59, 0x6d, 0xfa, 0x64, 0x54, 0xf3, 0x3d, 0x3d, 0x9c, 0xd2, 0x6a, 0xb1, 0x93, 0x85, 0xf7, 0x57,
0x16, 0x37, 0x30, 0x50, 0xa2, 0x91, 0x8c, 0xdb, 0x31, 0x42, 0xd2, 0x12, 0x7a, 0x01, 0x7d, 0xc5,
0x44, 0xcd, 0xed, 0x20, 0xd7, 0xc4, 0xc1, 0xe4, 0x27, 0x80, 0xa3, 0x5b, 0xb2, 0xf8, 0xc4, 0xf3,
0x6c, 0xb3, 0x14, 0xd2, 0xa4, 0xa6, 0xc5, 0x39, 0x12, 0x6b, 0xe5, 0xbf, 0xa9, 0x75, 0x36, 0x75,
0x9c, 0xf4, 0x2f, 0x9d, 0x14, 0x85, 0xf9, 0xb8, 0x27, 0x87, 0x8e, 0xac, 0x13, 0x4d, 0xb5, 0x33,
0xe8, 0x13, 0x07, 0xa6, 0xea, 0xf2, 0xf1, 0x5d, 0xd5, 0xc2, 0x9b, 0x97, 0x70, 0x78, 0xba, 0x41,
0x28, 0x80, 0xfd, 0xdd, 0xfb, 0x71, 0xcf, 0x3e, 0x3f, 0x8c, 0xc1, 0x47, 0x75, 0xff, 0x18, 0xf5,
0x7e, 0x3f, 0x46, 0xbd, 0x1f, 0xc7, 0x08, 0xdc, 0x1f, 0x23, 0xf0, 0xeb, 0x18, 0x81, 0x87, 0x63,
0x04, 0xbe, 0x7e, 0x79, 0xe2, 0x9d, 0x97, 0x4d, 0xa5, 0xf3, 0x92, 0xa7, 0xbb, 0x5c, 0xea, 0xce,
0x52, 0xbd, 0xcd, 0x2e, 0x7f, 0x87, 0x65, 0x60, 0xe5, 0xbb, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff,
0xed, 0x3e, 0x9a, 0x58, 0x57, 0x03, 0x00, 0x00,
0x18, 0x86, 0xcf, 0x4d, 0x93, 0x6b, 0x5c, 0x1d, 0x54, 0x16, 0x3a, 0x59, 0x20, 0x45, 0x51, 0x17,
0x22, 0x24, 0x1a, 0xa9, 0x20, 0xf6, 0x63, 0x38, 0xa9, 0x0b, 0x2a, 0xde, 0x60, 0x41, 0xae, 0xeb,
0xa6, 0x56, 0x93, 0x38, 0xb2, 0x9d, 0x56, 0x15, 0x0b, 0x7f, 0x84, 0xff, 0x73, 0x23, 0x23, 0xe3,
0x5d, 0x7f, 0x09, 0xb2, 0x9d, 0x96, 0x72, 0xb0, 0x30, 0xe5, 0x7d, 0x3e, 0xdb, 0xf9, 0xde, 0xef,
0x8d, 0x03, 0x3f, 0x16, 0xc2, 0xac, 0xdb, 0xc5, 0x84, 0xc9, 0x2a, 0xdf, 0x50, 0x43, 0x5f, 0x33,
0x59, 0x1b, 0x2a, 0x6a, 0xae, 0xf4, 0x5f, 0xac, 0x15, 0xcb, 0x69, 0xc1, 0x6b, 0x93, 0x37, 0x4a,
0x1a, 0xc9, 0x64, 0xa9, 0xbd, 0xd2, 0xb9, 0xd9, 0x37, 0x5c, 0x4f, 0x1c, 0xa0, 0xd0, 0xc1, 0x78,
0x01, 0xe3, 0xd9, 0xfc, 0x66, 0xb9, 0x54, 0x5c, 0x6b, 0xf4, 0x12, 0x46, 0x2b, 0x5a, 0x89, 0x72,
0x8f, 0x41, 0x0a, 0xb2, 0x27, 0xd3, 0xa7, 0x13, 0x7f, 0x62, 0x36, 0xbf, 0x75, 0x65, 0xd2, 0x2d,
0x23, 0x0c, 0x2f, 0xa9, 0x3f, 0x83, 0x7b, 0x29, 0xc8, 0x62, 0x72, 0x44, 0x84, 0x60, 0xbf, 0xa2,
0x7a, 0x83, 0x03, 0x57, 0x76, 0x7a, 0x7c, 0x0f, 0x60, 0x3c, 0xab, 0x0d, 0x57, 0x2b, 0xca, 0x38,
0xba, 0x86, 0xd1, 0x92, 0x6f, 0x05, 0xe3, 0xae, 0x49, 0x4c, 0x3a, 0xb2, 0x27, 0x6b, 0x5a, 0xf1,
0xee, 0x85, 0x4e, 0xa3, 0x29, 0x1c, 0x9e, 0xdc, 0x71, 0x8d, 0x83, 0x34, 0xc8, 0x86, 0xd3, 0xd1,
0xc9, 0x55, 0xb7, 0x42, 0x86, 0xe2, 0xf7, 0x26, 0x34, 0x82, 0x41, 0x65, 0x5a, 0xdc, 0x4f, 0x41,
0xd6, 0x27, 0x56, 0xda, 0x8e, 0xeb, 0x9d, 0xdd, 0x80, 0x43, 0xdf, 0xd1, 0x93, 0x9d, 0xa2, 0x61,
0x62, 0x4e, 0xcd, 0x1a, 0x47, 0x7e, 0x8a, 0x0e, 0xad, 0x17, 0xdb, 0x03, 0x5f, 0x7a, 0x2f, 0x56,
0xa3, 0x17, 0x30, 0x56, 0x74, 0xf7, 0x65, 0x55, 0xd2, 0x42, 0xe3, 0x41, 0x0a, 0xb2, 0x2b, 0x32,
0x50, 0x74, 0x77, 0x6b, 0x79, 0xfc, 0x15, 0x86, 0x44, 0xb6, 0xc6, 0x4d, 0xb1, 0xe4, 0xda, 0x74,
0xb3, 0x39, 0x6d, 0xfb, 0x14, 0xd4, 0xf0, 0x1d, 0xdd, 0x1f, 0xd3, 0xea, 0xf0, 0x2c, 0x8b, 0xe0,
0x8f, 0x2c, 0xae, 0x61, 0xa4, 0x65, 0xab, 0x18, 0x77, 0x63, 0xc4, 0xa4, 0x23, 0xf4, 0x0c, 0x86,
0x9a, 0xc9, 0x86, 0xbb, 0x41, 0xae, 0x88, 0x87, 0xf1, 0x77, 0x00, 0x87, 0x37, 0x64, 0xfe, 0x81,
0x8b, 0x62, 0xbd, 0x90, 0xca, 0xa6, 0x66, 0xe4, 0x29, 0x1d, 0x67, 0xe5, 0x9f, 0xa9, 0x9d, 0x6d,
0x3a, 0x73, 0xd2, 0x7b, 0xec, 0xa4, 0x2c, 0xed, 0xc7, 0x3d, 0x3a, 0xf4, 0xe4, 0x9c, 0x18, 0x6a,
0xbc, 0xc1, 0x90, 0x78, 0xb0, 0x55, 0x9f, 0x4f, 0xe8, 0xab, 0x0e, 0x5e, 0x3d, 0x87, 0x83, 0xe3,
0x0d, 0x42, 0x11, 0xec, 0x6d, 0xdf, 0x8e, 0x2e, 0xdc, 0xf3, 0xdd, 0x08, 0xbc, 0xd7, 0x77, 0x0f,
0xc9, 0xc5, 0xcf, 0x87, 0xe4, 0xe2, 0xdb, 0x21, 0x01, 0x77, 0x87, 0x04, 0xfc, 0x38, 0x24, 0xe0,
0xfe, 0x90, 0x80, 0xcf, 0x9f, 0xfe, 0xf3, 0xce, 0xab, 0xb6, 0x36, 0xa2, 0xe2, 0xf9, 0x56, 0x28,
0x73, 0xb6, 0xd4, 0x6c, 0x8a, 0xc7, 0xbf, 0xc3, 0x22, 0x72, 0xf2, 0xcd, 0xaf, 0x00, 0x00, 0x00,
0xff, 0xff, 0xf8, 0xd7, 0x14, 0x95, 0x57, 0x03, 0x00, 0x00,
}
func (m *IPAddress) Marshal() (dAtA []byte, err error) {

View File

@ -8,6 +8,7 @@ package cgroups
import (
"bufio"
"context"
"errors"
"fmt"
"io/ioutil"
"os"
@ -17,9 +18,9 @@ import (
"sync"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/rootless"
"github.com/opencontainers/runc/libcontainer"
libcontcgroups "github.com/opencontainers/runc/libcontainer/cgroups"
libcontcgroupsfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
libcontcgroupssystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/specconv"
"github.com/opencontainers/runtime-spec/specs-go"
@ -131,7 +132,7 @@ func New(config *Config) (*Manager, error) {
UseSystemdCgroup: useSystemdCgroup,
Spec: &newSpec,
RootlessCgroups: rootless,
}); err != nil {
}, nil); err != nil {
return nil, fmt.Errorf("Could not create cgroup config: %v", err)
}
}
@ -144,22 +145,28 @@ func New(config *Config) (*Manager, error) {
}
if useSystemdCgroup {
systemdCgroupFunc, err := libcontcgroupssystemd.NewSystemdCgroupsManager()
factory, err := libcontainer.New("")
if err != nil {
return nil, fmt.Errorf("Could not create linux factory for systemd cgroup manager: %v", err)
}
lfactory, ok := factory.(*libcontainer.LinuxFactory)
if !ok {
return nil, errors.New("expected linux factory returned on linux based systems")
}
err = libcontainer.SystemdCgroups(lfactory)
if err != nil {
return nil, fmt.Errorf("Could not create systemd cgroup manager: %v", err)
}
libcontcgroupssystemd.UseSystemd()
return &Manager{
mgr: systemdCgroupFunc(cgroups, cgroupPaths),
mgr: lfactory.NewCgroupsManager(cgroups, cgroupPaths),
}, nil
}
return &Manager{
mgr: &libcontcgroupsfs.Manager{
Cgroups: cgroups,
Rootless: rootless,
Paths: cgroupPaths,
},
mgr: libcontcgroupsfs.NewManager(cgroups, cgroupPaths, rootless),
}, nil
}
@ -299,7 +306,7 @@ func (m *Manager) AddDevice(ctx context.Context, device string) error {
return err
}
ld, err := DeviceToCgroupDevice(device)
ld, err := DeviceToCgroupDeviceRule(device)
if err != nil {
return err
}
@ -318,9 +325,14 @@ func (m *Manager) RemoveDevice(device string) error {
return err
}
ld, err := DeviceToCgroupDeviceRule(device)
if err != nil {
return err
}
m.Lock()
for i, d := range cgroups.Devices {
if d.Path == device {
if d.Major == ld.Major && d.Minor == ld.Minor {
cgroups.Devices = append(cgroups.Devices[:i], cgroups.Devices[i+1:]...)
m.Unlock()
return m.Apply()

View File

@ -7,11 +7,10 @@ package cgroups
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
)
@ -76,12 +75,11 @@ func IsSystemdCgroup(cgroupPath string) bool {
return false
}
func DeviceToCgroupDevice(device string) (*configs.Device, error) {
func DeviceToCgroupDeviceRule(device string) (*devices.Rule, error) {
var st unix.Stat_t
linuxDevice := configs.Device{
deviceRule := devices.Rule{
Allow: true,
Permissions: "rwm",
Path: device,
}
if err := unix.Stat(device, &st); err != nil {
@ -92,27 +90,23 @@ func DeviceToCgroupDevice(device string) (*configs.Device, error) {
switch devType {
case unix.S_IFCHR:
linuxDevice.Type = 'c'
deviceRule.Type = 'c'
case unix.S_IFBLK:
linuxDevice.Type = 'b'
deviceRule.Type = 'b'
default:
return nil, fmt.Errorf("unsupported device type: %v", devType)
}
major := int64(unix.Major(st.Rdev))
minor := int64(unix.Minor(st.Rdev))
linuxDevice.Major = major
linuxDevice.Minor = minor
deviceRule.Major = major
deviceRule.Minor = minor
linuxDevice.Gid = st.Gid
linuxDevice.Uid = st.Uid
linuxDevice.FileMode = os.FileMode(st.Mode)
return &linuxDevice, nil
return &deviceRule, nil
}
func DeviceToLinuxDevice(device string) (specs.LinuxDeviceCgroup, error) {
dev, err := DeviceToCgroupDevice(device)
dev, err := DeviceToCgroupDeviceRule(device)
if err != nil {
return specs.LinuxDeviceCgroup{}, err
}
@ -122,6 +116,6 @@ func DeviceToLinuxDevice(device string) (specs.LinuxDeviceCgroup, error) {
Type: string(dev.Type),
Major: &dev.Major,
Minor: &dev.Minor,
Access: dev.Permissions,
Access: string(dev.Permissions),
}, nil
}

View File

@ -105,7 +105,7 @@ func TestValidCgroupPath(t *testing.T) {
}
func TestDeviceToCgroupDevice(t *testing.T) {
func TestDeviceToCgroupDeviceRule(t *testing.T) {
assert := assert.New(t)
f, err := ioutil.TempFile("", "device")
@ -113,13 +113,13 @@ func TestDeviceToCgroupDevice(t *testing.T) {
f.Close()
// fail: regular file to device
dev, err := DeviceToCgroupDevice(f.Name())
dev, err := DeviceToCgroupDeviceRule(f.Name())
assert.Error(err)
assert.Nil(dev)
// fail: no such file
os.Remove(f.Name())
dev, err = DeviceToCgroupDevice(f.Name())
dev, err = DeviceToCgroupDeviceRule(f.Name())
assert.Error(err)
assert.Nil(dev)
@ -128,17 +128,13 @@ func TestDeviceToCgroupDevice(t *testing.T) {
t.Skipf("no such device: %v", devPath)
return
}
dev, err = DeviceToCgroupDevice(devPath)
dev, err = DeviceToCgroupDeviceRule(devPath)
assert.NoError(err)
assert.NotNil(dev)
assert.Equal(dev.Type, 'c')
assert.Equal(dev.Path, devPath)
assert.Equal(rune(dev.Type), 'c')
assert.NotZero(dev.Major)
assert.NotZero(dev.Minor)
assert.NotEmpty(dev.Permissions)
assert.NotZero(dev.FileMode)
assert.Zero(dev.Uid)
assert.Zero(dev.Gid)
assert.True(dev.Allow)
}

View File

@ -1086,34 +1086,6 @@ func getShmSize(c vc.ContainerConfig) (uint64, error) {
return shmSize, nil
}
// StatusToOCIState translates a virtcontainers container status into an OCI state.
func StatusToOCIState(status vc.ContainerStatus) specs.State {
return specs.State{
Version: specs.Version,
ID: status.ID,
Status: StateToOCIState(status.State.State),
Pid: status.PID,
Bundle: status.Annotations[vcAnnotations.BundlePathKey],
Annotations: status.Annotations,
}
}
// StateToOCIState translates a virtcontainers container state into an OCI one.
func StateToOCIState(state types.StateString) string {
switch state {
case types.StateReady:
return StateCreated
case types.StateRunning:
return StateRunning
case types.StateStopped:
return StateStopped
case types.StatePaused:
return StatePaused
default:
return ""
}
}
// EnvVars converts an OCI process environment variables slice
// into a virtcontainers EnvVar slice.
func EnvVars(envs []string) ([]types.EnvVar, error) {

View File

@ -188,163 +188,6 @@ func TestMinimalSandboxConfig(t *testing.T) {
assert.NoError(os.Remove(configPath))
}
func testStatusToOCIStateSuccessful(t *testing.T, cStatus vc.ContainerStatus, expected specs.State) {
ociState := StatusToOCIState(cStatus)
assert.Exactly(t, ociState, expected)
}
func TestStatusToOCIStateSuccessfulWithReadyState(t *testing.T) {
testContID := "testContID"
testPID := 12345
testRootFs := "testRootFs"
state := types.ContainerState{
State: types.StateReady,
}
containerAnnotations := map[string]string{
vcAnnotations.BundlePathKey: tempBundlePath,
}
cStatus := vc.ContainerStatus{
ID: testContID,
State: state,
PID: testPID,
RootFs: testRootFs,
Annotations: containerAnnotations,
}
expected := specs.State{
Version: specs.Version,
ID: testContID,
Status: "created",
Pid: testPID,
Bundle: tempBundlePath,
Annotations: containerAnnotations,
}
testStatusToOCIStateSuccessful(t, cStatus, expected)
}
func TestStatusToOCIStateSuccessfulWithRunningState(t *testing.T) {
testContID := "testContID"
testPID := 12345
testRootFs := "testRootFs"
state := types.ContainerState{
State: types.StateRunning,
}
containerAnnotations := map[string]string{
vcAnnotations.BundlePathKey: tempBundlePath,
}
cStatus := vc.ContainerStatus{
ID: testContID,
State: state,
PID: testPID,
RootFs: testRootFs,
Annotations: containerAnnotations,
}
expected := specs.State{
Version: specs.Version,
ID: testContID,
Status: "running",
Pid: testPID,
Bundle: tempBundlePath,
Annotations: containerAnnotations,
}
testStatusToOCIStateSuccessful(t, cStatus, expected)
}
func TestStatusToOCIStateSuccessfulWithStoppedState(t *testing.T) {
testContID := "testContID"
testPID := 12345
testRootFs := "testRootFs"
state := types.ContainerState{
State: types.StateStopped,
}
containerAnnotations := map[string]string{
vcAnnotations.BundlePathKey: tempBundlePath,
}
cStatus := vc.ContainerStatus{
ID: testContID,
State: state,
PID: testPID,
RootFs: testRootFs,
Annotations: containerAnnotations,
}
expected := specs.State{
Version: specs.Version,
ID: testContID,
Status: "stopped",
Pid: testPID,
Bundle: tempBundlePath,
Annotations: containerAnnotations,
}
testStatusToOCIStateSuccessful(t, cStatus, expected)
}
func TestStatusToOCIStateSuccessfulWithNoState(t *testing.T) {
testContID := "testContID"
testPID := 12345
testRootFs := "testRootFs"
containerAnnotations := map[string]string{
vcAnnotations.BundlePathKey: tempBundlePath,
}
cStatus := vc.ContainerStatus{
ID: testContID,
PID: testPID,
RootFs: testRootFs,
Annotations: containerAnnotations,
}
expected := specs.State{
Version: specs.Version,
ID: testContID,
Status: "",
Pid: testPID,
Bundle: tempBundlePath,
Annotations: containerAnnotations,
}
testStatusToOCIStateSuccessful(t, cStatus, expected)
}
func TestStateToOCIState(t *testing.T) {
var state types.StateString
assert := assert.New(t)
assert.Empty(StateToOCIState(state))
state = types.StateReady
assert.Equal(StateToOCIState(state), "created")
state = types.StateRunning
assert.Equal(StateToOCIState(state), "running")
state = types.StateStopped
assert.Equal(StateToOCIState(state), "stopped")
state = types.StatePaused
assert.Equal(StateToOCIState(state), "paused")
}
func TestEnvVars(t *testing.T) {
assert := assert.New(t)
envVars := []string{"foo=bar", "TERM=xterm", "HOME=/home/foo", "TERM=\"bar\"", "foo=\"\""}

View File

@ -28,6 +28,9 @@ const (
// StateStopped represents a sandbox/container that has been stopped.
StateStopped StateString = "stopped"
// StateCreating represents a sandbox/container that's in creating.
StateCreating StateString = "creating"
)
const (