proc

package
v0.4.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package proc is the fork backend. It runs one zygote per grant, linked with sdk/c/template/libfiberzygote, which forks fibers on request over a socketpair inherited as fd 3. CRIU checkpoints them. It is the reference backend that the conformance suite runs against.

The control socket carries lines. The zygote side is libfiberzygote (sdk/c/template). The host speaks first.

host   -> zygote HIDE <dir> | DROP <path> | RUNDIR <parent> <own>   (any number)
host   -> zygote PREPARE mntns|none                                 (ends the setup)
zygote -> host   READY
host   -> zygote CLONE <fence> <endpoint> <deadline_ms> <payload-hex|-> <session-hex|-> [opt,opt...]   + SCM_RIGHTS cgroup fd, workspace tree, channel
zygote -> host   CLONED <fence> <pid>  |  ERROR <fence> <text>
zygote -> host   EXITED <pid> exit:<n>|signal:<name>

RUNDIR names the directory all grants' run directories sit under, and this grant's own. A fiber with a mount namespace sees the parent covered and only its own directory bound back. PREPARE mntns has the zygote build that view once, in a mount namespace of its own, before it says READY, and every fiber's namespace is a copy of it. The zygote also drops every mount another fiberd home made, which it copied from the host at fz_init: a file system whose source starts with backend.MountSourcePrefix, and any bind of / (a restore root). fiberd need not name them, so a home needs no knowledge of other homes' paths, and DROP still names this backend's own restore root. The namespace itself is taken at fz_init, before any thread, when FIBERD_OWN_MNTNS is in the zygote's environment. A launcher's fibers get no mount namespace, so its zygote is started without the variable and sent PREPARE none and nothing else. The session is hex so a name may hold any character, and "-" for an anonymous fiber. CLONE options are any of pidns, mntns, nocaps and ws, comma-separated. With none the field is empty and the line ends in a space. The last descriptor on the line is the fiber's channel, which the host queued the fiber's incarnation record on before the line (backend.Incarnation). With ws the descriptor before it is the fiber's workspace, a detached copy of its root directory (open_tree), which the child mounts at backend.WorkspaceMount in its own namespace before anything else and the zygote closes once the child is forked. A plain fiber's namespace is a copy of the prepared one (mntns). A launcher's fiber sends ws without mntns, and its zygote gives the child a copy of the container's namespace at the birth. A child that cannot get every confinement it asked for ends before it runs, and the zygote sends ERROR. A zygote that could not prepare its namespace sends ERROR ? with the reason instead of READY and ends, and Warm fails with that reason. Every fiber of a plain proc grant asks for a mount namespace, so such a zygote is no warm template and the home must not report the grant ready on it.

Index

Constants

This section is empty.

Variables

View Source
var ErrZygote = errors.New("proc: zygote error")

Functions

func New

func New(o Options) backend.Backend

New opens the backend. It offers FIBER_CHECKPOINT with a criu of at least criu.MinVersion whose `criu check` passes, and FIBER_WARM otherwise.

Types

type Backend

type Backend struct {
	// contains filtered or unexported fields
}

Backend implements backend.Backend, backend.SelfCheckpointer and backend.DeltaCodec.

func NewBackend

func NewBackend(o Options) *Backend

NewBackend is New with the concrete type, for launchers that wrap it.

func (*Backend) CheckpointWarm

func (b *Backend) CheckpointWarm(ctx context.Context, warmID, dir string) error

CheckpointWarm implements backend.SelfCheckpointer: dump the zygote's pages while it keeps running, treating the control socket as external. A plain zygote lives in the mount namespace it prepared for its fibers, so its mounts are named the way a fiber's are at Park (the single-file mounts, and the run directory bind by name).

func (*Backend) Clone

func (b *Backend) Clone(ctx context.Context, warmID string, spec backend.FiberSpec) (backend.Fiber, error)

Clone makes the fiber's channel with its record queued, sends CLONE with the leaf cgroup fd and the fiber's end of the channel, and waits for CLONED. A handoff fiber's host end comes back in the Fiber. Any other fiber reads its record and replaces the channel with a pair of its own (libfiberzygote), so the backend closes its end here.

func (*Backend) Close

func (b *Backend) Close()

Close kills every zygote (and with them every fiber) and gives up the restore root.

func (*Backend) Compute

func (b *Backend) Compute(dir string, parent backend.Parent) (backend.DeltaInfo, error)

func (*Backend) EndpointSchemes

func (b *Backend) EndpointSchemes() []string

EndpointSchemes: a forked fiber binds whatever it is told, a unix socket or a tcp port in the home's network namespace; criu restores a listening tcp socket on its port like any other.

func (*Backend) Exits

func (b *Backend) Exits() <-chan backend.Exit

func (*Backend) Handoff

func (b *Backend) Handoff() bool

Handoff implements backend.Handoffer. libfiberzygote keeps a handoff fiber's channel at a fixed descriptor, and criu treats it as external.

func (*Backend) HasDelta

func (b *Backend) HasDelta(dir string) bool

func (*Backend) HoldsWorkspaces

func (b *Backend) HoldsWorkspaces() bool

HoldsWorkspaces implements backend.WorkspaceHolder. A plain fork backend's fibers have a mount namespace of their own to hold the workspace in, a copy of the zygote's prepared one. A launcher's fiber with a workspace gets one too, a copy of its container's, which the zygote makes at the birth. Its dump names every file the fiber still holds through the container's mounts, which the launcher lists.

func (*Backend) ImageBytes

func (b *Backend) ImageBytes(dir string) (uint64, error)

func (*Backend) Kill

func (b *Backend) Kill(fiberID string) error

Kill ends the fiber's root process; the exit is reported like any other. A fiber already reported gone is a no-op. One whose host pid is unknown is an error, never a signal, because kill(2) of 0 is this process group and a negative or guessed number is some other process.

func (*Backend) LoadParent

func (b *Backend) LoadParent(dir string) (backend.Parent, error)

func (*Backend) Merge

func (b *Backend) Merge(dir string, parent backend.Parent) error

func (*Backend) Name

func (b *Backend) Name() string

func (*Backend) Park

func (b *Backend) Park(ctx context.Context, fiberID string, spec backend.ParkSpec) error

Park asks the fiber to stop serving and dumps its tree with criu once it has said so. With Sync the tree keeps running (--leave-running), serving nothing, and the host ends it once the images are durable. With a Capture the tree is left stopped instead (--leave-stopped), the capture runs against it, and only then is it ended, or continued for a sync park or a failed capture. A fiber with a workspace has its bind named external, by the fixed key, at every dump.

func (*Backend) ReadDeltaInfo

func (b *Backend) ReadDeltaInfo(dir string) (backend.DeltaInfo, error)

func (*Backend) Resume

func (b *Backend) Resume(ctx context.Context, spec backend.ResumeSpec) (backend.Fiber, error)

Resume restores a checkpoint into the given cgroup; criu stays as the tree's parent and its exit is reported as the fiber's.

func (*Backend) Tier

func (b *Backend) Tier() core.Tier

func (*Backend) Unwarm

func (b *Backend) Unwarm(id string)

func (*Backend) Warm

func (b *Backend) Warm(ctx context.Context, spec backend.WarmSpec) (backend.Warm, error)

Warm execs the zygote inside the given cgroup with the control socketpair as fd 3 and waits for READY.

type Dumped

type Dumped struct {
	// PID is the tree's root as the host sees it.
	PID int
	// Zygote is the warm instance's process as the host sees it, the
	// container's init. It is PID itself when the zygote is dumped.
	Zygote int
	// Workspace says the tree is a fiber with a mount namespace of its
	// own, a copy of the zygote's, that holds its workspace at
	// backend.WorkspaceMount.
	Workspace bool
}

Dumped is the tree a launcher's DumpExtra names criu arguments for.

type Inherit

type Inherit struct {
	Key  string
	File *os.File
}

Inherit is a file criu hands the restored tree in place of the one its images name by Key, as --inherit-fd fd[N]:<Key>.

type Launcher

type Launcher interface {
	// Name is the backend name this launcher gives the fork backend.
	Name() string
	// Command builds the command that starts argv as the warm instance
	// for spec with ctl as its fd 3, logging to logf. It is exec'd by the
	// backend inside spec's cgroup.
	Command(spec backend.WarmSpec, argv []string, ctl, logf *os.File) (*exec.Cmd, error)
	// Channel is the control channel to speak to the started instance
	// on, given boot, the host's end of the pair Command was handed.
	// A launcher whose instance lives in a network namespace of its own
	// makes a pair there and hands the instance its end over boot, so the
	// instance can be checkpointed holding it. Returning boot keeps it.
	Channel(ctx context.Context, spec backend.WarmSpec, cmd *exec.Cmd, boot *net.UnixConn) (*net.UnixConn, error)
	// Socketpair makes a unix pair a fiber forked from the instance can
	// be checkpointed with, its channel. pid is the instance as the host
	// sees it.
	Socketpair(spec backend.WarmSpec, pid int, typ int) ([2]int, error)
	// PID is the zygote process itself (the container's init) once
	// Command has started, as the host sees it.
	PID(ctx context.Context, spec backend.WarmSpec, cmd *exec.Cmd) (int, error)
	// Restored is told of every tree of spec's grant criu brought back,
	// rooted at pid as the host sees it, for what the restored
	// namespaces still need.
	Restored(spec backend.WarmSpec, pid int) error
	// RestoredGone is told once for every RestoreExtra, when the restore
	// failed or when the restored tree has exited, so what RestoreExtra
	// made for it on this home can go.
	RestoredGone(spec backend.WarmSpec)
	// Release ends whatever Command created besides the process.
	Release(spec backend.WarmSpec)
	// Endpoint maps a host path in spec.WorkDir to where the zygote sees
	// it (the run directory is bind-mounted into its namespace).
	Endpoint(spec backend.WarmSpec, hostPath string) string
	// DumpExtra and RestoreExtra are criu arguments for a tree that
	// lives inside the launcher's namespaces. Both are given the image
	// directory. DumpExtra is told which tree is dumped and may record
	// beside the images what its restore will need to know. RestoreExtra
	// may have to make what the restore needs on this home first (a root
	// filesystem for a grant it never warmed, images the restored tree's
	// own identity may read), and may hand the tree files of this home
	// in place of ones its images name. The backend numbers those and
	// closes them once criu has them.
	DumpExtra(spec backend.WarmSpec, dir string, d Dumped) ([]string, error)
	RestoreExtra(spec backend.WarmSpec, dir string) ([]string, []Inherit, error)
}

Launcher starts the zygote for a Backend in an environment of its own.

type Options

type Options struct {
	// CRIU names the criu binary (default "criu").
	CRIU string
	// CRIUWrap is a command `criu restore` runs through, with criu's
	// argv after its own (criu.Options.Wrap). A launcher whose restores
	// need mounts of their own sets it.
	CRIUWrap []string
	// Launcher, when set, starts the zygote some other way than a plain
	// exec: inside an OCI container (pkg/backend/runc). Fibers are still
	// forked by the zygote over the control socket, checkpointed with
	// criu and computed as deltas; the launcher supplies what criu needs
	// to dump and restore trees that live in its namespaces.
	Launcher Launcher
	// RootBind is where the host's / is bind-mounted as the root of a
	// restored fiber's mount namespace, on the first resume that needs it
	// (default fiberd-root-<pid> under the temp directory). Every later
	// fiber unmounts it from its own namespace, because the bind would show
	// what HIDE covers, and a zygote of any other home drops it as a bind
	// of /. The backend makes the leaf directory and removes it
	// at Close, so its parent must be a place only fiberd writes. It
	// refuses a symlink or another user's directory there, and unmounts a
	// stale bind at the same path when it opens.
	RootBind string
	// Commander runs criu (criu.Options.Commander). Nil runs the binary
	// CRIU names.
	Commander criu.Commander
	// Borrow takes a duplicate of descriptor fd of process pid, as
	// pidfd_getfd(2) does, which a park and a resume use to reach the
	// fiber's channel. Nil is that system call, and a test sets one that
	// hands out ends of pairs of its own.
	Borrow func(pid, fd int) (*os.File, error)
	// Signal sends sig to process pid, which a park uses to ask the
	// fiber to stop serving. Nil is kill(2), and a test sets one that
	// records the signal.
	Signal func(pid int, sig syscall.Signal) error
	// contains filtered or unexported fields
}

Options configure the fork backend.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL