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