Update to latest go-criu (4.0.2)
This updates to the latest version of go-criu (4.0.2) which is based on CRIU 3.14. As go-criu provides an existing way to query the CRIU binary for its version this also removes all the code from runc to handle CRIU version checking and now relies on go-criu. An important side effect of this change is that this raises the minimum CRIU version to 3.0.0 as that is the first CRIU version that supports CRIU version queries via RPC in contrast to parsing the output of 'criu --version' CRIU 3.0 has been released in April of 2017. Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
parent
b207d578ec
commit
944e057025
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module github.com/opencontainers/runc
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/checkpoint-restore/go-criu v0.0.0-20191125063657-fcdcd07065c5
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.0.2
|
||||
github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3
|
||||
github.com/containerd/console v1.0.0
|
||||
github.com/coreos/go-systemd/v22 v22.0.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,6 +1,6 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/checkpoint-restore/go-criu v0.0.0-20191125063657-fcdcd07065c5 h1:950diauOSoCNaQfvLE0WaFadyI/ilKIjb6jEUQnm+hE=
|
||||
github.com/checkpoint-restore/go-criu v0.0.0-20191125063657-fcdcd07065c5/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.0.2 h1:jt+rnBIhFtPw0fhtpYGcUOilh4aO9Hj7r+YLEtf30uA=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/cilium/ebpf v0.0.0-20200319110858-a7172c01168f h1:W1RQPz3nR8RxUw/Uqk71GU3JlZ7pNa1pXrHs98h0o9U=
|
||||
github.com/cilium/ebpf v0.0.0-20200319110858-a7172c01168f/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s=
|
||||
github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 h1:qcqzLJa2xCo9sgdCzpT/SJSYxROTEstuhf7ZBHMirms=
|
||||
|
|
|
@ -26,7 +26,8 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
||||
criurpc "github.com/checkpoint-restore/go-criu/rpc"
|
||||
"github.com/checkpoint-restore/go-criu/v4"
|
||||
criurpc "github.com/checkpoint-restore/go-criu/v4/rpc"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
|
@ -674,16 +675,6 @@ func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.
|
|||
var t criurpc.CriuReqType
|
||||
t = criurpc.CriuReqType_FEATURE_CHECK
|
||||
|
||||
// criu 1.8 => 10800
|
||||
if err := c.checkCriuVersion(10800); err != nil {
|
||||
// Feature checking was introduced with CRIU 1.8.
|
||||
// Ignore the feature check if an older CRIU version is used
|
||||
// and just act as before.
|
||||
// As all automated PR testing is done using CRIU 1.7 this
|
||||
// code will not be tested by automated PR testing.
|
||||
return nil
|
||||
}
|
||||
|
||||
// make sure the features we are looking for are really not from
|
||||
// some previous check
|
||||
criuFeatures = nil
|
||||
|
@ -733,50 +724,6 @@ func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseCriuVersion(path string) (int, error) {
|
||||
var x, y, z int
|
||||
|
||||
out, err := exec.Command(path, "-V").Output()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Unable to execute CRIU command: %s", path)
|
||||
}
|
||||
|
||||
x = 0
|
||||
y = 0
|
||||
z = 0
|
||||
if ep := strings.Index(string(out), "-"); ep >= 0 {
|
||||
// criu Git version format
|
||||
var version string
|
||||
if sp := strings.Index(string(out), "GitID"); sp > 0 {
|
||||
version = string(out)[sp:ep]
|
||||
} else {
|
||||
return 0, fmt.Errorf("Unable to parse the CRIU version: %s", path)
|
||||
}
|
||||
|
||||
n, err := fmt.Sscanf(version, "GitID: v%d.%d.%d", &x, &y, &z) // 1.5.2
|
||||
if err != nil {
|
||||
n, err = fmt.Sscanf(version, "GitID: v%d.%d", &x, &y) // 1.6
|
||||
y++
|
||||
} else {
|
||||
z++
|
||||
}
|
||||
if n < 2 || err != nil {
|
||||
return 0, fmt.Errorf("Unable to parse the CRIU version: %s %d %s", version, n, err)
|
||||
}
|
||||
} else {
|
||||
// criu release version format
|
||||
n, err := fmt.Sscanf(string(out), "Version: %d.%d.%d\n", &x, &y, &z) // 1.5.2
|
||||
if err != nil {
|
||||
n, err = fmt.Sscanf(string(out), "Version: %d.%d\n", &x, &y) // 1.6
|
||||
}
|
||||
if n < 2 || err != nil {
|
||||
return 0, fmt.Errorf("Unable to parse the CRIU version: %s %d %s", out, n, err)
|
||||
}
|
||||
}
|
||||
|
||||
return x*10000 + y*100 + z, nil
|
||||
}
|
||||
|
||||
func compareCriuVersion(criuVersion int, minVersion int) error {
|
||||
// simple function to perform the actual version compare
|
||||
if criuVersion < minVersion {
|
||||
|
@ -786,9 +733,6 @@ func compareCriuVersion(criuVersion int, minVersion int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// This is used to store the result of criu version RPC
|
||||
var criuVersionRPC *criurpc.CriuVersion
|
||||
|
||||
// checkCriuVersion checks Criu version greater than or equal to minVersion
|
||||
func (c *linuxContainer) checkCriuVersion(minVersion int) error {
|
||||
|
||||
|
@ -798,50 +742,13 @@ func (c *linuxContainer) checkCriuVersion(minVersion int) error {
|
|||
return compareCriuVersion(c.criuVersion, minVersion)
|
||||
}
|
||||
|
||||
// First try if this version of CRIU support the version RPC.
|
||||
// The CRIU version RPC was introduced with CRIU 3.0.
|
||||
|
||||
// First, reset the variable for the RPC answer to nil
|
||||
criuVersionRPC = nil
|
||||
|
||||
var t criurpc.CriuReqType
|
||||
t = criurpc.CriuReqType_VERSION
|
||||
req := &criurpc.CriuReq{
|
||||
Type: &t,
|
||||
}
|
||||
|
||||
err := c.criuSwrk(nil, req, nil, false, nil)
|
||||
criu := criu.MakeCriu()
|
||||
var err error
|
||||
c.criuVersion, err = criu.GetCriuVersion()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CRIU version check failed: %s", err)
|
||||
}
|
||||
|
||||
if criuVersionRPC != nil {
|
||||
logrus.Debugf("CRIU version: %s", criuVersionRPC)
|
||||
// major and minor are always set
|
||||
c.criuVersion = int(*criuVersionRPC.Major) * 10000
|
||||
c.criuVersion += int(*criuVersionRPC.Minor) * 100
|
||||
if criuVersionRPC.Sublevel != nil {
|
||||
c.criuVersion += int(*criuVersionRPC.Sublevel)
|
||||
}
|
||||
if criuVersionRPC.Gitid != nil {
|
||||
// runc's convention is that a CRIU git release is
|
||||
// always the same as increasing the minor by 1
|
||||
c.criuVersion -= (c.criuVersion % 100)
|
||||
c.criuVersion += 100
|
||||
}
|
||||
return compareCriuVersion(c.criuVersion, minVersion)
|
||||
}
|
||||
|
||||
// This is CRIU without the version RPC and therefore
|
||||
// older than 3.0. Parsing the output is required.
|
||||
|
||||
// This can be remove once runc does not work with criu older than 3.0
|
||||
|
||||
c.criuVersion, err = parseCriuVersion(c.criuPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return compareCriuVersion(c.criuVersion, minVersion)
|
||||
}
|
||||
|
||||
|
@ -918,8 +825,8 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
// support for doing unprivileged dumps, but the setup of
|
||||
// rootless containers might make this complicated.
|
||||
|
||||
// criu 1.5.2 => 10502
|
||||
if err := c.checkCriuVersion(10502); err != nil {
|
||||
// We are relying on the CRIU version RPC which was introduced with CRIU 3.0.0
|
||||
if err := c.checkCriuVersion(30000); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1027,10 +934,6 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
|
||||
// append optional manage cgroups mode
|
||||
if criuOpts.ManageCgroupsMode != 0 {
|
||||
// criu 1.7 => 10700
|
||||
if err := c.checkCriuVersion(10700); err != nil {
|
||||
return err
|
||||
}
|
||||
mode := criurpc.CriuCgMode(criuOpts.ManageCgroupsMode)
|
||||
rpcOpts.ManageCgroupsMode = &mode
|
||||
}
|
||||
|
@ -1250,8 +1153,8 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
// TODO(avagin): Figure out how to make this work nicely. CRIU doesn't have
|
||||
// support for unprivileged restore at the moment.
|
||||
|
||||
// criu 1.5.2 => 10502
|
||||
if err := c.checkCriuVersion(10502); err != nil {
|
||||
// We are relying on the CRIU version RPC which was introduced with CRIU 3.0.0
|
||||
if err := c.checkCriuVersion(30000); err != nil {
|
||||
return err
|
||||
}
|
||||
if criuOpts.WorkDirectory == "" {
|
||||
|
@ -1394,10 +1297,6 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
|
||||
// append optional manage cgroups mode
|
||||
if criuOpts.ManageCgroupsMode != 0 {
|
||||
// criu 1.7 => 10700
|
||||
if err := c.checkCriuVersion(10700); err != nil {
|
||||
return err
|
||||
}
|
||||
mode := criurpc.CriuCgMode(criuOpts.ManageCgroupsMode)
|
||||
req.Opts.ManageCgroupsMode = &mode
|
||||
}
|
||||
|
@ -1586,20 +1485,11 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
|
|||
}
|
||||
if !resp.GetSuccess() {
|
||||
typeString := req.GetType().String()
|
||||
if typeString == "VERSION" {
|
||||
// If the VERSION RPC fails this probably means that the CRIU
|
||||
// version is too old for this RPC. Just return 'nil'.
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("criu failed: type %s errno %d\nlog file: %s", typeString, resp.GetCrErrno(), logPath)
|
||||
}
|
||||
|
||||
t := resp.GetType()
|
||||
switch {
|
||||
case t == criurpc.CriuReqType_VERSION:
|
||||
logrus.Debugf("CRIU version: %s", resp)
|
||||
criuVersionRPC = resp.GetVersion()
|
||||
break
|
||||
case t == criurpc.CriuReqType_FEATURE_CHECK:
|
||||
logrus.Debugf("Feature check says: %s", resp)
|
||||
criuFeatures = resp.GetFeatures()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
test/test
|
||||
test/piggie
|
||||
test/phaul
|
||||
image
|
||||
rpc/rpc.proto
|
|
@ -0,0 +1,25 @@
|
|||
language: go
|
||||
sudo: required
|
||||
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
|
||||
- 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 build phaul test/test test/phaul test/piggie
|
||||
# Run actual test as root as it uses CRIU.
|
||||
- sudo make test phaul-test
|
|
@ -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 . 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
|
|
@ -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.
|
|
@ -0,0 +1,5 @@
|
|||
module github.com/checkpoint-restore/go-criu/v4
|
||||
|
||||
go 1.13
|
||||
|
||||
require github.com/golang/protobuf v1.3.5
|
|
@ -0,0 +1,20 @@
|
|||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
@ -0,0 +1,250 @@
|
|||
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
|
||||
}
|
||||
|
||||
// MakeCriu returns the Criu object required for most operations
|
||||
func MakeCriu() *Criu {
|
||||
return &Criu{}
|
||||
}
|
||||
|
||||
// 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("criu", 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
|
||||
}
|
|
@ -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
|
@ -1,6 +1,7 @@
|
|||
# github.com/checkpoint-restore/go-criu v0.0.0-20191125063657-fcdcd07065c5
|
||||
# github.com/checkpoint-restore/go-criu/v4 v4.0.2
|
||||
## explicit
|
||||
github.com/checkpoint-restore/go-criu/rpc
|
||||
github.com/checkpoint-restore/go-criu/v4
|
||||
github.com/checkpoint-restore/go-criu/v4/rpc
|
||||
# github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3
|
||||
## explicit
|
||||
github.com/cilium/ebpf
|
||||
|
|
Loading…
Reference in New Issue