1use hyperv_ic_resources::kvp::KvpConnectRpc;
7use mesh::CancelContext;
8use mesh::rpc::RpcSend as _;
9use std::net::Ipv4Addr;
10use std::net::Ipv6Addr;
11use std::time::Duration;
12
13#[derive(clap::Args)]
14pub(crate) struct KvpCommand {
15 #[clap(long, default_value = "3")]
17 timeout: u64,
18 #[clap(subcommand)]
19 command: KvpSubcommand,
20}
21
22#[derive(clap::Subcommand)]
23enum KvpSubcommand {
24 Set {
26 #[clap(long)]
28 pool: KvpPool,
29
30 key: String,
32
33 #[clap(long = "type", short = 't', default_value = "string")]
34 value_type: ValueType,
35
36 value: String,
38 },
39 Get {
41 #[clap(long)]
43 pool: KvpPool,
44
45 key: String,
47 },
48 Enum {
50 #[clap(long)]
52 pool: KvpPool,
53 },
54 IpInfo {
56 adapter_id: String,
58 },
59}
60
61#[derive(clap::ValueEnum, Clone)]
62enum KvpPool {
63 Guest,
64 External,
65 Auto,
66 AutoExternal,
67}
68
69#[derive(clap::ValueEnum, Clone)]
70enum ValueType {
71 String,
72 Dword,
73 Qword,
74}
75
76pub(crate) async fn handle_kvp(
77 kvp: &mesh::Sender<KvpConnectRpc>,
78 command: KvpCommand,
79) -> anyhow::Result<()> {
80 let KvpCommand { timeout, command } = command;
81 CancelContext::new()
82 .with_timeout(Duration::from_secs(timeout))
83 .until_cancelled(handle_subcommand(kvp, command))
84 .await?
85}
86
87async fn handle_subcommand(
88 kvp: &mesh::Sender<KvpConnectRpc>,
89 command: KvpSubcommand,
90) -> anyhow::Result<()> {
91 let (kvp, _) = kvp.call_failable(KvpConnectRpc::WaitForGuest, ()).await?;
92 match command {
93 KvpSubcommand::Set {
94 pool,
95 key,
96 value_type,
97 value,
98 } => {
99 let pool = pool_cvt(pool);
100 let value = match value_type {
101 ValueType::String => hyperv_ic_resources::kvp::Value::String(value),
102 ValueType::Dword => hyperv_ic_resources::kvp::Value::U32(value.parse()?),
103 ValueType::Qword => hyperv_ic_resources::kvp::Value::U64(value.parse()?),
104 };
105 kvp.call_failable(
106 hyperv_ic_resources::kvp::KvpRpc::Set,
107 hyperv_ic_resources::kvp::SetParams { pool, key, value },
108 )
109 .await?;
110 }
111 KvpSubcommand::Get { pool, key } => {
112 let pool = pool_cvt(pool);
115 for i in 0.. {
116 match kvp
117 .call_failable(
118 hyperv_ic_resources::kvp::KvpRpc::Enumerate,
119 hyperv_ic_resources::kvp::EnumerateParams { pool, index: i },
120 )
121 .await?
122 {
123 Some(v) if v.key == key => {
124 println!("{}", DisplayValue(&v.value));
125 break;
126 }
127 Some(_) => {
128 }
130 None => {
131 anyhow::bail!("not found");
132 }
133 }
134 }
135 }
136 KvpSubcommand::Enum { pool } => {
137 let pool = pool_cvt(pool);
138 for i in 0.. {
139 match kvp
140 .call_failable(
141 hyperv_ic_resources::kvp::KvpRpc::Enumerate,
142 hyperv_ic_resources::kvp::EnumerateParams { pool, index: i },
143 )
144 .await?
145 {
146 Some(v) => {
147 println!("{}: {}", v.key, DisplayValue(&v.value));
148 }
149 None => break,
150 }
151 }
152 }
153 KvpSubcommand::IpInfo { adapter_id } => {
154 let ip_info = kvp
155 .call_failable(
156 hyperv_ic_resources::kvp::KvpRpc::GetIpInfo,
157 hyperv_ic_resources::kvp::GetIpInfoParams { adapter_id },
158 )
159 .await?;
160 let hyperv_ic_resources::kvp::IpInfo {
161 ipv4,
162 ipv6,
163 dhcp_enabled,
164 ipv4_addresses,
165 ipv6_addresses,
166 ipv4_gateways,
167 ipv6_gateways,
168 ipv4_dns_servers,
169 ipv6_dns_servers,
170 } = ip_info;
171 if dhcp_enabled {
172 println!("DHCP enabled");
173 } else {
174 println!("DHCP disabled");
175 }
176 let origin_str = |origin| match origin {
177 hyperv_ic_resources::kvp::AddressOrigin::Unknown => "unknown",
178 hyperv_ic_resources::kvp::AddressOrigin::Other => "other",
179 hyperv_ic_resources::kvp::AddressOrigin::Static => "static",
180 };
181 if ipv4 {
182 let a = Ipv4Addr::from;
183 println!("IPv4:");
184 for addr in ipv4_addresses {
185 println!(
186 " {}/{} (origin {})",
187 a(addr.address),
188 a(addr.subnet),
189 origin_str(addr.origin)
190 );
191 }
192 for gw in ipv4_gateways {
193 println!(" Gateway: {}", a(gw));
194 }
195 for dns in ipv4_dns_servers {
196 println!(" DNS: {}", a(dns));
197 }
198 }
199 if ipv6 {
200 let a = Ipv6Addr::from;
201 println!("IPv6:");
202 for addr in ipv6_addresses {
203 println!(
204 " {}/{} (origin {})",
205 a(addr.address),
206 addr.subnet,
207 origin_str(addr.origin)
208 );
209 }
210 for gw in ipv6_gateways {
211 println!(" Gateway: {}", a(gw));
212 }
213 for dns in ipv6_dns_servers {
214 println!(" DNS: {}", a(dns));
215 }
216 }
217 }
218 }
219 Ok(())
220}
221
222fn pool_cvt(pool: KvpPool) -> hyperv_ic_resources::kvp::KvpPool {
223 match pool {
224 KvpPool::Guest => hyperv_ic_resources::kvp::KvpPool::Guest,
225 KvpPool::External => hyperv_ic_resources::kvp::KvpPool::External,
226 KvpPool::Auto => hyperv_ic_resources::kvp::KvpPool::Auto,
227 KvpPool::AutoExternal => hyperv_ic_resources::kvp::KvpPool::AutoExternal,
228 }
229}
230
231struct DisplayValue<'a>(&'a hyperv_ic_resources::kvp::Value);
232
233impl std::fmt::Display for DisplayValue<'_> {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 match self.0 {
236 hyperv_ic_resources::kvp::Value::String(s) => write!(f, "\"{s}\""),
237 hyperv_ic_resources::kvp::Value::U32(v) => write!(f, "{v}"),
238 hyperv_ic_resources::kvp::Value::U64(v) => write!(f, "{v}"),
239 }
240 }
241}