1use crate::SimpleScsiDisk;
7use crate::scsidvd::SimpleScsiDvd;
8use anyhow::Context;
9use async_trait::async_trait;
10use disk_backend::resolve::ResolveDiskParameters;
11use futures::StreamExt;
12use pal_async::task::Spawn;
13use scsi_core::ResolveScsiDeviceHandleParams;
14use scsi_core::ResolvedScsiDevice;
15use scsidisk_resources::SimpleScsiDiskHandle;
16use scsidisk_resources::SimpleScsiDvdHandle;
17use scsidisk_resources::SimpleScsiDvdRequest;
18use std::sync::Arc;
19use std::sync::Weak;
20use thiserror::Error;
21use vm_resource::AsyncResolveResource;
22use vm_resource::ResolveError;
23use vm_resource::ResourceResolver;
24use vm_resource::declare_static_async_resolver;
25use vm_resource::kind::ScsiDeviceHandleKind;
26
27pub struct SimpleScsiResolver;
29
30declare_static_async_resolver! {
31 SimpleScsiResolver,
32 (ScsiDeviceHandleKind, SimpleScsiDiskHandle),
33 (ScsiDeviceHandleKind, SimpleScsiDvdHandle),
34}
35
36#[derive(Debug, Error)]
37pub enum Error {
38 #[error("failed to resolve backing disk")]
39 Disk(#[source] ResolveError),
40}
41
42#[async_trait]
43impl AsyncResolveResource<ScsiDeviceHandleKind, SimpleScsiDiskHandle> for SimpleScsiResolver {
44 type Output = ResolvedScsiDevice;
45 type Error = Error;
46
47 async fn resolve(
48 &self,
49 resolver: &ResourceResolver,
50 resource: SimpleScsiDiskHandle,
51 _: ResolveScsiDeviceHandleParams<'_>,
52 ) -> Result<Self::Output, Self::Error> {
53 let disk = resolver
54 .resolve(
55 resource.disk,
56 ResolveDiskParameters {
57 read_only: resource.read_only,
58 _async_trait_workaround: &(),
59 },
60 )
61 .await
62 .map_err(Error::Disk)?;
63
64 let disk = SimpleScsiDisk::new(disk.0, resource.parameters);
65 Ok(disk.into())
66 }
67}
68
69#[async_trait]
70impl AsyncResolveResource<ScsiDeviceHandleKind, SimpleScsiDvdHandle> for SimpleScsiResolver {
71 type Output = ResolvedScsiDevice;
72 type Error = Error;
73
74 async fn resolve(
75 &self,
76 resolver: &ResourceResolver,
77 resource: SimpleScsiDvdHandle,
78 input: ResolveScsiDeviceHandleParams<'_>,
79 ) -> Result<Self::Output, Self::Error> {
80 let media = if let Some(media) = resource.media {
81 Some(
82 resolver
83 .resolve(
84 media,
85 ResolveDiskParameters {
86 read_only: true,
87 _async_trait_workaround: &(),
88 },
89 )
90 .await
91 .map_err(Error::Disk)?
92 .0,
93 )
94 } else {
95 None
96 };
97 let dvd = Arc::new(SimpleScsiDvd::new(media));
98
99 if let Some(requests) = resource.requests {
101 input
102 .driver_source
103 .simple()
104 .spawn(
105 "dvd-requests",
106 handle_dvd_requests(Arc::downgrade(&dvd), resolver.clone(), requests),
107 )
108 .detach();
109 }
110
111 Ok(ResolvedScsiDevice(dvd))
112 }
113}
114
115async fn handle_dvd_requests(
116 dvd: Weak<SimpleScsiDvd>,
117 resolver: ResourceResolver,
118 mut requests: mesh::Receiver<SimpleScsiDvdRequest>,
119) {
120 while let Some(req) = requests.next().await {
121 match req {
122 SimpleScsiDvdRequest::ChangeMedia(rpc) => {
123 rpc.handle_failable(async |resource| {
124 let media = if let Some(resource) = resource {
125 Some(
126 resolver
127 .resolve(
128 resource,
129 ResolveDiskParameters {
130 read_only: true,
131 _async_trait_workaround: &(),
132 },
133 )
134 .await
135 .context("failed to resolve media")?
136 .0,
137 )
138 } else {
139 None
140 };
141 if let Some(dvd) = dvd.upgrade() {
142 dvd.change_media(media);
143 }
144 anyhow::Ok(())
145 })
146 .await
147 }
148 }
149 }
150}