FuguVM

App::FuguVM::State(3p) Perl Library Manual App::FuguVM::State(3p)

App::FuguVM::State - what FuguVM remembers about one VM between runs

    use App::FuguVM::State;
    my $state = App::FuguVM::State->new($state_dir, 'default')
        or die "cannot open the state of 'default'\n";
    $state->mark_running;
    if ($state->was_unclean_shutdown) {
        check_the_disk();
    }

Each VM has a directory under the state directory:

    <state_dir>/<vm>/status           the JSON state, mode 0600
    <state_dir>/<vm>/vm.pid           the QEMU process
    <state_dir>/<vm>/proxy.pid        the proxy child
    <state_dir>/<vm>/autoinstall.pid  the responder child
    <state_dir>/<vm>/disk.qcow2       the working disk

The JSON blob rides on Fugu::StateFile. The two process IDs ride on Fugu::Pidfile, which takes its lock before it truncates and reaps a zombie before it answers "running". A QEMU that became a zombie therefore reads as stopped, which is what a caller that is about to start a second one needs to know.

The module is persistence only. It starts nothing and stops nothing: the proxy lifecycle belongs to App::FuguVM::Guest. That split is what removed the "require" cycle between the two modules.

    App::FuguVM::State->new($state_dir, $vm_name)

Open the state of one VM, and create its directory when it is absent.

The VM name becomes a directory name, so it must be one safe path component: no separator, no NUL byte, and not one of the two directory entries. A name that is not returns "undef" and a message in the log. The method also returns "undef" when the directory is a symlink, is not a directory, or cannot be created.

Read and write the JSON state. "new" loads. A corrupt file gives empty state and does not stop the program that would rewrite it.

"store" returns the Fugu::StateFile. App::FuguVM::Guest gives it to the proxy, which keeps its port there.

"state_dir" returns the directory that holds every VM's state, and "vm_state_dir" the one that holds this VM's. "data" returns the state hash reference for a caller that changes several keys at once.

Return the Fugu::Pidfile objects. QEMU writes the first itself, through its "-pidfile" option; the proxy supervisor owns the second, and the autoinstall responder supervisor owns the third. Each file lives in the state directory of the guest, so an interrupted run leaves no child that nothing owns.

The QEMU process. QEMU writes the pid file itself, through its "-pidfile" option, so there is no setter.

The working disk of the VM.

The runtime record: the facts of one run of one guest. The record holds "accel" and the resolved "ssh_port" and "console_port". App::FuguVM::Guest writes it before it spawns QEMU, and reads it back for "status" and for the guest connections.

"get_runtime" returns the facts as a hash reference, and an empty hash reference when the guest never ran. The stop verbs call "clear_runtime" beside "clear_vm_pid", so the record dies with the run. "destroy" needs no call, because it empties the whole store.

Whether OpenBSD is installed on the disk. An installed system boots its own disk and never attaches the miniroot again.

"mark_installed" takes the architecture of the installed disk. The argument is required, so no path can forget it. "get_installed_arch" returns the recorded architecture, or "undef". App::FuguVM::Guest compares it with the configured value before it boots an existing disk.

The password that the installer set. The store writes at mode 0600.

Which SSH public key the guest holds. The state records the key itself, not only that there is one. Thus a key that changed in the configuration is installed again on the next "fuguvm up".

Whether the last stop was clean. "was_unclean_shutdown" also reports true when the state says running and the process is gone: the VM crashed or was killed, and the disk may need a check.

Fugu::Pidfile, Fugu::StateFile, App::FuguVM::Guest

Dick Olsson <hi@senzilla.io>

2026-10-16 OpenBSD