package phaul import ( "errors" "fmt" "github.com/checkpoint-restore/go-criu/v8" "github.com/checkpoint-restore/go-criu/v8/crit" "github.com/checkpoint-restore/go-criu/v8/crit/images/stats" proto "github.com/checkpoint-restore/go-criu/v8/internal/proto" "github.com/checkpoint-restore/go-criu/v8/rpc" ) const ( minPagesWritten uint64 = 64 maxIters int = 8 maxGrowDelta int64 = 32 ) // Client struct type Client struct { local Local remote Remote cfg Config } // MakePhaulClient function // Main entry point. Caller should create the client object by // passing here local, remote and comm. See comment in corresponding // interfaces/structs for explanation. // // Then call client.Migrate() and enjoy :) func MakePhaulClient(l Local, r Remote, c Config) (*Client, error) { return &Client{local: l, remote: r, cfg: c}, nil } func isLastIter(iter int, stats *stats.DumpStatsEntry, prevStats *stats.DumpStatsEntry) bool { if iter >= maxIters { fmt.Printf("`- max iters reached\n") return true } pagesWritten := stats.GetPagesWritten() if pagesWritten < minPagesWritten { fmt.Printf("`- tiny pre-dump (%d) reached\n", int(pagesWritten)) return true } pagesDelta := int64(pagesWritten) - int64(prevStats.GetPagesWritten()) if pagesDelta >= maxGrowDelta { fmt.Printf("`- grow iter (%d) reached\n", int(pagesDelta)) return true } return false } // Migrate function func (pc *Client) Migrate() (retErr error) { criu := criu.MakeCriu() psi := rpc.CriuPageServerInfo{ Fd: proto.Ptr(int32(pc.cfg.Memfd)), } opts := &rpc.CriuOpts{ Pid: proto.Ptr(pc.cfg.Pid), LogLevel: proto.Ptr[int32](4), LogFile: proto.Ptr("pre-dump.log"), Ps: &psi, } err := criu.Prepare() if err != nil { return err } defer func() { // append any cleanup errors to the returned error err := criu.Cleanup() if err != nil { retErr = errors.Join(retErr, err) } }() imgs, err := preparePhaulImages(pc.cfg.Wdir) if err != nil { return err } prevStats := &stats.DumpStatsEntry{} iter := 0 for { err = pc.remote.StartIter() if err != nil { return err } prevP := imgs.lastImagesDir() imgDir, err := imgs.openNextDir() if err != nil { return err } opts.ImagesDirFd = proto.Ptr(int32(imgDir.Fd())) if prevP != "" { opts.ParentImg = proto.Ptr(prevP) } err = criu.PreDump(opts, nil) _ = imgDir.Close() if err != nil { return err } iter++ err = pc.remote.StopIter() if err != nil { return err } // Get dump statistics with crit stats, err := crit.GetDumpStats(imgDir.Name()) if err != nil { return err } if isLastIter(iter, stats, prevStats) { break } prevStats = stats } err = pc.remote.StartIter() if err == nil { prevP := imgs.lastImagesDir() err = pc.local.DumpCopyRestore(criu, pc.cfg, prevP) err2 := pc.remote.StopIter() if err == nil { err = err2 } } return err }