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

Andrei Vagin avagin at virtuozzo.com
Mon Jan 9 23:49:40 PST 2017


Looks good. Two minore comments are inline.

On Mon, Dec 19, 2016 at 01:16:29PM +0300, Pavel Emelyanov wrote:
> 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")
	defer srv.Close()
> +
> +	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"),
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