Skip to content

Commit

Permalink
feat: vsock support for remote console access
Browse files Browse the repository at this point in the history
Having a remote console access can be helpful for different reasons,
e.g. an easy way to have multiple terminals, not using a serial which
can be slow to print a lot of text, etc.

With this new support, a user can simply use vng like before, and add
the new --vsock option:

  vng --vsock (...)

Then in another terminal, it can connect to the existing VM:

  vng --vsock-connect

Advanced users can set a different CID with --vsock-cid, useful when
multiple VMs are started in parallel.

It is also possible to change the command that is executed when
connected inside the VM. By default, 'bash -i' is used, but it is
possible to pass something else, e.g.

  vng --vsock byobu

Or zsh, fish, tmux, etc. Or a script, for example to set env vars, change dir
and set other dimensions, etc., e.g. starting the VM with:

  $ vng --vsock "${PWD}/console.sh"

... and connecting to it from another terminal with:

  $ read -r rows columns <<< "$(stty size)"
  $ cat <<-EOF > console.sh
	#! /bin/bash
	stty rows ${rows} columns ${columns}
	cd "\${virtme_chdir}"
	HOME=${HOME}
	byobu
    EOF
  $ chmod +x console.sh
  $ vng --vsock-connect

Note: on my side, 'bash' is a bit weird: I cannot see the command I'm
typing. I didn't try to find a solution for a too long time as I'm
usually not using bash.

Regarding the kernel config, two new config are now required: VSOCKETS
and VIRTIO_VSOCKETS. They seem light enough.

While at it, fixed a typo in the README file with the --network option.

Link: #151
Signed-off-by: Matthieu Baerts (NGI0) <[email protected]>
  • Loading branch information
matttbe committed Dec 2, 2024
1 parent d22b9a1 commit 180654c
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 2 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,33 @@ Examples
# vmlinux available in the system.
```

- Connect to a simple remote shell:
```
# Start the vng instance with vsock support:
$ vng --vsock
# In a separate terminal run the following command to connect to a remote shell:
$ 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
Expand Down Expand Up @@ -449,7 +476,7 @@ Troubleshooting
$ groups | grep "kvm\|libvirt"
```

- When using `--net bridge` to create a bridged network in the guest you
- When using `--network bridge` to create a bridged network in the guest you
may get the following error:
```
...
Expand Down
3 changes: 3 additions & 0 deletions virtme/commands/configkernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ def arg_fail(message):
"CONFIG_ZONE_DEVICE=y",
"CONFIG_FUSE_FS=y",
"CONFIG_VIRTIO_FS=y",
"##: vsock support",
"CONFIG_VSOCKETS=y",
"CONFIG_VIRTIO_VSOCKETS=y",
]

_GENERIC_CONFIG_OPTIONAL = [
Expand Down
30 changes: 30 additions & 0 deletions virtme/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,25 @@ def make_parser() -> argparse.ArgumentParser:
help="The MAC address to assign to the NIC interface, e.g. 52:54:00:12:34:56. "
+ "The last octet will be incremented for the next network devices.",
)
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.",
)
g.add_argument(
"--vsock-cid",
action="store",
type=int,
default=3,
help="CID for the VSock.",
)
g.add_argument(
"--vsock-connect",
action="store_true",
help="Connect to a VM using VSock.",
)
g.add_argument(
"--balloon",
action="store_true",
Expand Down Expand Up @@ -847,6 +866,13 @@ def is_subpath(path, potential_parent):
def do_it() -> int:
args = _ARGPARSER.parse_args()

if args.vsock_connect:
tty = os.ttyname(sys.stdin.fileno())
command = ['socat', f'file:{tty},raw,echo=0',
f'VSOCK-CONNECT:{args.vsock_cid}:1024']
os.execvp('socat', command)
sys.exit(0)

arch = architectures.get(args.arch)
is_native = args.arch == platform.machine()

Expand Down Expand Up @@ -1383,6 +1409,10 @@ def get_net_mac(index):
]
)

if args.vsock:
kernelargs.extend([f"virtme.vsockexec=`{args.vsock}`"])
qemuargs.extend(["-device", "vhost-vsock-pci,guest-cid=%d" % args.vsock_cid])

if args.pwd:
rel_pwd = os.path.relpath(os.getcwd(), args.root)
if rel_pwd.startswith(".."):
Expand Down
6 changes: 6 additions & 0 deletions virtme/guest/virtme-init
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ if cat /proc/cmdline |grep -q -E '(^| )virtme.snapd($| )'; then
fi
fi

vsock_exec=$(sed -ne "s/.*virtme.vsockexec=\`\(.*\)\`.*/\1/p" /proc/cmdline)
if [[ -n "${vsock_exec}" ]]; then
socat "VSOCK-LISTEN:1024,reuseaddr,fork" \
"EXEC:\"${vsock_exec}\",pty,stderr,setsid,sigint,sane,echo=0" &
fi

user_cmd=$(sed -ne "s/.*virtme.exec=\`\(.*\)\`.*/\1/p" /proc/cmdline)
if [[ -n "${user_cmd}" ]]; then
if [[ ! -e "/dev/virtio-ports/virtme.stdin" ||
Expand Down
46 changes: 46 additions & 0 deletions virtme_ng/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,28 @@ def make_parser():
+ "The last octet will be incremented for the next network devices.",
)

parser.add_argument(
"--vsock",
action="store",
const="bash -i",
nargs="?",
help="Enable a VSock to communicate from the host to the device. "
+ "An argument can be optionally specified to start a different shell.",
)

parser.add_argument(
"--vsock-cid",
action="store",
type=int,
help="CID for the VSock.",
)

parser.add_argument(
"--vsock-connect",
action="store_true",
help="Connect to a VM using VSock.",
)

parser.add_argument(
"--disk",
"-D",
Expand Down Expand Up @@ -947,6 +969,24 @@ def _get_virtme_net_mac_address(self, args):
else:
self.virtme_param["net_mac_address"] = ""

def _get_virtme_vsock(self, args):
if args.vsock is not None:
self.virtme_param["vsock"] = "--vsock '" + args.vsock + "'"
else:
self.virtme_param["vsock"] = ""

def _get_virtme_vsock_cid(self, args):
if args.vsock_cid is not None:
self.virtme_param["vsock_cid"] = "--vsock-cid " + str(args.vsock_cid)
else:
self.virtme_param["vsock_cid"] = ""

def _get_virtme_vsock_connect(self, args):
if args.vsock_connect:
self.virtme_param["vsock_connect"] = "--vsock-connect"
else:
self.virtme_param["vsock_connect"] = ""

def _get_virtme_disk(self, args):
if args.disk is not None:
disk_str = ""
Expand Down Expand Up @@ -1102,6 +1142,9 @@ def run(self, args):
self._get_virtme_mods(args)
self._get_virtme_network(args)
self._get_virtme_net_mac_address(args)
self._get_virtme_vsock(args)
self._get_virtme_vsock_cid(args)
self._get_virtme_vsock_connect(args)
self._get_virtme_disk(args)
self._get_virtme_sound(args)
self._get_virtme_disable_microvm(args)
Expand Down Expand Up @@ -1141,6 +1184,9 @@ def run(self, args):
+ f'{self.virtme_param["mods"]} '
+ f'{self.virtme_param["network"]} '
+ f'{self.virtme_param["net_mac_address"]} '
+ f'{self.virtme_param["vsock"]} '
+ f'{self.virtme_param["vsock_cid"]} '
+ f'{self.virtme_param["vsock_connect"]} '
+ f'{self.virtme_param["disk"]} '
+ f'{self.virtme_param["sound"]} '
+ f'{self.virtme_param["disable_microvm"]} '
Expand Down
2 changes: 1 addition & 1 deletion virtme_ng_init
Submodule virtme_ng_init updated 1 files
+25 −0 src/main.rs

0 comments on commit 180654c

Please sign in to comment.