[CRIU] [PATCH 1/2] lib: Add simple Go wrappers for swrk mode

Pavel Emelyanov xemul at virtuozzo.com
Wed Jan 25 08:19:02 PST 2017


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.

Changes since v1:

* Added keep_open mode for pre-dumps. Do use it one needs
  to call criu.Prepare() right after creation and criu.Cleanup()
  right after .Dump()

* Report resp.cr_errmsg string on request error.


Further TODO:

- docs
- code comments

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   | 114 +++++++++++++++++++++++++++++
 5 files changed, 352 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..90866ea
--- /dev/null
+++ b/lib/go/src/criu/main.go
@@ -0,0 +1,177 @@
+package criu
+
+import (
+	"errors"
+	"fmt"
+	"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")
+	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.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 fmt.Errorf("operation failed (msg:%s err:%d)",
+				resp.GetCrErrmsg(), resp.GetCrErrno())
+		}
+
+		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..ae38bef
--- /dev/null
+++ b/lib/go/src/test/main.go
@@ -0,0 +1,114 @@
+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 doDump(c *criu.Criu, pid_s string, img_dir string, pre bool, prev_img string) error {
+	fmt.Printf("Dumping\n")
+	pid, _ := strconv.Atoi(pid_s)
+	img, err := os.Open(img_dir)
+	if err != nil {
+		return fmt.Errorf("can't open image dir (%s)", err)
+	}
+	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"),
+	}
+
+	if prev_img != "" {
+		opts.ParentImg = proto.String(prev_img)
+		opts.TrackMem = proto.Bool(true)
+	}
+
+	if pre {
+		err = c.PreDump(opts, TestNfy{})
+	} else {
+		err = c.Dump(opts, TestNfy{})
+	}
+	if err != nil {
+		return fmt.Errorf("dump fail (%s)", err)
+	}
+
+	return nil
+}
+
+// Usage: test $act $pid $images_dir
+func main() {
+	c := criu.MakeCriu()
+	act := os.Args[1]
+	switch act {
+	case "dump":
+		err := doDump(c, os.Args[2], os.Args[3], false, "")
+		if err != nil {
+			fmt.Print(err)
+			os.Exit(1)
+		}
+	case "dump2":
+		err := c.Prepare()
+		if err != nil {
+			fmt.Print(err)
+			os.Exit(1)
+		}
+
+		err = doDump(c, os.Args[2], os.Args[3]+"/pre", true, "")
+		if err != nil {
+			fmt.Printf("pre-dump failed")
+			fmt.Print(err)
+			os.Exit(1)
+		}
+		err = doDump(c, os.Args[2], os.Args[3], false, "./pre")
+		if err != nil {
+			fmt.Printf("dump failed")
+			fmt.Print(err)
+			os.Exit(1)
+		}
+
+		c.Cleanup()
+	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("restore.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