1use async_trait::async_trait;
14use fuchsia_inspect::{LazyNode, Node};
15use futures::FutureExt;
16use std::collections::hash_map::HashMap;
17use std::sync::{Arc, Mutex, Weak};
18
19const INFO_NODE_NAME: &'static str = "fs.info";
20const USAGE_NODE_NAME: &'static str = "fs.usage";
21const VOLUMES_NODE_NAME: &'static str = "fs.volumes";
22
23#[async_trait]
28pub trait FsInspect {
29 fn get_info_data(&self) -> InfoData;
30 async fn get_usage_data(&self) -> UsageData;
31}
32
33#[async_trait]
35pub trait FsInspectVolume {
36 async fn get_volume_data(&self) -> VolumeData;
37}
38
39pub struct FsInspectTree {
42 _info: LazyNode,
43 _usage: LazyNode,
44 _volumes: LazyNode,
45 volumes_tracker: Arc<Mutex<HashMap<String, Weak<dyn FsInspectVolume + Send + Sync + 'static>>>>,
46}
47
48impl FsInspectTree {
49 pub fn new(fs: Weak<dyn FsInspect + Send + Sync + 'static>, root: &Node) -> FsInspectTree {
52 let fs_clone = fs.clone();
53 let info_node = root.create_lazy_child(INFO_NODE_NAME, move || {
54 let fs_clone = fs_clone.clone();
55 async move {
56 let inspector = fuchsia_inspect::Inspector::default();
57 if let Some(fs) = fs_clone.upgrade() {
58 fs.get_info_data().record_into(inspector.root());
59 }
60 Ok(inspector)
61 }
62 .boxed()
63 });
64
65 let fs_clone = fs.clone();
66 let usage_node = root.create_lazy_child(USAGE_NODE_NAME, move || {
67 let fs_clone = fs_clone.clone();
68 async move {
69 let inspector = fuchsia_inspect::Inspector::default();
70 if let Some(fs) = fs_clone.upgrade() {
71 fs.get_usage_data().await.record_into(inspector.root());
72 }
73 Ok(inspector)
74 }
75 .boxed()
76 });
77
78 let volumes_tracker = Arc::new(Mutex::new(HashMap::<
79 String,
80 Weak<dyn FsInspectVolume + Send + Sync + 'static>,
81 >::new()));
82 let tracker_weak = Arc::downgrade(&volumes_tracker);
83 let volumes_node = root.create_lazy_child(VOLUMES_NODE_NAME, move || {
84 let tracker_ref = tracker_weak.clone();
85 async move {
86 let inspector = fuchsia_inspect::Inspector::default();
87 let root = inspector.root();
88 let tracker = match tracker_ref.upgrade() {
89 Some(tracker) => tracker,
90 None => return Ok(inspector),
93 };
94 let volumes = {
95 let tracker = tracker.lock().unwrap();
96 let mut volumes = Vec::with_capacity(tracker.len());
97 for (name, volume) in tracker.iter() {
98 volumes.push((name.clone(), volume.clone()));
99 }
100 volumes
101 };
102 for (name, volume_weak) in volumes {
103 let volume = match volume_weak.upgrade() {
104 Some(v) => v,
105 None => continue,
106 };
107 let child = root.create_child(name.clone());
108 volume.get_volume_data().await.record_into(&child);
109 root.record(child);
110 }
111 Ok(inspector)
112 }
113 .boxed()
114 });
115
116 FsInspectTree {
117 _info: info_node,
118 _usage: usage_node,
119 _volumes: volumes_node,
120 volumes_tracker,
121 }
122 }
123
124 pub fn register_volume(
127 self: &Arc<Self>,
128 name: String,
129 volume: Weak<dyn FsInspectVolume + Send + Sync + 'static>,
130 ) {
131 self.volumes_tracker.lock().unwrap().insert(name, volume);
132 }
133
134 pub fn unregister_volume(&self, name: String) {
135 self.volumes_tracker.lock().unwrap().remove(&name);
136 }
137}
138
139pub struct InfoData {
141 pub id: u64,
142 pub fs_type: u64,
143 pub name: String,
144 pub version_major: u64,
145 pub version_minor: u64,
146 pub block_size: u64,
147 pub max_filename_length: u64,
148 pub oldest_version: Option<String>,
149}
150
151impl InfoData {
152 fn record_into(self, node: &Node) {
153 node.record_uint("id", self.id);
154 node.record_uint("type", self.fs_type);
155 node.record_string("name", self.name);
156 node.record_uint("version_major", self.version_major);
157 node.record_uint("version_minor", self.version_minor);
158 node.record_string(
159 "current_version",
160 format!("{}.{}", self.version_major, self.version_minor),
161 );
162 node.record_uint("block_size", self.block_size);
163 node.record_uint("max_filename_length", self.max_filename_length);
164 if self.oldest_version.is_some() {
165 node.record_string("oldest_version", self.oldest_version.as_ref().unwrap());
166 }
167 }
168}
169
170pub struct UsageData {
172 pub total_bytes: u64,
173 pub used_bytes: u64,
174 pub total_nodes: u64,
175 pub used_nodes: u64,
176}
177
178impl UsageData {
179 fn record_into(self, node: &Node) {
180 node.record_uint("total_bytes", self.total_bytes);
181 node.record_uint("used_bytes", self.used_bytes);
182 node.record_uint("total_nodes", self.total_nodes);
183 node.record_uint("used_nodes", self.used_nodes);
184 }
185}
186
187pub struct VolumeData {
189 pub used_bytes: u64,
190 pub bytes_limit: Option<u64>,
191 pub used_nodes: u64,
192 pub encrypted: bool,
193 pub port_koid: u64,
195}
196
197impl VolumeData {
198 fn record_into(self, node: &Node) {
199 node.record_uint("used_bytes", self.used_bytes);
200 if let Some(bytes_limit) = self.bytes_limit {
201 node.record_uint("bytes_limit", bytes_limit);
202 }
203 node.record_uint("used_nodes", self.used_nodes);
204 node.record_bool("encrypted", self.encrypted);
205 node.record_uint("port_koid", self.port_koid);
206 }
207}