[CRIU] [PATCH 1/2] lib: Add simple Go wrappers for swrk mode
Pavel Emelyanov
xemul at virtuozzo.com
Mon Dec 19 02:16:29 PST 2016
We'll need some docs :) bu the API is
criu := MakeCriu()
criu.Dump(opts, notify)
criu.Restore(opts, notify)
criu.PreDump(opts, notify)
criu.StartPageServer(opts)
where opts is the object from rpc.proto, Go has almost native support
for those, so caller should
- compile .proto file
- export it and golang/protobuf/proto
- create and initialize the CriuOpts struct
and notify is an interface with callbacks that correspond to criu
notification messages.
A stupid dump/restore tool in src/test/main.go demonstrates the above.
Nearest TODO:
- docs
- code comments
- req.keep_open support for pre-dumps
- resp.err_str support
Signed-off-by: Pavel Emelyanov <xemul at virtuozzo.com>
---
lib/go/.gitignore | 1 +
lib/go/Makefile | 8 +++
lib/go/src/criu/main.go | 177 ++++++++++++++++++++++++++++++++++++++++++++++
lib/go/src/criu/notify.go | 52 ++++++++++++++
lib/go/src/test/main.go | 78 ++++++++++++++++++++
5 files changed, 316 insertions(+)
create mode 100644 lib/go/.gitignore
create mode 100644 lib/go/Makefile
create mode 100644 lib/go/src/criu/main.go
create mode 100644 lib/go/src/criu/notify.go
create mode 100644 lib/go/src/test/main.go
diff --git a/lib/go/.gitignore b/lib/go/.gitignore
new file mode 100644
index 0000000..8cab569
--- /dev/null
+++ b/lib/go/.gitignore
@@ -0,0 +1 @@
+src/rpc/rpc.pb.go
diff --git a/lib/go/Makefile b/lib/go/Makefile
new file mode 100644
index 0000000..c8551d6
--- /dev/null
+++ b/lib/go/Makefile
@@ -0,0 +1,8 @@
+all: test
+
+test: rpc
+ GOPATH=$(shell pwd):/usr/share/gocode go build -o test test
+
+rpc:
+ mkdir -p src/rpc/
+ protoc --go_out=src/rpc/ --proto_path=../../images/ ../../images/rpc.proto
diff --git a/lib/go/src/criu/main.go b/lib/go/src/criu/main.go
new file mode 100644
index 0000000..3b208a3
--- /dev/null
+++ b/lib/go/src/criu/main.go
@@ -0,0 +1,177 @@
+package criu
+
+import (
+ "errors"
+ "github.com/golang/protobuf/proto"
+ "os"
+ "os/exec"
+ "rpc"
+ "strconv"
+ "syscall"
+)
+
+type Criu struct {
+ swrk_cmd *exec.Cmd
+ swrk_sk *os.File
+}
+
+func MakeCriu() *Criu {
+ return &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")
+
+ args := []string{"swrk", strconv.Itoa(fds[1])}
+ cmd := exec.Command("criu", args...)
+
+ err = cmd.Start()
+ if err != nil {
+ cln.Close()
+ srv.Close()
+ return err
+ }
+
+ srv.Close()
+
+ c.swrk_cmd = cmd
+ c.swrk_sk = cln
+
+ return nil
+}
+
+func (c *Criu) cleanup() {
+ if c.swrk_cmd != nil {
+ c.swrk_sk.Close()
+ c.swrk_sk = nil
+ c.swrk_cmd.Wait()
+ c.swrk_cmd = nil
+ }
+}
+
+func (c *Criu) sendAndRecv(req_b []byte) ([]byte, int, error) {
+ cln := c.swrk_sk
+ _, err := cln.Write(req_b)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ resp_b := make([]byte, 2*4096)
+ n, err := cln.Read(resp_b)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return resp_b, n, nil
+}
+
+func (c *Criu) doSwrk(req_type rpc.CriuReqType, opts *rpc.CriuOpts, nfy CriuNotify) error {
+ req := rpc.CriuReq{
+ Type: &req_type,
+ Opts: opts,
+ }
+
+ if nfy != nil {
+ opts.NotifyScripts = proto.Bool(true)
+ }
+
+ if c.swrk_cmd == nil {
+ err := c.prepare()
+ if err != nil {
+ return err
+ }
+
+ defer c.cleanup()
+ }
+
+ for {
+ req_b, err := proto.Marshal(&req)
+ if err != nil {
+ return err
+ }
+
+ resp_b, resp_s, err := c.sendAndRecv(req_b)
+ if err != nil {
+ return err
+ }
+
+ resp := &rpc.CriuResp{}
+ err = proto.Unmarshal(resp_b[:resp_s], resp)
+ if err != nil {
+ return err
+ }
+
+ if !resp.GetSuccess() {
+ return errors.New("operation failed")
+ }
+
+ resp_type := resp.GetType()
+ if resp_type == req_type {
+ break
+ }
+ if resp_type != rpc.CriuReqType_NOTIFY {
+ return errors.New("unexpected responce")
+ }
+ if nfy == nil {
+ return 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 err
+ }
+
+ req = rpc.CriuReq{
+ Type: &resp_type,
+ NotifySuccess: proto.Bool(true),
+ }
+ }
+
+ return nil
+}
+
+func (c *Criu) Dump(opts rpc.CriuOpts, nfy CriuNotify) error {
+ return c.doSwrk(rpc.CriuReqType_DUMP, &opts, nfy)
+}
+
+func (c *Criu) Restore(opts rpc.CriuOpts, nfy CriuNotify) error {
+ return c.doSwrk(rpc.CriuReqType_RESTORE, &opts, nfy)
+}
+
+func (c *Criu) PreDump(opts rpc.CriuOpts, nfy CriuNotify) error {
+ return c.doSwrk(rpc.CriuReqType_PRE_DUMP, &opts, nfy)
+}
+
+func (c *Criu) StartPageServer(opts rpc.CriuOpts) error {
+ return c.doSwrk(rpc.CriuReqType_PAGE_SERVER, &opts, nil)
+}
diff --git a/lib/go/src/criu/notify.go b/lib/go/src/criu/notify.go
new file mode 100644
index 0000000..8ce16b7
--- /dev/null
+++ b/lib/go/src/criu/notify.go
@@ -0,0 +1,52 @@
+package criu
+
+type CriuNotify interface {
+ PreDump() error
+ PostDump() error
+ PreRestore() error
+ PostRestore(pid int32) error
+ NetworkLock() error
+ NetworkUnlock() error
+ SetupNamespaces(pid int32) error
+ PostSetupNamespaces() error
+ PostResume() error
+}
+
+type CriuNoNotify struct {
+}
+
+func (c CriuNoNotify) PreDump() error {
+ return nil
+}
+
+func (c CriuNoNotify) PostDump() error {
+ return nil
+}
+
+func (c CriuNoNotify) PreRestore() error {
+ return nil
+}
+
+func (c CriuNoNotify) PostRestore(pid int32) error {
+ return nil
+}
+
+func (c CriuNoNotify) NetworkLock() error {
+ return nil
+}
+
+func (c CriuNoNotify) NetworkUnlock() error {
+ return nil
+}
+
+func (c CriuNoNotify) SetupNamespaces(pid int32) error {
+ return nil
+}
+
+func (c CriuNoNotify) PostSetupNamespaces() error {
+ return nil
+}
+
+func (c CriuNoNotify) PostResume() error {
+ return nil
+}
diff --git a/lib/go/src/test/main.go b/lib/go/src/test/main.go
new file mode 100644
index 0000000..758d21e
--- /dev/null
+++ b/lib/go/src/test/main.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+ "criu"
+ "fmt"
+ "github.com/golang/protobuf/proto"
+ "os"
+ "rpc"
+ "strconv"
+)
+
+type TestNfy struct {
+ criu.CriuNoNotify
+}
+
+func (c TestNfy) PreDump() error {
+ fmt.Printf("TEST PRE DUMP\n")
+ return nil
+}
+
+func main() {
+ c := criu.MakeCriu()
+
+ act := os.Args[1]
+ switch act {
+ case "dump":
+ fmt.Printf("Dumping\n")
+ pid, _ := strconv.Atoi(os.Args[2])
+ img, err := os.Open(os.Args[3])
+ if err != nil {
+ fmt.Printf("can't open image dir")
+ os.Exit(1)
+ }
+ defer img.Close()
+
+ opts := rpc.CriuOpts{
+ Pid: proto.Int32(int32(pid)),
+ ImagesDirFd: proto.Int32(int32(img.Fd())),
+ LogLevel: proto.Int32(4),
+ LogFile: proto.String("dump.log"),
+ }
+
+ err = c.Dump(opts, TestNfy{})
+ if err != nil {
+ fmt.Printf("Error:")
+ fmt.Print(err)
+ fmt.Printf("\n")
+ os.Exit(1)
+ }
+ case "restore":
+ fmt.Printf("Restoring\n")
+ img, err := os.Open(os.Args[2])
+ if err != nil {
+ fmt.Printf("can't open image dir")
+ os.Exit(1)
+ }
+ defer img.Close()
+
+ opts := rpc.CriuOpts{
+ ImagesDirFd: proto.Int32(int32(img.Fd())),
+ LogLevel: proto.Int32(4),
+ LogFile: proto.String("dump.log"),
+ }
+
+ err = c.Restore(opts, nil)
+ if err != nil {
+ fmt.Printf("Error:")
+ fmt.Print(err)
+ fmt.Printf("\n")
+ os.Exit(1)
+ }
+ default:
+ fmt.Printf("unknown action\n")
+ os.Exit(1)
+ }
+
+ fmt.Printf("Success\n")
+}
--
2.5.0
More information about the CRIU
mailing list