Skip to content

Commit

Permalink
virtme-ng: channel the return code of a command to the host
Browse files Browse the repository at this point in the history
Send the return code of the command executed inside the guest to the
host.

This allows to run commands inside a guest and give users the impression
that they are running on the host.

Examples:

  $ vng -r -- true && echo OK || echo ERROR: $?
  OK
  $ vng -r -- false && echo OK || echo ERROR: $?
  ERROR: 1
  $ vng -r -- exit 22 && echo OK || echo ERROR: $?
  ERROR: 22

This feature is particularly useful for automation/CI, since it easily
allows to check the return code of a script executed inside virtme-ng as
if it was running directly on the host.

This fixes issue #48.

Signed-off-by: Andrea Righi <[email protected]>
  • Loading branch information
Andrea Righi committed Jan 31, 2024
1 parent 4381750 commit 25b2095
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 8 deletions.
34 changes: 32 additions & 2 deletions virtme/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,24 @@ def do_it() -> int:
]
)

def do_script(shellcmd: str, show_boot_console=False) -> None:
ret_path = None

def cleanup_script_retcode():
os.unlink(ret_path)

def fetch_script_retcode():
if ret_path is None:
return None
try:
with open(ret_path, 'r', encoding="utf-8") as file:
number_str = file.read().strip()
if number_str.isdigit():
return int(number_str)
return None
except FileNotFoundError:
return None

def do_script(shellcmd: str, ret_path=None, show_boot_console=False) -> None:
if args.graphics is None:
# Turn off default I/O
qemuargs.extend(arch.qemu_nodisplay_args())
Expand Down Expand Up @@ -1106,6 +1123,13 @@ def do_script(shellcmd: str, show_boot_console=False) -> None:
["-device", "virtserialport,name=virtme.dev_stderr,chardev=dev_stderr"]
)

# Create a virtio serial device to channel the retcode of the script
# executed in the guest to the host.
if ret_path is not None:
qemuargs.extend(["-chardev", f"file,id=ret,path={ret_path}"])
qemuargs.extend(["-device", arch.virtio_dev_type("serial")])
qemuargs.extend(["-device", "virtserialport,name=virtme.ret,chardev=ret"])

# Scripts shouldn't reboot
qemuargs.extend(["-no-reboot"])

Expand Down Expand Up @@ -1141,7 +1165,9 @@ def do_script(shellcmd: str, show_boot_console=False) -> None:
args.script_sh = args.graphics

if args.script_sh is not None:
do_script(args.script_sh, show_boot_console=args.show_boot_console)
_, ret_path = tempfile.mkstemp(prefix="virtme_ret")
atexit.register(cleanup_script_retcode)
do_script(args.script_sh, ret_path=ret_path, show_boot_console=args.show_boot_console)

if args.script_exec is not None:
do_script(
Expand Down Expand Up @@ -1305,7 +1331,11 @@ def do_script(shellcmd: str, show_boot_console=False) -> None:
if pid:
try:
pid, status = os.waitpid(pid, 0)
ret = fetch_script_retcode()
if ret is not None:
return ret
return status

except KeyboardInterrupt:
sys.stderr.write("Interrupted.")
sys.exit(1)
Expand Down
13 changes: 12 additions & 1 deletion virtme/guest/virtme-init
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ if [[ -n "${user_cmd}" ]]; then
/dev/virtio-ports/virtme.stderr \
/dev/virtio-ports/virtme.dev_stdout \
/dev/virtio-ports/virtme.dev_stderr

if [ -e /dev/virtio-ports/virtme.ret ]; then
chown ${virtme_user} \
/dev/virtio-ports/virtme.ret
fi
fi

# Fix /dev/stdout and /dev/stderr.
Expand Down Expand Up @@ -289,7 +294,13 @@ if [[ -n "${user_cmd}" ]]; then
else
setsid bash /tmp/.virtme-script </dev/virtio-ports/virtme.stdin >/dev/virtio-ports/virtme.stdout 2>/dev/virtio-ports/virtme.stderr
fi
log "script returned $?"
ret=$?
log "script returned {$ret}"

# Channel exit code to the host.
if [ -e /dev/virtio-ports/virtme.ret ]; then
echo ${ret} > /dev/virtio-ports/virtme.ret
fi

# Hmm. We should expose the return value somehow.
sync
Expand Down
12 changes: 8 additions & 4 deletions virtme_ng/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,8 +1102,7 @@ def clean(kern_source, args):

def run(kern_source, args):
"""Run the kernel."""
kern_source.run(args)
return True
return kern_source.run(args)


@spinner_decorator(message="🐞 generating memory dump")
Expand Down Expand Up @@ -1137,12 +1136,17 @@ def do_it() -> int:
if not args.skip_config:
config(kern_source, args)
if args.kconfig:
return
return 0
make(kern_source, args)
else:
run(kern_source, args)
try:
run(kern_source, args)
return 0
except CalledProcessError as exc:
return exc.returncode
except CalledProcessError as exc:
raise SilentError() from exc
return 0


def main() -> int:
Expand Down
2 changes: 1 addition & 1 deletion virtme_ng_init
Submodule virtme_ng_init updated 1 files
+17 −1 src/main.rs

0 comments on commit 25b2095

Please sign in to comment.