[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