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 Ok(())
53}
54
55static KLOG: crate::sync::SpinLock<KernelLogBuffer> =
56 crate::sync::SpinLock::new(KernelLogBuffer::new());
57
58const KLOG_CAPACITY: usize = 256;
59
60struct KernelLogBuffer {
61 entries: [KlogEntry; KLOG_CAPACITY],
62 head: usize,
63 count: usize,
64}
65
66#[derive(Clone, Copy)]
67struct KlogEntry {
68 tick: u64,
69 len: u8,
70 data: [u8; 120],
71}
72
73impl KlogEntry {
74 const fn empty() -> Self {
75 Self {
76 tick: 0,
77 len: 0,
78 data: [0; 120],
79 }
80 }
81}
82
83impl KernelLogBuffer {
84 const fn new() -> Self {
85 Self {
86 entries: [KlogEntry::empty(); KLOG_CAPACITY],
87 head: 0,
88 count: 0,
89 }
90 }
91
92 fn push(&mut self, msg: &str) {
93 let tick = crate::process::scheduler::ticks();
94 let bytes = msg.as_bytes();
95 let copy_len = core::cmp::min(bytes.len(), 120);
96 let idx = (self.head + self.count) % KLOG_CAPACITY;
97 if self.count < KLOG_CAPACITY {
98 self.count += 1;
99 } else {
100 self.head = (self.head + 1) % KLOG_CAPACITY;
101 }
102 self.entries[idx].tick = tick;
103 self.entries[idx].len = copy_len as u8;
104 self.entries[idx].data[..copy_len].copy_from_slice(&bytes[..copy_len]);
105 }
106
107 fn iter(&self) -> impl Iterator<Item = &KlogEntry> {
108 let h = self.head;
109 let c = self.count;
110 (0..c).map(move |i| &self.entries[(h + i) % KLOG_CAPACITY])
111 }
112}
113
114pub fn klog_write(msg: &str) {
115 KLOG.lock().push(msg);
116}
117
118pub(super) fn cmd_dmesg_impl(args: &[String]) -> Result<(), ShellError> {
119 let limit: usize = if !args.is_empty() {
120 args[0].parse().unwrap_or(50)
121 } else {
122 50
123 };
124
125 let log = KLOG.lock();
126 let entries: alloc::vec::Vec<_> = log.iter().collect();
127 let start = if entries.len() > limit {
128 entries.len() - limit
129 } else {
130 0
131 };
132 let hz = crate::arch::x86_64::timer::TIMER_HZ;
133
134 if entries.is_empty() {
135 shell_println!("(kernel log empty)");
136 return Ok(());
137 }
138
139 for entry in &entries[start..] {
140 let secs = entry.tick / hz;
141 let cs = (entry.tick % hz) * 100 / hz;
142 let text = core::str::from_utf8(&entry.data[..entry.len as usize]).unwrap_or("???");
143 shell_println!("[{:>6}.{:02}] {}", secs, cs, text);
144 }
145 Ok(())
146}
147
148pub(super) fn cmd_echo_impl(args: &[String]) -> Result<(), ShellError> {
149 let mut first = true;
150 for arg in args {
151 if !first {
152 crate::shell_print!(" ");
153 }
154 crate::shell_print!("{}", arg);
155 first = false;
156 }
157 shell_println!("");
158 Ok(())
159}
160
161pub(super) fn cmd_whoami_impl(_args: &[String]) -> Result<(), ShellError> {
162 if let Some(label) = crate::silo::current_task_silo_label() {
163 shell_println!("silo: {}", label);
164 } else {
165 shell_println!("silo: kernel (no silo context)");
166 }
167
168 if let Some(task) = crate::process::current_task_clone() {
169 shell_println!("task: {} (pid={}, tid={})", task.name, task.pid, task.tid);
170 }
171
172 Ok(())
173}
174
175pub(super) fn cmd_grep_impl(args: &[String]) -> Result<(), ShellError> {
182 if args.is_empty() {
183 shell_println!("Usage: grep <pattern> [path]");
184 return Err(ShellError::InvalidArguments);
185 }
186 let pattern = args[0].as_str();
187
188 let (data, label) = if let Some(piped) = crate::shell::output::take_pipe_input() {
189 (piped, String::from("(pipe)"))
190 } else if args.len() >= 2 {
191 let path = args[1].as_str();
192 let fd = vfs::open(path, vfs::OpenFlags::READ).map_err(|_| {
193 shell_println!("grep: cannot open '{}'", path);
194 ShellError::ExecutionFailed
195 })?;
196 let d = match vfs::read_all(fd) {
197 Ok(d) => d,
198 Err(_) => {
199 let _ = vfs::close(fd);
200 shell_println!("grep: cannot read '{}'", path);
201 return Err(ShellError::ExecutionFailed);
202 }
203 };
204 let _ = vfs::close(fd);
205 (d, String::from(path))
206 } else {
207 shell_println!("Usage: grep <pattern> <path>");
208 return Err(ShellError::InvalidArguments);
209 };
210
211 let text = core::str::from_utf8(&data).unwrap_or("");
212 let mut found = 0u32;
213 for line in text.split('\n') {
214 if line.contains(pattern) {
215 shell_println!("{}", line);
216 found += 1;
217 }
218 }
219 if found == 0 {
220 shell_println!("(no match in {})", label);
221 }
222 Ok(())
223}