1use std::time::Instant;
2
3use clap::Parser;
4use miette::IntoDiagnostic;
5use naming::{GetFlags, dynamic_naming_factory};
6use secgate::TwzError;
7use tracing::Level;
8use twizzler::object::{MapFlags, ObjID};
9
10#[secgate::gatecall]
11pub fn hold(id: ObjID, flags: MapFlags) -> Result<bool, TwzError> {}
12#[secgate::gatecall]
13pub fn drop(id: ObjID, flags: MapFlags) -> Result<bool, TwzError> {}
14#[secgate::gatecall]
15pub fn preload(id: ObjID) -> Result<(), TwzError> {}
16#[secgate::gatecall]
17pub fn stat(_id: ObjID) -> Result<(), TwzError> {}
18#[secgate::gatecall]
19pub fn list_nth(nth: u64) -> Result<Option<CachedStats>, TwzError> {}
20
21#[derive(Debug, Clone, Copy)]
22#[repr(C)]
23pub struct CachedStats {
24 pub id: ObjID,
25 pub start: Instant,
26 pub flags: MapFlags,
27 pub addr: u64,
28}
29
30#[derive(clap::Args, Clone, Debug)]
31struct TrailingArgs {
32 #[arg(
33 help("List of names or object IDs"),
34 trailing_var_arg = true,
35 allow_hyphen_values = true
36 )]
37 args: Vec<String>,
38}
39
40#[derive(clap::Subcommand, Clone, Debug)]
41enum Command {
42 #[clap(about = "Hold an object.")]
43 Hold(TrailingArgs),
44 #[clap(about = "Drop holds on an object.")]
45 Drop(TrailingArgs),
46 #[clap(about = "Preload an object.")]
47 Preload(TrailingArgs),
48 #[clap(about = "Print stats about an object.")]
49 Stat(TrailingArgs),
50 #[clap(about = "List held objects.")]
51 List,
52}
53
54#[derive(clap::Parser, Clone)]
55struct Args {
56 #[clap(subcommand)]
57 cmd: Command,
58}
59
60fn do_hold(id: ObjID) -> twizzler::Result<()> {
61 tracing::info!("do hold: {}", id);
62 hold(id, MapFlags::READ)?;
63 hold(id, MapFlags::READ | MapFlags::EXEC)?;
64 hold(id, MapFlags::READ | MapFlags::NO_NULLPAGE)?;
65 hold(id, MapFlags::READ | MapFlags::WRITE)?;
66 hold(id, MapFlags::READ | MapFlags::WRITE | MapFlags::PERSIST)?;
67 Ok(())
68}
69
70fn do_drop(id: ObjID) -> twizzler::Result<()> {
71 tracing::info!("do drop: {}", id);
72 drop(id, MapFlags::READ)?;
73 drop(id, MapFlags::READ | MapFlags::EXEC)?;
74 drop(id, MapFlags::READ | MapFlags::NO_NULLPAGE)?;
75 drop(id, MapFlags::READ | MapFlags::WRITE)?;
76 drop(id, MapFlags::READ | MapFlags::WRITE | MapFlags::PERSIST)?;
77 Ok(())
78}
79
80fn do_preload(id: ObjID) -> twizzler::Result<()> {
81 tracing::info!("do preload: {}", id);
82 preload(id)
83}
84
85fn do_stat(id: ObjID) -> twizzler::Result<()> {
86 tracing::info!("do stat: {}", id);
87 stat(id)
88}
89
90fn per_arg(arg: &str, cb: fn(ObjID) -> twizzler::Result<()>) -> twizzler::Result<()> {
91 if let Ok(id) = u128::from_str_radix(arg, 16) {
92 match cb(id.into()) {
93 Ok(_) => return twizzler::Result::Ok(()),
94 Err(e) => tracing::debug!(
95 "failed to operate on parsed object ID {} ({}), trying again with name",
96 e,
97 id
98 ),
99 }
100 }
101 match dynamic_naming_factory()
102 .unwrap()
103 .get(arg, GetFlags::FOLLOW_SYMLINK)
104 {
105 Err(e) => {
106 tracing::warn!("could not resolve {}: {}", arg, e);
107 }
108 Ok(ns) => match ns.kind {
109 naming::NsNodeKind::Namespace => {
110 tracing::debug!("enumerating directory {}", arg);
111 match dynamic_naming_factory()
112 .unwrap()
113 .enumerate_names_nsid(ns.id, 0, usize::MAX)
114 {
115 Ok(nodes) => {
116 for node in nodes {
117 if let Ok(name) = node.name() {
118 if name != "." && name != ".." {
119 let name = format!("{}/{}", arg, name);
120 let _ = per_arg(name.as_str(), cb);
121 }
122 }
124 }
125 }
126 Err(e) => {
127 tracing::warn!("could not enumerate directory {}: {}", arg, e);
128 }
129 }
130 }
131 naming::NsNodeKind::Object => {
132 if let Err(e) = cb(ns.id) {
133 tracing::warn!("could not operate on {}: {}", arg, e);
134 }
135 }
136 naming::NsNodeKind::SymLink => {
137 tracing::warn!("cannot hold / preload symlinks")
138 }
139 },
140 }
141 Ok(())
142}
143
144fn main() -> miette::Result<()> {
145 tracing::subscriber::set_global_default(
146 tracing_subscriber::fmt()
147 .with_max_level(Level::DEBUG)
148 .without_time()
149 .finish(),
150 )
151 .unwrap();
152
153 let args = Args::try_parse().into_diagnostic()?;
154
155 let mut errs = false;
156 match args.cmd {
157 Command::Hold(args) => {
158 for arg in args.args {
159 if per_arg(&arg, do_hold).is_err() {
160 errs = true;
161 }
162 }
163 }
164 Command::Drop(args) => {
165 for arg in args.args {
166 if per_arg(&arg, do_drop).is_err() {
167 errs = true;
168 }
169 }
170 }
171 Command::Preload(args) => {
172 for arg in args.args {
173 if per_arg(&arg, do_preload).is_err() {
174 errs = true;
175 }
176 }
177 }
178 Command::Stat(args) => {
179 for arg in args.args {
180 if per_arg(&arg, do_stat).is_err() {
181 errs = true;
182 }
183 }
184 }
185 Command::List => {
186 let mut i = 0;
187 while let Some(info) = list_nth(i).into_diagnostic()? {
188 println!(
189 "{} {:?} {:x} {} seconds old",
190 info.id,
191 info.flags,
192 info.addr,
193 (Instant::now() - info.start).as_secs_f32()
194 );
195 i += 1;
196 }
197 }
198 }
199
200 if errs {
201 miette::bail!("errors occurred")
202 }
203 Ok(())
204}