strat9_kernel/shell/commands/util/
mod.rs1mod audit;
3mod date;
4mod dmesg;
5mod echo;
6mod env;
7mod grep;
8mod ntpdate;
9mod uptime;
10mod watch;
11mod whoami;
12
13use crate::{
14 shell::{is_interrupted, ShellError},
15 shell_println, vfs,
16};
17use alloc::string::String;
18
19pub use audit::cmd_audit;
20pub use date::cmd_date;
21pub use dmesg::cmd_dmesg;
22pub use echo::cmd_echo;
23pub use env::{
24 cmd_env, cmd_setenv, cmd_unsetenv, init_shell_env, shell_getenv, shell_setenv, shell_unsetenv,
25};
26pub use grep::cmd_grep;
27pub use ntpdate::cmd_ntpdate;
28pub use uptime::cmd_uptime;
29pub use watch::cmd_watch;
30pub use whoami::cmd_whoami;
31
32pub(super) fn cmd_uptime_impl(_args: &[String]) -> Result<(), ShellError> {
33 let ticks = crate::process::scheduler::ticks();
34 let hz = crate::arch::x86_64::timer::TIMER_HZ;
35 let total_secs = ticks / hz;
36 let hours = total_secs / 3600;
37 let minutes = (total_secs % 3600) / 60;
38 let secs = total_secs % 60;
39
40 let task_count = crate::process::get_all_tasks()
41 .map(|t| t.len())
42 .unwrap_or(0);
43 let silos = crate::silo::list_silos_snapshot().len();
44
45 shell_println!(
46 "up {:02}:{:02}:{:02} ({} ticks @ {} Hz) {} tasks, {} silos",
47 hours,
48 minutes,
49 secs,
50 ticks,
51 hz,
52 task_count,
53 silos
54 );
55
56 let tsc_khz = crate::arch::x86_64::boot_timestamp::tsc_khz();
58 let stats = crate::process::scheduler::perf_counters::snapshot();
59 shell_println!(
60 "perf: {}",
61 stats
62 .iter()
63 .map(|s| {
64 let avg = s.avg_us(tsc_khz);
65 alloc::format!("{} avg={}us ({})", s.name, avg, s.count)
66 })
67 .collect::<alloc::vec::Vec<_>>()
68 .join(" ")
69 );
70
71 Ok(())
72}
73
74static KLOG: crate::sync::SpinLock<KernelLogBuffer> =
75 crate::sync::SpinLock::new(KernelLogBuffer::new());
76
77const KLOG_CAPACITY: usize = 256;
78
79struct KernelLogBuffer {
80 entries: [KlogEntry; KLOG_CAPACITY],
81 head: usize,
82 count: usize,
83}
84
85#[derive(Clone, Copy)]
86struct KlogEntry {
87 tick: u64,
88 len: u8,
89 data: [u8; 120],
90}
91
92impl KlogEntry {
93 const fn empty() -> Self {
94 Self {
95 tick: 0,
96 len: 0,
97 data: [0; 120],
98 }
99 }
100}
101
102impl KernelLogBuffer {
103 const fn new() -> Self {
104 Self {
105 entries: [KlogEntry::empty(); KLOG_CAPACITY],
106 head: 0,
107 count: 0,
108 }
109 }
110
111 fn push(&mut self, msg: &str) {
112 let tick = crate::process::scheduler::ticks();
113 let bytes = msg.as_bytes();
114 let copy_len = core::cmp::min(bytes.len(), 120);
115 let idx = (self.head + self.count) % KLOG_CAPACITY;
116 if self.count < KLOG_CAPACITY {
117 self.count += 1;
118 } else {
119 self.head = (self.head + 1) % KLOG_CAPACITY;
120 }
121 self.entries[idx].tick = tick;
122 self.entries[idx].len = copy_len as u8;
123 self.entries[idx].data[..copy_len].copy_from_slice(&bytes[..copy_len]);
124 }
125
126 fn iter(&self) -> impl Iterator<Item = &KlogEntry> {
127 let h = self.head;
128 let c = self.count;
129 (0..c).map(move |i| &self.entries[(h + i) % KLOG_CAPACITY])
130 }
131}
132
133pub fn klog_write(msg: &str) {
134 KLOG.lock().push(msg);
135}
136
137pub(super) fn cmd_dmesg_impl(args: &[String]) -> Result<(), ShellError> {
138 let limit: usize = if !args.is_empty() {
139 args[0].parse().unwrap_or(50)
140 } else {
141 50
142 };
143
144 let log = KLOG.lock();
145 let entries: alloc::vec::Vec<_> = log.iter().collect();
146 let start = if entries.len() > limit {
147 entries.len() - limit
148 } else {
149 0
150 };
151 let hz = crate::arch::x86_64::timer::TIMER_HZ;
152
153 if entries.is_empty() {
154 shell_println!("(kernel log empty)");
155 return Ok(());
156 }
157
158 for entry in &entries[start..] {
159 let secs = entry.tick / hz;
160 let cs = (entry.tick % hz) * 100 / hz;
161 let text = core::str::from_utf8(&entry.data[..entry.len as usize]).unwrap_or("???");
162 shell_println!("[{:>6}.{:02}] {}", secs, cs, text);
163 }
164 Ok(())
165}
166
167pub(super) fn cmd_echo_impl(args: &[String]) -> Result<(), ShellError> {
168 let mut first = true;
169 for arg in args {
170 if !first {
171 crate::shell_print!(" ");
172 }
173 crate::shell_print!("{}", arg);
174 first = false;
175 }
176 shell_println!("");
177 Ok(())
178}
179
180pub(super) fn cmd_whoami_impl(_args: &[String]) -> Result<(), ShellError> {
181 if let Some(label) = crate::silo::current_task_silo_label() {
182 shell_println!("silo: {}", label);
183 } else {
184 shell_println!("silo: kernel (no silo context)");
185 }
186
187 if let Some(task) = crate::process::current_task_clone() {
188 shell_println!("task: {} (pid={}, tid={})", task.name, task.pid, task.tid);
189 }
190
191 Ok(())
192}
193
194pub(super) fn cmd_grep_impl(args: &[String]) -> Result<(), ShellError> {
201 if args.is_empty() {
202 shell_println!("Usage: grep <pattern> [path]");
203 return Err(ShellError::InvalidArguments);
204 }
205 let pattern = args[0].as_str();
206
207 let (data, label) = if let Some(piped) = crate::shell::output::take_pipe_input() {
208 (piped, String::from("(pipe)"))
209 } else if args.len() >= 2 {
210 let path = args[1].as_str();
211 let fd = vfs::open(path, vfs::OpenFlags::READ).map_err(|_| {
212 shell_println!("grep: cannot open '{}'", path);
213 ShellError::ExecutionFailed
214 })?;
215 let d = match vfs::read_all(fd) {
216 Ok(d) => d,
217 Err(_) => {
218 let _ = vfs::close(fd);
219 shell_println!("grep: cannot read '{}'", path);
220 return Err(ShellError::ExecutionFailed);
221 }
222 };
223 let _ = vfs::close(fd);
224 (d, String::from(path))
225 } else {
226 shell_println!("Usage: grep <pattern> <path>");
227 return Err(ShellError::InvalidArguments);
228 };
229
230 let text = core::str::from_utf8(&data).unwrap_or("");
231 let mut found = 0u32;
232 for line in text.split('\n') {
233 if is_interrupted() {
234 shell_println!("(grep cancelled after {} matches)", found);
235 return Ok(());
236 }
237 if line.contains(pattern) {
238 shell_println!("{}", line);
239 found += 1;
240 }
241 }
242 if found == 0 {
243 shell_println!("(no match in {})", label);
244 }
245 Ok(())
246}