1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::path::Path;
6 use std::path::PathBuf;
7
8 use serde::Deserialize;
9 use serde::Serialize;
10 use serde_keyvalue::FromKeyValues;
11
12 static VHOST_VSOCK_DEFAULT_PATH: &str = "/dev/vhost-vsock";
13
default_vsock_path() -> PathBuf14 fn default_vsock_path() -> PathBuf {
15 PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)
16 }
17
18 #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, FromKeyValues)]
19 #[serde(deny_unknown_fields)]
20
21 /// Configuration for a Vsock device.
22 pub struct VsockConfig {
23 /// CID to be used for this vsock device.
24 pub cid: u64,
25 /// Path to the vhost-vsock device.
26 #[serde(default = "default_vsock_path", rename = "device")]
27 pub vhost_device: PathBuf,
28 }
29
30 impl VsockConfig {
31 /// Create a new vsock configuration. If `vhost_device` is `None`, the default vhost-vsock
32 /// device path will be used.
new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self33 pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self {
34 Self {
35 cid,
36 #[cfg(unix)]
37 vhost_device: vhost_device
38 .map(|p| PathBuf::from(p.as_ref()))
39 .unwrap_or_else(|| PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)),
40 }
41 }
42 }
43
44 #[cfg(test)]
45 mod tests {
46 use serde_keyvalue::from_key_values;
47 use serde_keyvalue::ErrorKind;
48 use serde_keyvalue::ParseError;
49
50 use super::*;
51
from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError>52 fn from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError> {
53 from_key_values(options)
54 }
55
56 #[test]
params_from_key_values()57 fn params_from_key_values() {
58 // Default device
59 assert_eq!(
60 from_vsock_arg("cid=56").unwrap(),
61 VsockConfig {
62 vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
63 cid: 56,
64 }
65 );
66
67 // No argument
68 assert_eq!(
69 from_vsock_arg("").unwrap_err(),
70 ParseError {
71 kind: ErrorKind::SerdeError("missing field `cid`".into()),
72 pos: 0
73 }
74 );
75
76 // CID passed without key
77 assert_eq!(
78 from_vsock_arg("78").unwrap(),
79 VsockConfig {
80 #[cfg(unix)]
81 vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
82 cid: 78,
83 }
84 );
85
86 // CID passed twice
87 assert_eq!(
88 from_vsock_arg("cid=42,cid=56").unwrap_err(),
89 ParseError {
90 kind: ErrorKind::SerdeError("duplicate field `cid`".into()),
91 pos: 0,
92 }
93 );
94
95 // Invalid argument
96 assert_eq!(
97 from_vsock_arg("invalid=foo").unwrap_err(),
98 ParseError {
99 kind: ErrorKind::SerdeError(
100 "unknown field `invalid`, expected `cid` or `device`".into()
101 ),
102 pos: 0,
103 }
104 );
105
106 // Path device
107 assert_eq!(
108 from_vsock_arg("device=/some/path,cid=56").unwrap(),
109 VsockConfig {
110 vhost_device: "/some/path".into(),
111 cid: 56,
112 }
113 );
114
115 // CID passed without key
116 assert_eq!(
117 from_vsock_arg("56,device=/some/path").unwrap(),
118 VsockConfig {
119 vhost_device: "/some/path".into(),
120 cid: 56,
121 }
122 );
123
124 // Missing cid
125 assert_eq!(
126 from_vsock_arg("device=42").unwrap_err(),
127 ParseError {
128 kind: ErrorKind::SerdeError("missing field `cid`".into()),
129 pos: 0,
130 }
131 );
132
133 // Device passed twice
134 assert_eq!(
135 from_vsock_arg("cid=56,device=42,device=/some/path").unwrap_err(),
136 ParseError {
137 kind: ErrorKind::SerdeError("duplicate field `device`".into()),
138 pos: 0,
139 }
140 );
141 }
142 }
143