OS installation scripting over agentless QEMU guest automation
Project description
Reliquary
Reliquary helps to automate guest VMs, it can script OS installations from standard vendor installation media and produce bootable disk images without manual interaction. It can help execute one-off commands and capture the results. It is built on its own agentless QEMU guest automation layer, which owns QEMU lifecycle, media, QMP identity checks, keyboard input, screen access, screenshots, and per-run state.
Reliquary machines are ephemeral: disposable rigs for scripted installs and automated guest tasks, cheap to destroy and recreate. The machine is never the product — often nothing durable comes out at all (the point was to run some tests). Reliquary is not a VM manager for machines you keep.
When to use Packer, Vagrant, os-autoinst, or Reliquary
For modern, standard operating-system testing in VMs, start with Packer and Vagrant. Packer is the established tool for defining source-controlled image builds from install media, provisioners, and checksummed inputs, then producing reusable VM or cloud artifacts. Vagrant is the established tool for source-controlled development and test environments: bring a VM up from a box, sync project files, run provisioners, execute guest commands over SSH or WinRM, collect normal test output, and destroy or recreate the environment when needed.
Reliquary is for the cases where that cooperative guest channel is not available, not trustworthy, or is itself the thing being created or tested. It drives a guest through the VM's observable console: keyboard events in, screen text and screenshots out, with media changes and run records captured by the host. That makes it useful for text-mode installers, legacy systems such as DOS, broken or partially configured guests, boot menus, setup flows before SSH/WinRM/guest tools exist, and tests where the screen or installer behavior is the assertion surface.
If your guest can already accept SSH, WinRM, cloud-init, a guest agent, or a normal configuration-management/provisioning path, Packer and Vagrant are usually the better default. Use Reliquary when the important part of the workflow lives before that point, below it, or outside it.
os-autoinst, the engine under openQA, covers much of that console-driven ground at production scale: booting systems under test, driving installers and applications, matching screens with needles, checking serial and screen output, and recording job artifacts. If you need openQA's scheduler, worker farm, web UI, asset management, and job history around that engine, use openQA. Reliquary's overlap is with os-autoinst itself, not with openQA as a service.
Reliquary lives in the smaller local-tool and embedding-library space. It is meant to be run directly from a source tree or a user's machine, as a QEMU automation harness a script, test runner, CI job, or coding agent can call without adopting a scheduler, worker farm, web service, or image-needle workflow. Its current strongest case is agentless text-mode automation — especially DOS and other guests where VGA text, keyboard input, virtual FAT media, and compact run records are enough and a larger OS-testing service would be more machinery than the job needs. Its planned authoring niche is local demonstration-driven drafting: do the task once, then turn the observed keystrokes, media changes, and screen states into ordinary script and asset files that can be reviewed and edited.
Blueprints and machines
The first thing to learn is Reliquary's central model. A blueprint is a reusable JSON design you author and keep —
a <name>.rlqb file, conventionally under <reliquary_home>/blueprints/. A machine is a disposable realization Reliquary builds from it,
identified by a generated id — one blueprint, many machines. The blueprint is one file holding named components — a
machine plus the media, source, and archive components it draws on. Machines are created, run, destroyed, and
recreated freely: the blueprint (media components and all) and its scripts are always enough to rebuild one, so nothing
Reliquary materializes is ever precious. Editing a blueprint never changes an existing machine by itself; a machine
keeps the snapshot it was created from. To adopt blueprint edits, destroy the machine and create it again.
A blueprint, and the machines it makes
Here is a whole blueprint — an MS-DOS-era box with 1 MB of memory
and one floppy drive, saved as blueprints/msdos.rlqb:
[
{
"type": "machine",
"name": "msdos",
"platform": "dos",
"memory": "1M",
"description": "A 1 MB MS-DOS box with one floppy drive",
"drives": {
// Declared but empty: what goes in is a per-machine choice.
"floppy0": null
},
"boot": ["floppy0"]
}
]
Create two machines from it and they are identical, differing only in id — numbered from zero, lowest free number first:
rlq create-machine --blueprint msdos # prints msdos-0
rlq create-machine --blueprint msdos # prints msdos-1
Now put a disk in one of them. insert-media changes that machine
and nothing else — it survives stop and start, and msdos-0 never
hears about it:
rlq insert-media --machine msdos-1 floppy0 dos-622-disk1
That is the split worth remembering: the blueprint is the design, and a machine's own state is what has happened to it since. Memory, drive slots and boot order come from the blueprint, so changing them means editing the file. What is in a drive is the machine's.
A different design is a different blueprint. This one has sixteen
times the memory and a CD-ROM, so it is blueprints/dos-cd.rlqb
rather than a variant of the first:
[
{
"type": "machine",
"name": "dos-cd",
"platform": "dos",
"memory": "16M",
"description": "A 16 MB DOS box that boots from CD",
"drives": {
"hdd0": { "type": "media", "size": "20M" },
"cdrom0": null
},
"boot": ["cdrom0", "hdd0"]
}
]
Its machines are dos-cd-0, dos-cd-1, and so on — the id always
names the blueprint it came from.
Editing a blueprint does not reach back into machines already built
from it; each keeps the snapshot it was created from. rlq
apply-blueprint --machine msdos-0 adopts the edits into a stopped
machine, absorbing what it can — memory, boot order, CPU count — and
failing closed where it cannot, such as a changed size on an image
that has already been materialized. rlq recreate-machine is the
answer when it refuses: destroy and rebuild under the same id.
Read the Blueprint guide for the format, the CLI reference for commands, and the API reference for the Python surface. The normative definitions are the composed blueprint model and Machine blueprints and machines.
Installation
Windows is the supported host. It is the platform Reliquary is developed and tested on. The host code is written portably and the paths for macOS and Linux hosts are there, but they are not exercised yet — so they are not claimed. (The guest side is a separate question: what a machine runs inside is up to its blueprint.)
py -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e .
On an untested host (macOS or Linux), activate the environment with:
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e .
Scripting OS installations
Installing registers two equivalent commands: rlq (the short form used throughout the docs) and reliquary.
rlq --help
rlq run-script install --blueprint freedos
From a clean home, that one command materializes a machine from the codex's freedos blueprint (seeding the
blueprint — its LiveCD media rides inside it — and its scripts into your home as ordinary user-owned files), inserts the
fetched, hash-verified LiveCD to the blueprint's empty CD drive, boots it, and drives the FreeDOS installer end to end —
language, partitioning, the reboot, the "Plain DOS system" package set — until the guest powers itself off and the
script ejects the CD. The machine is left with FreeDOS installed on its hard disk; confirm it boots, then start and stop
it freely:
rlq run-script verify --blueprint freedos
rlq start-machine --blueprint freedos
rlq stop-machine --blueprint freedos
To inspect a script without running it, rlq check-script prints
the resolved timing plan — each observation's timeout, each guest
input's pacing, and where each came from:
rlq check-script freedos-install
rlq check-script install --blueprint freedos
Vendor media is cached and verified against pinned SHA-256 hashes on every use, under cache/media/
(Documents\reliquary by default; override the payload cache with --media-dir, or move everything
at once with --home-dir or the RELIQUARY_HOME_DIR environment variable).
A run returns its output and stores nothing: it streams its progress live — --progress pretty for a person,
--progress jsonl for a program — and the stream is gone when the run ends. Redirect it if you want to keep it. When a
run fails, the report names what it was waiting for, which clock expired, the route it took, the screen row that came
nearest, a screenshot, and the command to try next. Pass --display to show the QEMU window instead of running
headless — helpful when debugging a script.
The machine layer
Beneath the scripts, Reliquary is a general automation harness for running remote tasks in QEMU guests, usable on its own through the CLI and Python interfaces documented below.
DOS is the default and currently the only complete platform workflow. It boots a DOS guest, types at its keyboard, reads
its VGA text screen, runs commands, takes screenshots, and retrieves files written by the guest. Other platform names
reserve the generic QEMU lifecycle but their provisioning and guest-task semantics currently raise
NotImplementedError until an adapter is implemented.
The platform model
A blueprint names its guest platform in the required platform field;
new-blueprint scaffolds dos by default. DOS is the only complete
workflow today — other platform names reserve the generic QEMU
lifecycle but raise NotImplementedError for provisioning and
guest-task semantics until an adapter is implemented. Platform-specific
behavior is never inferred from an image. Future adapters can define how
a guest is provisioned, how a remote task is launched, and how its
result is collected while reusing the same ownership-verified QEMU
machine layer.
Why the DOS adapter exists
Automating a modern virtual machine usually means installing a guest agent, opening a network connection, or reading a serial console. Those options are often unavailable in DOS, and they are especially unsuitable when the software under test is the driver that would provide that communication.
Reliquary therefore works agentlessly:
- Input is sent as keyboard events through QEMU's control protocol.
- Text is read directly from VGA text memory, without OCR.
- Files are exchanged through a QEMU virtual FAT drive.
- Command completion is detected by watching for the DOS prompt.
- Screenshots are captured through QEMU.
The guest needs no Reliquary software, network driver, serial driver, or background service. This makes the harness useful even while the guest is partially configured or broken.
Any DOS with a bootable image works. A machine's drives each name a
media component, and the media owns its content — a blank
materialize: new disk of size, a difference/copy over a
payload, or a use attach (an ISO, or a host directory served as a
virtual FAT drive) — and Reliquary materializes any per-machine image
under cache/machines/<id>/media/. Any QEMU-supported image format
works; *.img and *.iso are taken as raw. Reliquary hands back a
guest program's raw output, and interpreting it is left to the caller.
The workflow
- Describe the machine. Author a
<name>.rlqbblueprint (by hand, withrlq new-blueprint, or by seeding one from the codex) declaring the platform, memory, and drives; each drive names a media component, and the media owns how it materializes — a blanknewdisk, adifference/copyover a payload, or auseattach. To hand the guest its own files, a media whosesourceis a host directory (materialize: use) is the natural surface: that directory is the drive. - Create a machine.
rlq create-machine --blueprint <name>materializes one under a generated id (orrun-scriptcreates one on demand). The blueprint (media components and all) and its scripts are always enough to rebuild it, so a machine is never precious. - Drive it. Run an attached
.rlqsscript withrlq run-script <label> --blueprint <name>, or operate the machine interactively —rlq start-machine, thenexec/wait/screen/press/selectagainst it, thenrlq stop-machine. - Take the results. A run returns its output to you and keeps
nothing of its own. A small value comes back through a machine
variable (
rlq get-machine-var); a file comes back by its guest address (rlq get-file "A:\RESULT.TXT" . esult.txt); a whole image comes back by swapping it out. Out of band,rlq get-machine-dirprints the machine directory: while the machine is stopped, its directory-source and image drives are ordinary host state to read or prepare. - Recreate freely.
destroy-machinedeletes a machine entirely andrecreate-machinerebuilds it under the same id;apply-blueprintadopts blueprint edits into a stopped machine.
Requirements
Reliquary requires:
- Python 3.9 or newer
- QEMU with
qemu-system-i386(andqemu-imgto create hard-disk images)
The Python package installs QEMU's official qemu.qmp library. QEMU itself is a separate application and must be
installed on the host.
Reliquary searches for QEMU in this order:
RELIQUARY_QEMU_HOMEenvironment variableQEMU_HOMEenvironment variable- The system
PATH - Common installation directories on Windows, macOS, and Linux
RELIQUARY_QEMU_HOME is how a specific installation is chosen; the
QEMU adapter probes the rest in the order above.
Where Reliquary keeps things
Reliquary has six working directories, and you can put each one wherever you like — machines on a fast disk, media on a big one, blueprints in your project's repository. Name any of them and the rest follow:
| Directory | Flag | Environment | Where it lands if you say nothing |
|---|---|---|---|
| home | --home-dir |
RELIQUARY_HOME_DIR |
Documents/reliquary |
| blueprints | --blueprints-dir |
RELIQUARY_BLUEPRINTS_DIR |
<home>/blueprints |
| scripts | --scripts-dir |
RELIQUARY_SCRIPTS_DIR |
<home>/scripts |
| cache | --cache-dir |
RELIQUARY_CACHE_DIR |
<home>/cache |
| media | --media-dir |
RELIQUARY_MEDIA_DIR |
<cache>/media |
| machines | --machines-dir |
RELIQUARY_MACHINES_DIR |
<cache>/machines |
The home's own default is reliquary/ under your Documents folder (the Windows known Documents folder — including when
redirected, e.g. into OneDrive — ~/Documents on macOS, and xdg-user-dir DOCUMENTS on Linux/BSD). When no Documents
folder can be determined, it falls back to ~/reliquary.
A flag beats the environment, and both beat the default. Assigning the cache moves media and machines with it unless you
have placed those too, so --cache-dir D:\reliquary-cache is enough to keep the bulk off a synced Documents folder. From
Python the twins are reliquary.set_home_dir(), set_cache_dir(), set_blueprints_dir(), and so on — or a
reliquary.Context(...) to scope a single call.
The platform Documents lookup itself is available as
reliquary.documents_dir(), returning the folder path or None when it cannot be determined, for callers that want to
anchor their own directories the same way.
The layout is:
Documents/reliquary/
├── blueprints/ composed blueprints you author (<name>.rlqb) —
│ a machine plus its media/source/archive components
├── scripts/ automation scripts (<name>.rlqs)
└── cache/ regenerable; resolves independently
│ (--cache-dir) and can live elsewhere
├── media/ cached, hash-verified media payloads
└── machines/<id>/ each materialized machine — its own directory
with machine.json (the state; while running its
`vm` section holds the live VM identity, and
any machine variables a script set), media/
(per-machine images), screenshots/, and a
<backend>/ subdir (e.g. qemu/qemu-stderr.log).
A run stores nothing here — it returns its
output to whoever started it
A machine is wholly its machines-directory entry — there is no root-home machine model. Everything under the cache is regenerable. When you have not named a home, the one Reliquary picked is printed to standard error.
Blueprints (with their media/source/archive components) and scripts are
read from the blueprints and scripts directories above, each walked
recursively by file extension. A name those directories do not hold is
taken from the built-in codex — convenient interactively, and the
reason freedos works on a fresh install. For automation, point the
directories at your project and turn that fallback off:
rlq --blueprints-dir .\vm --scripts-dir .\vm --no-autoseed run-script install --blueprint freedos
Now your source-controlled files are the only source, so a run is
reproducible and never picks up whatever happens to be in your home.
Seeding on request still works wherever you point it —
rlq --blueprints-dir .\vm seed-blueprint freedos copies a first draft
into your project, and you commit it. The embedding API is stricter
still: it assigns no directory and never autoseeds, so a library call
fails closed rather than reading a developer's home.
A machine records which blueprint file it was built from, so a
--blueprint <name> selection only ever picks a machine built from the
blueprint the current invocation resolves — two projects that share a
blueprint name never disturb each other's machines.
First session
Beyond running a whole script, you can drive a machine interactively —
useful for exploring a guest or debugging a workflow step by step. This
session starts a machine, reaches the DOS prompt, runs a few commands,
and stops it. Every guest-console command selects its machine with
--blueprint <name> (or --machine <id>).
1. Get a machine
Seed a codex blueprint (or scaffold your own with
rlq new-blueprint <name>), then create a machine from it:
rlq seed-blueprint freedos
rlq create-machine --blueprint freedos
create-machine materializes the machine's per-machine images under
cache/machines/<id>/media/ and prints the generated id. (If a
machine of this blueprint already exists — e.g. one installed by
run-script install — you can start it directly.)
2. Start it
rlq start-machine --blueprint freedos
Reliquary picks the backend, starts it headless, assigns the VM a
unique identity, and records it in the machine's machine.json (as a
vm section, written while running). Later commands find the running
VM through the machine selector, so nothing about the connection is
ever copied by hand — how the backend is reached is its adapter's
business. The machine stays running until you stop it. Add
--display for a visible, manually interactive window.
3. Reach the DOS prompt
start-machine returns when QEMU is ready, not when DOS is. Wait for a
prompt before running commands:
rlq wait "C:\\\\>" --blueprint freedos
4. Run DOS commands
rlq exec "dir" --blueprint freedos
rlq exec "myprog.exe > result.log" --blueprint freedos
exec types the command and waits for the DOS prompt to return. To
retrieve detailed output, name a directory-source media (source a
host directory, materialize: use) on a machine drive and have the
program write to it; while the machine is stopped, that directory is
ordinary host state (rlq get-machine-dir prints the path).
5. Inspect the guest
rlq screen --blueprint freedos
rlq screenshot after-test --blueprint freedos
screen prints the current 80-by-25 text screen; screenshot saves a
PNG under the machine's own screenshots/ directory. Screenshot names
are filename stems, not paths.
6. Stop it
rlq stop-machine --blueprint freedos
Stopping verifies the VM's recorded identity before closing it and flushes guest writes to any virtual FAT drive. QEMU snapshots a directory-source media's directory when the drive is attached, so after changing its host files, stop and restart the machine before using them in the guest.
Command guide
Managing machines (blueprint lifecycle)
rlq create-machine --blueprint NAME
rlq start-machine (--blueprint NAME | --machine ID) [--display]
rlq stop-machine (--blueprint NAME | --machine ID)
rlq destroy-machine (--blueprint NAME | --machine ID)
rlq list-machines [--blueprint NAME]
rlq delete-blueprint NAME
--blueprint selects that blueprint's sole machine (or names the blueprint for create-machine).
--machine takes the full id (<blueprint>-<n>) exactly — no prefix matching and no bare-number form.
The two selectors are mutually exclusive. Machines live under
cache/machines/<name>-<n>/.
delete-blueprint removes the home blueprint file and refuses while
any machine of it still exists.
rlq create-machine --home-dir $scratch --blueprint plain
rlq start-machine --home-dir $scratch --blueprint plain --display
rlq stop-machine --home-dir $scratch --blueprint plain
rlq list-machines --home-dir $scratch
Media catalog
rlq list-media [--builtin]
rlq fetch-media NAME
rlq clean-media [NAME]
rlq prune-media [--dry-run]
rlq add-media NAME FILE
list-media names the media resolvable from the active source (or the
package codex with --builtin). fetch-media resolves one by name and
warms its cached payload.
Everything cached lives in one place, cache/media/, keyed by the name
of the media it is — a container is a media like any other, so there is
no separate archive cache. clean-media reclaims what can be fetched
or derived again; prune-media is the informed version, keeping what
the scope can still attach and dropping what only existed to produce
it, so after an install the extracted ISO stays and the zip husk goes.
add-media is the door for payloads nothing can locate — a licensed
ISO you cannot redistribute — verified against the blueprint's pin and
then never reclaimed behind your back.
(Media are specs inside a .rlqb now, so there is no delete-media
command — removing one means editing the blueprint that declares it.)
Driving a machine from a program
rlq run-script LABEL (--blueprint NAME | --machine ID) [--progress MODE]
rlq get-machine-var KEY (--blueprint NAME | --machine ID)
rlq put-file HOST-PATH GUEST-ADDRESS (--blueprint NAME | --machine ID)
rlq get-file GUEST-ADDRESS HOST-PATH (--blueprint NAME | --machine ID)
rlq put-files HOST-DIR GUEST-DIR (--blueprint NAME | --machine ID)
rlq get-files GUEST-DIR HOST-DIR (--blueprint NAME | --machine ID)
rlq list-files GUEST-DIR [--recursive] (--blueprint NAME | --machine ID)
rlq insert-media SLOT --file PATH (--blueprint NAME | --machine ID)
This is the loop an automating program runs: put work into the guest, run it, read the result back, iterate. Reliquary supplies the transports and attaches no meaning to what travels through them — there is no pass/fail vocabulary and no result parsing, because those belong to whatever you are building.
Values come back as machine variables: a script's set result
"PASS" writes one, and get-machine-var result reads it from any
process. They are cleared at each start, so one always reports what the
current boot produced. Readiness rides the same channel — your own
ready script sets a variable and you poll for it; Reliquary ships no
readiness script of its own.
Files come back by their guest address — A:\RESULT.TXT, the way
the guest names it, never a host image path. One file moves with
put-file / get-file and a whole tree with put-files /
get-files, whose addresses name a directory the same way (A:\ is
the drive itself); list-files says what is over there, printing
addresses the other four accept. All of them work while the machine is
stopped, at whatever letter the drive lands on — including behind an
installed C:. Over a directory-source drive the backend snapshots
that directory at attach, so a stopped machine is what makes a put
visible and a guest write flushed; over a drive image Reliquary
mounts the disk on the host and works the filesystem inside it, so
files move into and out of an installed C: with no guest running.
A write is staged and swapped in at the end, so an interrupted one
leaves the disk as it was.
When a reboot per round costs too much, swap the medium instead:
insert-media --file mounts an image you built, live, and ejecting
flushes the guest's writes back to that same file.
rlq put-files .\suite "A:\" --machine rig-0
rlq run-script test --machine rig-0 --progress jsonl > run.jsonl
rlq get-machine-var result --machine rig-0
rlq stop-machine --machine rig-0
rlq list-files "A:\OUT" --recursive --machine rig-0
rlq get-files "A:\OUT" .\out --machine rig-0
--progress selects the live rendering: pretty for a person,
plain for a log, jsonl for a program (stdout carries the event
stream and nothing else, the last line being the outcome). Exit codes
carry the outcome too, and they are the same on every command rather
than a script run's alone — 2 your input is illegal on its face,
3 it is legal and the world does not satisfy it, 4 the operation
started and failed, 5 cancelled, 1 a fault of Reliquary's own and
never a mistake of yours.
Keyboard and command input
rlq type TEXT
rlq enter LINE
rlq press KEY [KEY ...]
rlq exec COMMAND
rlq select ITEM [--exclude TEXT]
Every guest-console command targets a running machine — select it with
--blueprint <name> / --machine <id>, as in the First
session. The examples below omit the selector for
brevity.
type sends raw text with no trailing Enter; enter types a line and presses Enter. exec additionally waits for the
prompt to return. press accepts the script language's portable key names (and + chords), such as:
rlq press down enter
select selects an entry in a cursor-key driven text menu, such as a boot menu. It presses the up/down cursor keys and
follows the selection highlight through the VGA attribute bytes, so the entry is confirmed by what the guest actually
displays before Enter is pressed:
rlq select "Use FreeDOS 1.4 in Live Environment mode"
The item text is matched case-insensitively against the visible screen rows. A row exactly equal to the item wins over
rows merely containing it (so "Plain DOS system" is selectable beside
"Plain DOS system, with sources"); otherwise the item must be contained in exactly one row. --exclude TEXT (
repeatable) rules rows out instead: rows containing an excluded text are never selected, which is another way to
disambiguate:
rlq select "Full installation" --exclude "with sources"
Use the global --timeout SECONDS option to change the 30-second navigation timeout.
Reading the guest
rlq screen
rlq wait REGEX
rlq screenshot [NAME]
Use the global --timeout SECONDS option to change the timeout for
exec or wait.
QEMU monitor access
rlq hmp "info block"
hmp sends a raw QEMU human-monitor command. It is intended for QEMU operations that do not yet have a dedicated
Reliquary command.
Run reliquary --help or reliquary COMMAND --help for the complete current syntax.
Python usage
The CLI is a thin veneer over the embedding API: every command maps
one-to-one onto a Python call with the same semantics. To run a whole
script, reliquary.run_script("install", blueprint="freedos")
is the one call. To drive a machine directly, create and start it, then
attach the interaction adapter to the machine's own directory — that is
where its recorded identity lives, so ownership is verified against it:
import reliquary
machine_id = reliquary.create_machine("freedos")
reliquary.start_machine(machine_id)
home = reliquary.machine_dir_path(machine_id)
machine = reliquary.Machine(home)
guest = reliquary.AgentlessGuestExec(machine)
try:
guest.wait_ready()
guest.execute("dir", timeout=15)
print("\n".join(machine.screen_text()))
machine.screenshot("after-test")
finally:
reliquary.stop_machine(machine_id)
Machine also exposes the VGA text screen directly: machine.screen_text()
returns the 80x25 rows, and machine.wait_text(pattern, timeout=60) polls until the screen matches a regular
expression (returning the matching screen)
or raises TimeoutError. This is how to block on specific output, such as a boot menu:
machine.wait_text(r"Welcome to FreeDOS")
Once a cursor-key menu is displayed, machine.cursor_menu_select()
navigates it by feedback: it presses up/down, follows the selection highlight through the VGA attribute bytes, and
presses Enter only after the highlight sits on the row matching the given text (case-insensitive; an exact row match
wins over rows merely containing the item, which otherwise must be unique). exclude= takes text snippets whose rows
are never selected, as another way to disambiguate. The item must match when navigation starts; a menu that rewrites its
rows as the highlight moves (the FreeDOS installer's language chooser translates every entry into the newly highlighted
language) is then navigated by row, and the returned text is what the selected row displayed when Enter was pressed:
machine.wait_text(r"Welcome to FreeDOS")
machine.cursor_menu_select("Use FreeDOS 1.4 in Live Environment mode")
These screen and keyboard operations live on the platform-neutral
Machine, so they work on any guest displaying through VGA text mode — boot menus and loaders included, before any
operating system is up. Module-level conveniences (reliquary.cursor_menu_select(item, port=port),
reliquary.screen_text(port=port), ...) wrap the same methods.
Machine.qmp() exposes the identity-verified QMP session when a caller needs raw monitor access. The yielded QEMU
session provides both cmd() for QMP and
hmp() for human-monitor commands:
with machine.qmp() as qmp:
status = qmp.cmd("query-status")
blocks = qmp.hmp("info block")
Running scripts and managing machines
The lifecycle and scripting verbs are all available as Python calls
with the same names as their CLI twins: create_machine /
start_machine / stop_machine / destroy_machine /
recreate_machine / apply_blueprint, and run_script /
check_script for the .rlqs language. run_script returns the
run's output — the whole event stream, plus the final script phase
and the machine's phase — and writes nothing to disk; it raises by
error class on failure. See the API reference
for the full surface and docs/spec/api.md
for the end-goal design.
Troubleshooting
QEMU cannot be found
Install QEMU and put qemu-system-i386 on PATH, or set
RELIQUARY_QEMU_HOME to the QEMU installation directory. With no
QEMU available, create-machine reports every backend it probed and
why each was passed over.
A command cannot find an active VM
Guest-console commands find the running VM through the vm section of
the machine's cache/machines/<id>/machine.json. Ensure every command
uses the same --home-dir / RELIQUARY_HOME_DIR and the same machine
selector, and that rlq start-machine completed successfully.
The VM identity does not match
Reliquary verifies the unique QEMU name and per-start uuid before
sending any command. An identity error means the recorded port now
belongs to another process or the state file is stale. The unrelated VM
is not modified. Review the machine's machine.json (its vm section)
and its backend qemu/qemu-stderr.log (under cache/machines/<id>/;
rlq get-machine-dir prints the path), then start the machine again.
QEMU exits during startup
The error includes the selected port, QEMU exit status, command line,
and path to the machine's qemu/qemu-stderr.log. That log normally
contains QEMU's reason, such as an invalid device option or an
unavailable disk image.
Guest-written files are missing
Stop QEMU before reading files written to the virtual FAT drive. Writes are flushed back to the host during shutdown.
License
BSD-3-Clause. See LICENSE.
The name Reliquary is owned by Paul Galbraith and is not licensed for use by forks or redistributions. See TRADEMARKS.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reliquary-0.1.0.dev4.tar.gz.
File metadata
- Download URL: reliquary-0.1.0.dev4.tar.gz
- Upload date:
- Size: 561.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d40fe92a49861d1d2f6218635eb55a6450ef5af9810ec4332e61fcf0889e7d6
|
|
| MD5 |
042936efdd94f6ad11403896ead0122a
|
|
| BLAKE2b-256 |
d8d38a54e54d630a96636afc0d067610338dd30b6a2d6cb06602e13eb0bde5f0
|
File details
Details for the file reliquary-0.1.0.dev4-py3-none-any.whl.
File metadata
- Download URL: reliquary-0.1.0.dev4-py3-none-any.whl
- Upload date:
- Size: 221.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e23e68595c7b0fe7415243c9731c7a96da99fdd3eeb734745708f0b0eb859b9
|
|
| MD5 |
41d76cb937a9fa7b2dda90ab57a7afb1
|
|
| BLAKE2b-256 |
64a512450329391a19e9891b98f0a0291e1ae8c0766875ab4e4483b42ef48845
|