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