1use crate::sync::SpinLock;
8use alloc::{string::String, vec::Vec};
9
10const AUDIT_CAPACITY: usize = 512;
11
12#[derive(Debug, Clone, Copy)]
14pub enum AuditCategory {
15 Silo,
17 Capability,
19 Syscall,
21 Process,
23 Security,
25}
26
27#[derive(Clone)]
29pub struct AuditEntry {
30 pub seq: u64,
32 pub tick: u64,
34 pub pid: u32,
36 pub silo_id: u32,
38 pub category: AuditCategory,
40 pub message: String,
42}
43
44struct AuditLog {
45 entries: [Option<AuditEntry>; AUDIT_CAPACITY],
46 head: usize,
47 count: usize,
48 next_seq: u64,
49}
50
51impl AuditLog {
52 const fn new() -> Self {
53 const NONE: Option<AuditEntry> = None;
54 Self {
55 entries: [NONE; AUDIT_CAPACITY],
56 head: 0,
57 count: 0,
58 next_seq: 1,
59 }
60 }
61
62 fn push(&mut self, category: AuditCategory, pid: u32, silo_id: u32, message: String) {
63 let tick = crate::process::scheduler::ticks();
64 let seq = self.next_seq;
65 self.next_seq += 1;
66
67 let entry = AuditEntry {
68 seq,
69 tick,
70 pid,
71 silo_id,
72 category,
73 message,
74 };
75 let idx = (self.head + self.count) % AUDIT_CAPACITY;
76 self.entries[idx] = Some(entry);
77 if self.count < AUDIT_CAPACITY {
78 self.count += 1;
79 } else {
80 self.head = (self.head + 1) % AUDIT_CAPACITY;
81 }
82 }
83
84 fn entries_newest(&self, n: usize) -> Vec<AuditEntry> {
85 let take = n.min(self.count);
86 let mut out = Vec::with_capacity(take);
87 let start = if self.count > take {
88 (self.head + self.count - take) % AUDIT_CAPACITY
89 } else {
90 self.head
91 };
92 for i in 0..take {
93 let idx = (start + i) % AUDIT_CAPACITY;
94 if let Some(e) = &self.entries[idx] {
95 out.push(e.clone());
96 }
97 }
98 out
99 }
100}
101
102static AUDIT: SpinLock<AuditLog> = SpinLock::new(AuditLog::new());
103
104pub fn log(category: AuditCategory, pid: u32, silo_id: u32, message: String) {
109 AUDIT.lock().push(category, pid, silo_id, message);
110}
111
112pub fn recent(n: usize) -> Vec<AuditEntry> {
114 AUDIT.lock().entries_newest(n)
115}
116
117pub fn total_count() -> u64 {
119 AUDIT.lock().next_seq - 1
120}