From dbed89b54f898271b88a88411954f857c793afb5 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 2 Dec 2024 15:53:39 +0100 Subject: [PATCH] vsock: connect: generate helper script Directly connecting to the remote shell using socat launching 'bash' remotely doesn't give a nice user experience: env vars like $HOME are not propagated, the terminal size is set to 80x24, the command that is typed is not visible, the directory is not the one picked with --cwd, etc. To give a better user experience, the '--vsock-connect' option is now creating a script that is setting up the missing pieces. This script is executed when the VM is launched with '--vsock' without any arguments. Note that it is required to pass info from the host, when launching '--vsock-connect', to the VM, e.g. for the terminal size. This is done via the script that is executed once connected to the vsock inside the VM. The VM needs access to this script, and the host needs to be able to modify it. Such script is then created in /tmp/virtme-vsock/.sh on both the host and the VM. If the whole filesystem is mounted (default), this file will be accessible from the VM. If not, a mount point will be added to access the parent directory from the VM. Signed-off-by: Matthieu Baerts (NGI0) --- README.md | 18 ------------ virtme/commands/run.py | 59 ++++++++++++++++++++++++++++++++++++---- virtme/guest/virtme-init | 4 +++ virtme_ng/run.py | 6 ++-- virtme_ng_init | 2 +- 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a4a7215..b08a701 100644 --- a/README.md +++ b/README.md @@ -354,24 +354,6 @@ Examples $ vng --vsock-connect ``` - - Connect to a remote shell with proper dimensions, env vars, and using 'Fish': -``` - # Start the vng instance with vsock support: - $ vng --vsock "${PWD}/console.sh" - - # In a separate terminal run the following commands: - $ read -r rows columns <<< "$(stty size)" - $ cat <<-EOF > console.sh - #! /bin/bash - stty rows ${rows} columns ${columns} - cd "\${virtme_chdir}" - HOME=${HOME} - fish # use use zsh, tmux, byobu, screen, etc. - EOF - $ chmod +x console.sh - $ vng --vsock-connect -``` - - Run virtme-ng inside a docker container: ``` $ docker run -it --privileged ubuntu:23.10 /bin/bash diff --git a/virtme/commands/run.py b/virtme/commands/run.py index 0b2cbd1..e7ce1fe 100644 --- a/virtme/commands/run.py +++ b/virtme/commands/run.py @@ -133,13 +133,16 @@ def make_parser() -> argparse.ArgumentParser: g.add_argument( "--vsock", action="store", - default=None, - help="Enable a VSock to communicate from the host to the device and " - + "execute the specified command.", + nargs="?", + metavar="COMMAND", + const="", + help="Enable a VSock to communicate from the host to the device. " + + "An argument can be optionally specify to start a different command.", ) g.add_argument( "--vsock-cid", action="store", + metavar="CID", type=int, default=3, help="CID for the VSock.", @@ -866,7 +869,27 @@ def is_subpath(path, potential_parent): def do_it() -> int: args = _ARGPARSER.parse_args() + vsock_script_path = os.path.join(tempfile.gettempdir(), "virtme-vsock", + f"{args.vsock_cid}.sh") + if args.vsock_connect: + # Note: we could accept arguments passed to --vsock-connect to run a + # specific command, and return, without requiring an interactive tty. + try: + (cols, rows) = os.get_terminal_size() + except OSError: + cols, rows = (80, 24) + + with open(vsock_script_path, 'w', encoding="utf-8") as file: + print(( + '#! /bin/bash\n' + f'stty rows {rows} cols {cols} iutf8 echo\n' + 'HOME=$(getent passwd ${virtme_user:-root} | cut -d: -f6)\n' + 'cd ${virtme_chdir:+"${virtme_chdir}"}\n' + 'exec su ${virtme_user:-root}' + ), file=file) + os.chmod(vsock_script_path, 0o755) + tty = os.ttyname(sys.stdin.fileno()) command = ['socat', f'file:{tty},raw,echo=0', f'VSOCK-CONNECT:{args.vsock_cid}:1024'] @@ -1409,8 +1432,34 @@ def get_net_mac(index): ] ) - if args.vsock: - kernelargs.extend([f"virtme.vsockexec=`{args.vsock}`"]) + def cleanup_vsock_script(): + os.unlink(vsock_script_path) + + if args.vsock is not None: + if os.path.exists(vsock_script_path): + arg_fail("vsock: '%s' file exists: " % vsock_script_path + + "another VM is running with the same --vsock-cid? " + + "If not, remove this file.") + + # create an empty file that can be populated later on + vsock_script_dir = os.path.dirname(vsock_script_path) + os.makedirs(vsock_script_dir, exist_ok=True) + open(vsock_script_path, 'w', encoding="utf-8").close() + atexit.register(cleanup_vsock_script) + + if args.vsock: + vsock_exec = args.vsock + else: + vsock_exec = vsock_script_path + if args.root != "/": + virtfs_config = VirtFSConfig( + path=vsock_script_dir, + mount_tag="virtme.vsockmount", + ) + export_virtfs(qemu, arch, qemuargs, virtfs_config) + kernelargs.append("virtme_vsockmount=%s" % vsock_script_dir) + + kernelargs.extend([f"virtme.vsockexec=`{vsock_exec}`"]) qemuargs.extend(["-device", "vhost-vsock-pci,guest-cid=%d" % args.vsock_cid]) if args.pwd: diff --git a/virtme/guest/virtme-init b/virtme/guest/virtme-init index 2681ad7..45eeaf2 100755 --- a/virtme/guest/virtme-init +++ b/virtme/guest/virtme-init @@ -277,6 +277,10 @@ fi vsock_exec=$(sed -ne "s/.*virtme.vsockexec=\`\(.*\)\`.*/\1/p" /proc/cmdline) if [[ -n "${vsock_exec}" ]]; then + if [[ -n "${virtme_vsockmount}" ]]; then + mkdir -p "${virtme_vsockmount}" + mount -t 9p -o version=9p2000.L,trans=virtio,access=any "virtme.vsockmount" "${virtme_vsockmount}" + fi socat "VSOCK-LISTEN:1024,reuseaddr,fork" \ "EXEC:\"${vsock_exec}\",pty,stderr,setsid,sigint,sane,echo=0" & fi diff --git a/virtme_ng/run.py b/virtme_ng/run.py index 6fe8aee..b20ede6 100644 --- a/virtme_ng/run.py +++ b/virtme_ng/run.py @@ -362,15 +362,17 @@ def make_parser(): parser.add_argument( "--vsock", action="store", - const="bash -i", nargs="?", + metavar="COMMAND", + const="", help="Enable a VSock to communicate from the host to the device. " - + "An argument can be optionally specify to start a different shell.", + + "An argument can be optionally specify to start a different command.", ) parser.add_argument( "--vsock-cid", action="store", + metavar="CID", type=int, help="CID for the VSock.", ) diff --git a/virtme_ng_init b/virtme_ng_init index c43ba1c..c12db88 160000 --- a/virtme_ng_init +++ b/virtme_ng_init @@ -1 +1 @@ -Subproject commit c43ba1ca5d91953a723eb1d6d0f7b4b88da327ca +Subproject commit c12db8879ab4583b8aa80664f1c5fd17d309b36e