| App::FuguVM::CLI(3p) | Perl Library Manual | App::FuguVM::CLI(3p) |
NAME
App::FuguVM::CLI - the subcommands of fuguvm
SYNOPSIS
use App::FuguVM::CLI;
exit App::FuguVM::CLI->run(@ARGV);
DESCRIPTION
The module holds the subcommand bodies of fuguvm(1). The dispatch, the option parsing, the usage and the generic exit codes come from Fugu::CLI.
Each subcommand declares its own options in one table beside its body, so no command repeats a "Getopt::Long" block. "_prepare" applies the global options and loads the project before a body runs, and "init" is the one command that runs without a project. The five lifecycle commands have no body of their own: their table entry names the App::FuguVM::Guest verb, and "run" calls it on the loaded VM.
Command data goes to standard output; a diagnostic goes to the logger, which writes to standard error. Thus "fuguvm snapshot list --names" stays readable in a pipeline when the tool has something to warn about.
THE STATUS REPORT
"fuguvm status" writes one "key: value" line for each key to standard output, in sorted key order. It writes the lines whatever "--quiet" says, because the lines are data. The key set is stable: every key appears on every run. A value can be empty, and an empty value means "not now". A line with an empty value ends with the colon and one space, so every line has one shape.
The keys are "accel", "arch", "bind_address", "console_port", "disk_exists", "installed", "name", "pid", "proxy_url", "ssh_port" and "state". "pid" and the two ports of an "auto" directive are empty for a stopped guest, and "proxy_url" is empty while the proxy does not run.
"fuguvm status <key>" writes the bare value of one key, with no key name and no colon. Thus a make target reads one port without a text filter. An unknown key gives exit code 2, and the diagnostic names every valid key.
"fuguvm disk info" shares the printer, so its report follows the same "key: value" contract on standard output.
THE FOUR CONNECTION VERBS
"ssh", "put", "get" and "console" each need a running guest. Each one checks first, and a guest that does not run gives exit code 1 with a message that names the VM. App::FuguVM::Remote holds the transport of the first three, and App::FuguVM::Console holds the attachment.
"fuguvm ssh" with no argument opens an interactive session, and it returns the exit code of ssh(1). With arguments it runs one remote command. Each argument is one word of the argument vector: the tool quotes every word, so the remote shell performs no splitting, no expansion, and no globbing. Write "--" before any word that starts with a hyphen, because the option parser reads such a word as an option of the tool and exits with code 2. A caller that wants a remote shell asks for one:
fuguvm ssh -- sh -c 'cd /src && make regress'
The remote command gets no terminal and no standard input, and its output arrives when the command ends. The verb returns the exit code of the remote command. A connect failure reads as exit code 1, with a diagnostic on standard error.
"fuguvm put [--mode=<octal>] <local> <remote>" copies a local file or a local directory into the guest, and "fuguvm get <remote> <local>" copies one guest file to the host. Each "<remote>" must be an absolute path. A bad argument count, a relative remote path, a "--mode" value that is not 3 or 4 octal digits, a "put" source that is neither a regular file nor a directory, and a "get" destination that is an existing directory each give exit code 2. A failed transfer gives exit code 1. fuguvm(1) holds the full transfer rules.
"fuguvm get" needs "Fugu::SSH->read_file". When the installed Fugu release has no such method, the verb logs the needed release and gives exit code 1.
"fuguvm console" attaches the terminal of the operator to the serial console of the guest, and it returns the exit code of telnet(1).
THE MIRROR COMMAND
"fuguvm mirror fetch <file>" downloads one file of the release of the invoked guest, verifies it through App::FuguVM::Mirror, stores it in the proxy cache, and writes the cached path to standard output, where a script can read it. The scope comes from the manifests: the tool uses the release scope when the manifest of the architecture directory names the file, and the source scope when the manifest of the version directory does. Both manifests are authoritative lists, so the tool guesses nothing, and it refuses a name that neither manifest holds with exit code 1.
"fuguvm mirror fetch ports.tar.gz" is the call that a consumer makes. The guest then reads a verified ports tree from the cache, over http, through the proxy.
"fuguvm mirror verify" verifies every cached file of the version and the architecture of the invoked guest. It logs one "scope/name" line for each file that failed, and one summary line with the three counts. It removes each file that failed, in its own scope only, because a file that fails a digest must not stay in a cache that a later run reads. A cached manifest pair that fails its own proof counts as failed too, and the removal covers it. The verb does not remove an "unknown" file: a name that no manifest holds is not a failure, and index.txt is such a name on every mirror. The verb exits 1 when one file failed, and 0 otherwise. It is idempotent: a second run over a clean cache removes nothing and exits 0. The verb always proves, whatever the "verify" directive says.
"mirror" with no action, with an unknown action, "mirror fetch" with no file, and "mirror verify" with an argument, each exit 2. An absent public key for the version, and a "signify_dir" that is not a directory, each exit 3. A failed download, a failed signature, and a failed digest each exit 1.
THE IMAGE EXPORT
"fuguvm image export <path> [--format=qcow2|raw]" writes the installed base image of the invoked VM as a full-disk image. The source is the base image of the cache entry that backs the working disk; a snapshot layer resolves to the same entry. A standalone disk, from "--no-cache" or "image_cache no", exports the working disk itself. The default format is "qcow2", and "raw" writes the sparse raw form.
The write goes through one temporary sibling, "<path>.tmp.<pid>", and one rename, so a failure leaves no partial file behind. The command refuses a running guest with exit code 5, because a live overlay is not consistent. It refuses an existing target with exit code 1, because it must not overwrite an image that an operator published, and it creates no directory for the operator.
The report follows the "key: value" contract of "status": "bytes", "format", "key" (empty for a standalone disk), "path" and "source", with the two paths absolute.
EXIT CODES
The generic codes come from Fugu::CLI: 0 success, 1 error, 2 invalid arguments, 3 configuration error, 7 timeout.
Code 1 also reports an "image export" whose target exists, whose parent directory is absent, or whose guest has no installed disk, and an import whose "base_disk" file is absent at a cache miss. The four connection verbs give it for a guest that does not run, and "put" and "get" give it for a failed transfer. A failed download, a failed signature, a failed digest, a "mirror verify" that found one bad file, and "verify yes" with no signify(1) on the host, each give it too.
Code 2 also reports an unknown "status" key, an "image" call with no action, no path, or an unknown "--format" value, a "mirror" call with no action, an unknown action, or no file, and each argument refusal of "put" and "get" that the section above lists.
Code 3 also reports an "arch" value that is neither "amd64" nor "arm64", and a QEMU binary of the architecture that is not on "PATH". An invalid "bind_address", "ssh_port", "console_port" or "qemu_version" value gives the same code. So does a QEMU version that does not match the pinned "qemu_version", a "signify_dir" that is not a directory, an absent public key for the version on a "mirror" call, and each refusal of the image-lifecycle directives that App::FuguVM::Config lists. An absent key on "up" reads as a failed image, with code 1.
These are the codes that mean something to a VM:
4 the named VM is not in the configuration
5 the VM runs and the command needs it stopped
9 an expect script failed, the install and autoinstall
scripts included
11 the named snapshot does not exist
Code 9 also reports the installer prompt "Continue without verification?" under "verify yes": the script stops on it, because the prompt means that the mirror data is wrong.
Code 11 is scriptable: a script that runs "snapshot restore || provision-from-scratch" can tell a missing layer from a real failure.
App::FuguVM::Guest defines the codes that it returns, because this module loads App::FuguVM::Guest and the reverse import would be a cycle.
METHODS
run
App::FuguVM::CLI->run(@argv)
Parse, dispatch and return an exit code. This is the whole of bin/fuguvm.
SEE ALSO
Fugu::CLI, App::FuguVM::Config, App::FuguVM::Guest, fuguvm(1)
AUTHOR
Dick Olsson <hi@senzilla.io>
| 2026-10-16 | OpenBSD |