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