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