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 /// Configuration for a Vsock device.
21 pub struct VsockConfig {
22 /// CID to be used for this vsock device.
23 pub cid: u64,
24 /// Path to the vhost-vsock device.
25 #[serde(default = "default_vsock_path", rename = "device")]
26 pub vhost_device: PathBuf,
27 }
28
29 impl VsockConfig {
30 /// Create a new vsock configuration. If `vhost_device` is `None`, the default vhost-vsock
31 /// device path will be used.
new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self32 pub fn new<P: AsRef<Path>>(cid: u64, vhost_device: Option<P>) -> Self {
33 Self {
34 cid,
35 #[cfg(any(target_os = "android", target_os = "linux"))]
36 vhost_device: vhost_device
37 .map(|p| PathBuf::from(p.as_ref()))
38 .unwrap_or_else(|| PathBuf::from(VHOST_VSOCK_DEFAULT_PATH)),
39 }
40 }
41 }
42
43 #[cfg(test)]
44 mod tests {
45 use serde_keyvalue::from_key_values;
46 use serde_keyvalue::ErrorKind;
47 use serde_keyvalue::ParseError;
48
49 use super::*;
50
from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError>51 fn from_vsock_arg(options: &str) -> Result<VsockConfig, ParseError> {
52 from_key_values(options)
53 }
54
55 #[test]
params_from_key_values()56 fn params_from_key_values() {
57 // Default device
58 assert_eq!(
59 from_vsock_arg("cid=56").unwrap(),
60 VsockConfig {
61 vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
62 cid: 56,
63 }
64 );
65
66 // No argument
67 assert_eq!(
68 from_vsock_arg("").unwrap_err(),
69 ParseError {
70 kind: ErrorKind::SerdeError("missing field `cid`".into()),
71 pos: 0
72 }
73 );
74
75 // CID passed without key
76 assert_eq!(
77 from_vsock_arg("78").unwrap(),
78 VsockConfig {
79 #[cfg(any(target_os = "android", target_os = "linux"))]
80 vhost_device: VHOST_VSOCK_DEFAULT_PATH.into(),
81 cid: 78,
82 }
83 );
84
85 // CID passed twice
86 assert_eq!(
87 from_vsock_arg("cid=42,cid=56").unwrap_err(),
88 ParseError {
89 kind: ErrorKind::SerdeError("duplicate field `cid`".into()),
90 pos: 0,
91 }
92 );
93
94 // Invalid argument
95 assert_eq!(
96 from_vsock_arg("invalid=foo").unwrap_err(),
97 ParseError {
98 kind: ErrorKind::SerdeError(
99 "unknown field `invalid`, expected `cid` or `device`".into()
100 ),
101 pos: 0,
102 }
103 );
104
105 // Path device
106 assert_eq!(
107 from_vsock_arg("device=/some/path,cid=56").unwrap(),
108 VsockConfig {
109 vhost_device: "/some/path".into(),
110 cid: 56,
111 }
112 );
113
114 // CID passed without key
115 assert_eq!(
116 from_vsock_arg("56,device=/some/path").unwrap(),
117 VsockConfig {
118 vhost_device: "/some/path".into(),
119 cid: 56,
120 }
121 );
122
123 // Missing cid
124 assert_eq!(
125 from_vsock_arg("device=42").unwrap_err(),
126 ParseError {
127 kind: ErrorKind::SerdeError("missing field `cid`".into()),
128 pos: 0,
129 }
130 );
131
132 // Device passed twice
133 assert_eq!(
134 from_vsock_arg("cid=56,device=42,device=/some/path").unwrap_err(),
135 ParseError {
136 kind: ErrorKind::SerdeError("duplicate field `device`".into()),
137 pos: 0,
138 }
139 );
140 }
141 }
142