Skip to content

Commit 9b7077e

Browse files
author
Hang SU
committed
console: add Unix domain socket backend support
- Introduce `BackendType::Uds` for VM communication via Unix sockets - Refactor address generation logic with `generate_vm_sock_addrs` - Add CLI parameter `--vm-sock` to specify UDS path - Update documentation and error handling Signed-off-by: Hang SU <[email protected]> Suggested-by: Xuewei Niu <[email protected]>
1 parent 5808770 commit 9b7077e

File tree

6 files changed

+165
-14
lines changed

6 files changed

+165
-14
lines changed

vhost-device-console/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### Added
55

66
- [#811](https://github.com/rust-vmm/vhost-device/pull/811) Being able to specify max queue size
7+
- [#821](https://github.com/rust-vmm/vhost-device/pull/821) Add Unix domain socket backend support
78

89
### Changed
910

vhost-device-console/README.md

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ This program is a vhost-user backend that emulates a VirtIO Console device.
66
The device's binary takes as parameters a socket path, a socket number which
77
is the number of connections, commonly used across all vhost-devices to
88
communicate with the vhost-user frontend devices, and the backend type
9-
"nested" or "network".
9+
"nested" or "network" or "uds".
1010

1111
The "nested" backend allows input/output to the guest console through the
1212
current terminal.
1313

1414
The "network" backend creates a local TCP port (specified on vhost-device-console
1515
arguments) and allows input/output to the guest console via that socket.
1616

17+
The "uds" backend creates a unix domain socket (specified on vhost-device-console
18+
arguments) and allows input/output to the guest console via that socket.
19+
1720
This program is tested with QEMU's `vhost-user-device-pci` device.
1821
Examples' section below.
1922

@@ -40,13 +43,20 @@ vhost-device-console --socket-path=<SOCKET_PATH>
4043
The localhost's port to be used for each guest, this part will be increased with
4144
0,1,2..socket_count-1.
4245

43-
-- option:: -b, --backend=nested|network
46+
-- option:: -b, --backend=nested|network|uds
4447

4548
The backend type vhost-device-console to be used. The current implementation
46-
supports two types of backends: "nested", "network" (described above).
49+
supports 3 types of backends: "nested", "network", "uds".
4750
Note: The nested backend is selected by default and can be used only when
4851
socket_count equals 1.
4952

53+
.. option:: -v, --uds-path=uds-file-path
54+
55+
The unix domain socket to be used for each guest, this path will be suffixed with
56+
0,1,2..socket_count-1. e.g.: `--uds-path=/tmp/vm.sock --socket-count=2`
57+
leads to two connectable vhost-user sockets:
58+
/tmp/vm.sock0, /tmp/vm.sock1
59+
5060
.. option:: -q, --max-queue-size=SIZE
5161

5262
The maximum size of virtqueues. It is optional, and the default value is
@@ -62,10 +72,10 @@ VIRTIO_CONSOLE_F_SIZE features.
6272
## Features
6373

6474
The current device gives access to multiple QEMU guest by providing a login prompt
65-
either by connecting to a localhost server port (network backend) or by creating an
66-
nested command prompt in the current terminal (nested backend). This prompt appears
67-
as soon as the guest is fully booted and gives the ability to user run command as a
68-
in regular terminal.
75+
either by connecting to a localhost server port (network backend) or a unix socket
76+
file (uds backend) or by creating an nested command prompt in the current terminal
77+
(nested backend). This prompt appears as soon as the guest is fully booted and
78+
gives the ability to user run command as a in regular terminal.
6979

7080
## Examples
7181

@@ -83,15 +93,20 @@ For testing the device the required dependencies are:
8393
The daemon should be started first:
8494
```shell
8595
host# vhost-device-console --socket-path=/tmp/console.sock --socket-count=1 \
86-
--tcp-port=12345 --backend=network
96+
--tcp-port=12345 --backend=network # for network backend
97+
or
98+
99+
host# vhost-device-console --socket-path=/tmp/console.sock --socket-count=1 \
100+
--uds-path=/tmp/vm.sock --backend=uds # for uds backend
87101
```
88102
>Note: In case the backend is "nested" there is no need to provide
89-
"--socket-count" and "--tcp-port" parameters.
103+
"--socket-count", "--tcp-port" and "--uds-path" parameters.
90104

91105
The QEMU invocation needs to create a chardev socket the device can
92106
use to communicate as well as share the guests memory over a memfd.
93107

94-
There are two option for running QEMU with vhost-device-console:
108+
There are two option for running QEMU with vhost-device-console,
109+
and one for User Mode Linux:
95110

96111
1) Using `vhost-user-console-pci`:
97112
```text
@@ -119,9 +134,22 @@ host# qemu-system
119134
...
120135
```
121136

137+
3) Using User Mode Linux [`virtio console`](https://github.com/torvalds/linux/blob/848e076317446f9c663771ddec142d7c2eb4cb43/include/uapi/linux/virtio_ids.h#L34):
138+
```text
139+
host# linux root=/dev/ubda1 rw ubd0=$YOUR-PATH/kata-ubuntu-latest.image \
140+
<normal UML options> \
141+
virtio_uml.device=/tmp/console.sock0:3 console=tty0 console=hvc0 \
142+
init=/bin/systemd \
143+
systemd.unit=kata-containers.target agent.debug_console
144+
```
145+
Test with [kata-ubuntu-latest.image](https://github.com/kata-containers/kata-containers/releases/),
146+
you can also use systemd to setup pty manually without kata-agent help.
147+
122148
Eventually, the user can connect to the console by running:
123149
```test
124-
host# stty -icanon -echo && nc localhost 12345 && stty echo
150+
host# stty -icanon -echo && nc localhost 12345 && stty echo # for network backend
151+
or
152+
host# stty -icanon -echo && nc -U /tmp/vm.sock0 && stty echo # for uds backend
125153
```
126154

127155
>Note: `stty -icanon -echo` is used to force the tty layer to disable buffering and send / receive each character individually. After closing the connection please run `stty echo` so character are printed back on the local terminal console.

vhost-device-console/src/backend.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ pub enum Error {
4242
ThreadPanic(String, Box<dyn Any + Send>),
4343
#[error("Error using multiple sockets with Nested backend")]
4444
WrongBackendSocket,
45+
#[error("Invalid uds file")]
46+
InvalidUdsFile,
4547
}
4648

4749
#[derive(PartialEq, Eq, Debug)]
4850
pub struct VuConsoleConfig {
4951
pub socket_path: PathBuf,
52+
pub uds_path: PathBuf,
5053
pub backend: BackendType,
5154
pub tcp_port: String,
5255
pub socket_count: u32,
@@ -86,6 +89,26 @@ impl VuConsoleConfig {
8689
|i: u32| -> String { "127.0.0.1:".to_owned() + &(port_base + i).to_string() };
8790
(0..self.socket_count).map(make_tcp_port).collect()
8891
}
92+
93+
BackendType::Uds => {
94+
let uds_filename = self.uds_path.file_name().expect("uds has no filename.");
95+
let uds_parent = self
96+
.uds_path
97+
.parent()
98+
.expect("uds has no parent directory.");
99+
100+
let make_uds_path = |i: u32| -> String {
101+
let mut filename = uds_filename.to_os_string();
102+
filename.push(std::ffi::OsStr::new(&i.to_string()));
103+
uds_parent
104+
.join(&filename)
105+
.to_str()
106+
.expect("Path contains invalid UTF-8 characters")
107+
.to_string()
108+
};
109+
110+
(0..self.socket_count).map(make_uds_path).collect()
111+
}
89112
}
90113
}
91114
}
@@ -192,6 +215,7 @@ mod tests {
192215
fn test_console_valid_configuration_nested() {
193216
let args = ConsoleArgs {
194217
socket_path: String::from("/tmp/vhost.sock").into(),
218+
uds_path: String::from("/tmp/vm.sock").into(),
195219
backend: BackendType::Nested,
196220
tcp_port: String::from("12345"),
197221
socket_count: 1,
@@ -205,6 +229,7 @@ mod tests {
205229
fn test_console_invalid_configuration_nested_1() {
206230
let args = ConsoleArgs {
207231
socket_path: String::from("/tmp/vhost.sock").into(),
232+
uds_path: String::from("/tmp/vm.sock").into(),
208233
backend: BackendType::Nested,
209234
tcp_port: String::from("12345"),
210235
socket_count: 0,
@@ -221,6 +246,7 @@ mod tests {
221246
fn test_console_invalid_configuration_nested_2() {
222247
let args = ConsoleArgs {
223248
socket_path: String::from("/tmp/vhost.sock").into(),
249+
uds_path: String::from("/tmp/vm.sock").into(),
224250
backend: BackendType::Nested,
225251
tcp_port: String::from("12345"),
226252
socket_count: 2,
@@ -237,6 +263,7 @@ mod tests {
237263
fn test_console_valid_configuration_network_1() {
238264
let args = ConsoleArgs {
239265
socket_path: String::from("/tmp/vhost.sock").into(),
266+
uds_path: String::from("/tmp/vm.sock").into(),
240267
backend: BackendType::Network,
241268
tcp_port: String::from("12345"),
242269
socket_count: 1,
@@ -250,6 +277,7 @@ mod tests {
250277
fn test_console_valid_configuration_network_2() {
251278
let args = ConsoleArgs {
252279
socket_path: String::from("/tmp/vhost.sock").into(),
280+
uds_path: String::from("/tmp/vm.sock").into(),
253281
backend: BackendType::Network,
254282
tcp_port: String::from("12345"),
255283
socket_count: 2,
@@ -280,6 +308,7 @@ mod tests {
280308
fn test_start_backend_server_success() {
281309
let args = ConsoleArgs {
282310
socket_path: String::from("/not_a_dir/vhost.sock").into(),
311+
uds_path: String::from("/tmp/vm.sock").into(),
283312
backend: BackendType::Network,
284313
tcp_port: String::from("12345"),
285314
socket_count: 1,
@@ -293,6 +322,7 @@ mod tests {
293322
fn test_start_backend_success() {
294323
let config = VuConsoleConfig {
295324
socket_path: String::from("/not_a_dir/vhost.sock").into(),
325+
uds_path: String::from("/tmp/vm.sock").into(),
296326
backend: BackendType::Network,
297327
tcp_port: String::from("12346"),
298328
socket_count: 1,

vhost-device-console/src/console.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub enum BackendType {
1515
#[default]
1616
Nested,
1717
Network,
18+
Uds,
1819
}
1920

2021
#[derive(Debug)]

vhost-device-console/src/main.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,21 @@ struct ConsoleArgs {
5454
#[clap(short = 's', long, value_name = "SOCKET")]
5555
socket_path: PathBuf,
5656

57+
/// Virtual machine communication endpoint.
58+
/// Unix domain socket path (e.g., "/tmp/vm.sock").
59+
#[clap(
60+
short = 'v',
61+
long,
62+
value_name = "VM_SOCKET",
63+
default_value = "/tmp/vm.sock"
64+
)]
65+
uds_path: PathBuf,
66+
5767
/// Number of guests (sockets) to connect to.
5868
#[clap(short = 'c', long, default_value_t = 1)]
5969
socket_count: u32,
6070

61-
/// Console backend (Network, Nested) to be used.
71+
/// Console backend (Network, Nested, Uds) to be used.
6272
#[clap(short = 'b', long, value_enum, default_value = "nested")]
6373
backend: BackendType,
6474

@@ -87,14 +97,29 @@ impl TryFrom<ConsoleArgs> for VuConsoleConfig {
8797

8898
let ConsoleArgs {
8999
socket_path,
100+
uds_path,
90101
backend,
91102
tcp_port,
92103
socket_count,
93104
max_queue_size,
94105
} = args;
95106

107+
// check validation of uds_path under Uds mode.
108+
if backend == BackendType::Uds {
109+
if uds_path.as_os_str().is_empty() {
110+
return Err(Error::InvalidUdsFile);
111+
}
112+
113+
if let Some(parent_dir) = uds_path.parent() {
114+
if !parent_dir.exists() {
115+
return Err(Error::InvalidUdsFile);
116+
}
117+
}
118+
}
119+
96120
Ok(Self {
97121
socket_path,
122+
uds_path,
98123
backend,
99124
tcp_port,
100125
socket_count,

0 commit comments

Comments
 (0)