⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/services/
process_server.rs

1//! Process Server Implementation
2//!
3//! Manages process lifecycle, resource tracking, and process enumeration.
4
5use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
6use core::sync::atomic::{AtomicU64, Ordering};
7
8use spin::RwLock;
9
10use crate::{
11    error::KernelError,
12    process::{ProcessId, ProcessPriority},
13};
14
15/// Resource limits for a process
16#[derive(Debug, Clone)]
17pub struct ResourceLimits {
18    pub max_memory: u64,   // Maximum memory in bytes
19    pub max_cpu_time: u64, // Maximum CPU time in microseconds
20    pub max_files: u32,    // Maximum open files
21    pub max_threads: u32,  // Maximum threads
22    pub nice_value: i8,    // Process nice value (-20 to 19)
23    pub stack_size: u64,   // Stack size limit
24    pub core_size: u64,    // Core dump size limit
25}
26
27impl Default for ResourceLimits {
28    fn default() -> Self {
29        Self {
30            max_memory: 256 * 1024 * 1024, // 256 MB
31            max_cpu_time: u64::MAX,        // Unlimited
32            max_files: 1024,               // 1024 files
33            max_threads: 256,              // 256 threads
34            nice_value: 0,                 // Normal priority
35            stack_size: 8 * 1024 * 1024,   // 8 MB
36            core_size: 0,                  // No core dumps
37        }
38    }
39}
40
41/// Process information
42#[derive(Debug, Clone)]
43pub struct ProcessInfo {
44    pub pid: ProcessId,
45    pub ppid: ProcessId,
46    pub name: String,
47    pub state: ProcessState,
48    pub uid: u32,
49    pub gid: u32,
50    pub start_time: u64,
51    pub cpu_time: u64,
52    pub memory_usage: u64,
53    pub thread_count: u32,
54    pub priority: ProcessPriority,
55    pub exit_code: Option<i32>,
56    pub command_line: Vec<String>,
57    pub environment: Vec<String>,
58    pub working_directory: String,
59    pub session_id: u32,
60    pub terminal: Option<String>,
61}
62
63/// Process state
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum ProcessState {
66    Running,
67    Sleeping,
68    Waiting,
69    Stopped,
70    Zombie,
71    Dead,
72}
73
74/// Session information
75#[derive(Debug, Clone)]
76pub struct Session {
77    pub sid: u32,
78    pub leader: ProcessId,
79    pub terminal: Option<String>,
80    pub processes: Vec<ProcessId>,
81}
82
83/// Process group information
84#[derive(Debug, Clone)]
85pub struct ProcessGroup {
86    pub pgid: u32,
87    pub leader: ProcessId,
88    pub session_id: u32,
89    pub processes: Vec<ProcessId>,
90}
91
92/// Global process server instance
93pub struct ProcessServer {
94    /// All processes
95    processes: RwLock<BTreeMap<u64, ProcessInfo>>,
96
97    /// Resource limits per process
98    resource_limits: RwLock<BTreeMap<u64, ResourceLimits>>,
99
100    /// Sessions
101    sessions: RwLock<BTreeMap<u32, Session>>,
102
103    /// Process groups
104    process_groups: RwLock<BTreeMap<u32, ProcessGroup>>,
105
106    /// Next PID to allocate
107    next_pid: AtomicU64,
108
109    /// Next session ID
110    next_sid: AtomicU64,
111
112    /// Next process group ID
113    next_pgid: AtomicU64,
114
115    /// Orphaned processes waiting for reaping
116    orphans: RwLock<Vec<ProcessId>>,
117
118    /// Process accounting statistics
119    total_processes_created: AtomicU64,
120    total_processes_exited: AtomicU64,
121}
122
123impl ProcessServer {
124    /// Create a new process server
125    pub fn new() -> Self {
126        crate::println!("[PROCESS_SERVER] Creating new ProcessServer...");
127
128        crate::println!("[PROCESS_SERVER] Creating BTreeMaps...");
129        let processes = RwLock::new(BTreeMap::new());
130        let resource_limits = RwLock::new(BTreeMap::new());
131        let sessions = RwLock::new(BTreeMap::new());
132        let process_groups = RwLock::new(BTreeMap::new());
133
134        crate::println!("[PROCESS_SERVER] Creating Vec...");
135        let orphans = RwLock::new(Vec::new());
136
137        crate::println!("[PROCESS_SERVER] Creating atomics...");
138        let next_pid = AtomicU64::new(2); // Start at 2, PID 1 is init
139        let next_sid = AtomicU64::new(1);
140        let next_pgid = AtomicU64::new(1);
141        let total_processes_created = AtomicU64::new(0);
142        let total_processes_exited = AtomicU64::new(0);
143
144        crate::println!("[PROCESS_SERVER] Constructing ProcessServer...");
145        let server = Self {
146            processes,
147            resource_limits,
148            sessions,
149            process_groups,
150            next_pid,
151            next_sid,
152            next_pgid,
153            orphans,
154            total_processes_created,
155            total_processes_exited,
156        };
157
158        crate::println!("[PROCESS_SERVER] ProcessServer created successfully");
159        server
160    }
161}
162
163impl Default for ProcessServer {
164    fn default() -> Self {
165        Self::new()
166    }
167}
168
169impl ProcessServer {
170    /// Create a new process
171    pub fn create_process(
172        &self,
173        parent_pid: ProcessId,
174        name: String,
175        uid: u32,
176        gid: u32,
177        command_line: Vec<String>,
178        environment: Vec<String>,
179    ) -> Result<ProcessId, KernelError> {
180        let pid = ProcessId(self.next_pid.fetch_add(1, Ordering::SeqCst));
181
182        // Get parent's session and process group
183        let (session_id, pgid) = {
184            let processes = self.processes.read();
185            if let Some(parent) = processes.get(&parent_pid.0) {
186                (parent.session_id, parent.session_id) // Inherit from parent
187            } else {
188                (0, 0) // Init process
189            }
190        };
191
192        let info = ProcessInfo {
193            pid,
194            ppid: parent_pid,
195            name: name.clone(),
196            state: ProcessState::Running,
197            uid,
198            gid,
199            start_time: self.get_system_time(),
200            cpu_time: 0,
201            memory_usage: 0,
202            thread_count: 1,
203            priority: ProcessPriority::Normal,
204            exit_code: None,
205            command_line,
206            environment,
207            working_directory: String::from("/"),
208            session_id,
209            terminal: None,
210        };
211
212        // Set default resource limits
213        let limits = ResourceLimits::default();
214
215        // Add to process table
216        self.processes.write().insert(pid.0, info);
217        self.resource_limits.write().insert(pid.0, limits);
218
219        // Add to session's process list
220        if session_id > 0 {
221            if let Some(session) = self.sessions.write().get_mut(&session_id) {
222                session.processes.push(pid);
223            }
224        }
225
226        // Add to process group
227        if pgid > 0 {
228            if let Some(pg) = self.process_groups.write().get_mut(&pgid) {
229                pg.processes.push(pid);
230            }
231        }
232
233        self.total_processes_created.fetch_add(1, Ordering::Relaxed);
234
235        crate::println!("[PROCESS_SERVER] Created process {} ({})", pid.0, name);
236        Ok(pid)
237    }
238
239    /// Terminate a process
240    pub fn terminate_process(&self, pid: ProcessId, exit_code: i32) -> Result<(), KernelError> {
241        let mut processes = self.processes.write();
242
243        if let Some(process) = processes.get_mut(&pid.0) {
244            process.state = ProcessState::Zombie;
245            process.exit_code = Some(exit_code);
246
247            // Reparent children to init
248            let children: Vec<ProcessId> = processes
249                .values()
250                .filter(|p| p.ppid == pid)
251                .map(|p| p.pid)
252                .collect();
253
254            for child_pid in children {
255                if let Some(child) = processes.get_mut(&child_pid.0) {
256                    child.ppid = ProcessId(1); // Reparent to init
257
258                    // Add to orphan list if child is zombie
259                    if child.state == ProcessState::Zombie {
260                        self.orphans.write().push(child_pid);
261                    }
262                }
263            }
264
265            self.total_processes_exited.fetch_add(1, Ordering::Relaxed);
266
267            crate::println!(
268                "[PROCESS_SERVER] Process {} terminated with code {}",
269                pid.0,
270                exit_code
271            );
272            Ok(())
273        } else {
274            Err(KernelError::ProcessNotFound { pid: pid.0 })
275        }
276    }
277
278    /// Wait for a child process
279    pub fn wait_for_child(
280        &self,
281        parent_pid: ProcessId,
282        specific_pid: Option<ProcessId>,
283    ) -> Result<(ProcessId, i32), KernelError> {
284        let mut processes = self.processes.write();
285
286        // Find zombie children
287        let zombie_child = processes
288            .values()
289            .find(|p| {
290                p.ppid == parent_pid
291                    && p.state == ProcessState::Zombie
292                    && (specific_pid.is_none() || specific_pid == Some(p.pid))
293            })
294            .map(|p| (p.pid, p.exit_code.unwrap_or(0)));
295
296        if let Some((child_pid, exit_code)) = zombie_child {
297            // Reap the zombie
298            processes.remove(&child_pid.0);
299            self.resource_limits.write().remove(&child_pid.0);
300
301            // Remove from session and process group
302            self.remove_from_session_and_group(child_pid);
303
304            crate::println!("[PROCESS_SERVER] Reaped zombie process {}", child_pid.0);
305            Ok((child_pid, exit_code))
306        } else {
307            Err(KernelError::NotFound {
308                resource: "zombie children",
309                id: parent_pid.0,
310            })
311        }
312    }
313
314    /// Get process information
315    pub fn get_process_info(&self, pid: ProcessId) -> Option<ProcessInfo> {
316        self.processes.read().get(&pid.0).cloned()
317    }
318
319    /// List all processes
320    pub fn list_processes(&self) -> Vec<ProcessInfo> {
321        self.processes.read().values().cloned().collect()
322    }
323
324    /// Set resource limits for a process
325    pub fn set_resource_limits(
326        &self,
327        pid: ProcessId,
328        limits: ResourceLimits,
329    ) -> Result<(), KernelError> {
330        if self.processes.read().contains_key(&pid.0) {
331            self.resource_limits.write().insert(pid.0, limits);
332            Ok(())
333        } else {
334            Err(KernelError::ProcessNotFound { pid: pid.0 })
335        }
336    }
337
338    /// Get resource limits for a process
339    pub fn get_resource_limits(&self, pid: ProcessId) -> Option<ResourceLimits> {
340        self.resource_limits.read().get(&pid.0).cloned()
341    }
342
343    /// Create a new session
344    pub fn create_session(&self, leader_pid: ProcessId) -> Result<u32, KernelError> {
345        let sid = self.next_sid.fetch_add(1, Ordering::SeqCst) as u32;
346
347        let session = Session {
348            sid,
349            leader: leader_pid,
350            terminal: None,
351            processes: vec![leader_pid],
352        };
353
354        self.sessions.write().insert(sid, session);
355
356        // Update process's session ID
357        if let Some(process) = self.processes.write().get_mut(&leader_pid.0) {
358            process.session_id = sid;
359        }
360
361        Ok(sid)
362    }
363
364    /// Create a new process group
365    pub fn create_process_group(
366        &self,
367        leader_pid: ProcessId,
368        session_id: u32,
369    ) -> Result<u32, KernelError> {
370        let pgid = self.next_pgid.fetch_add(1, Ordering::SeqCst) as u32;
371
372        let pg = ProcessGroup {
373            pgid,
374            leader: leader_pid,
375            session_id,
376            processes: vec![leader_pid],
377        };
378
379        self.process_groups.write().insert(pgid, pg);
380        Ok(pgid)
381    }
382
383    /// Send signal to process
384    pub fn send_signal(&self, pid: ProcessId, signal: i32) -> Result<(), KernelError> {
385        if let Some(_process) = self.processes.read().get(&pid.0) {
386            match signal {
387                9 => {
388                    // SIGKILL
389                    self.terminate_process(pid, -signal)?;
390                }
391                15 => {
392                    // SIGTERM
393                    self.terminate_process(pid, 0)?;
394                }
395                19 => {
396                    // SIGSTOP
397                    if let Some(process) = self.processes.write().get_mut(&pid.0) {
398                        process.state = ProcessState::Stopped;
399                    }
400                }
401                18 => {
402                    // SIGCONT
403                    if let Some(process) = self.processes.write().get_mut(&pid.0) {
404                        if process.state == ProcessState::Stopped {
405                            process.state = ProcessState::Running;
406                        }
407                    }
408                }
409                _ => {
410                    // Handle other signals
411                    crate::println!(
412                        "[PROCESS_SERVER] Signal {} sent to process {}",
413                        signal,
414                        pid.0
415                    );
416                }
417            }
418            Ok(())
419        } else {
420            Err(KernelError::ProcessNotFound { pid: pid.0 })
421        }
422    }
423
424    /// Update process statistics
425    pub fn update_process_stats(&self, pid: ProcessId, cpu_time: u64, memory_usage: u64) {
426        if let Some(process) = self.processes.write().get_mut(&pid.0) {
427            process.cpu_time = cpu_time;
428            process.memory_usage = memory_usage;
429        }
430    }
431
432    /// Clean up zombie processes
433    pub fn reap_zombies(&self) -> usize {
434        let mut reaped = 0;
435        let mut processes = self.processes.write();
436        let mut to_remove = Vec::new();
437
438        // Find zombies whose parents are init or dead
439        for (pid, process) in processes.iter() {
440            if process.state == ProcessState::Zombie
441                && (process.ppid.0 == 1 || !processes.contains_key(&process.ppid.0))
442            {
443                to_remove.push(*pid);
444            }
445        }
446
447        // Remove zombies
448        for pid in to_remove {
449            processes.remove(&pid);
450            self.resource_limits.write().remove(&pid);
451            self.remove_from_session_and_group(ProcessId(pid));
452            reaped += 1;
453        }
454
455        if reaped > 0 {
456            crate::println!("[PROCESS_SERVER] Reaped {} zombie processes", reaped);
457        }
458
459        reaped
460    }
461
462    /// Get system statistics
463    pub fn get_statistics(&self) -> ProcessServerStats {
464        let processes = self.processes.read();
465        let mut running = 0;
466        let mut sleeping = 0;
467        let mut stopped = 0;
468        let mut zombies = 0;
469
470        for process in processes.values() {
471            match process.state {
472                ProcessState::Running => running += 1,
473                ProcessState::Sleeping | ProcessState::Waiting => sleeping += 1,
474                ProcessState::Stopped => stopped += 1,
475                ProcessState::Zombie => zombies += 1,
476                ProcessState::Dead => {}
477            }
478        }
479
480        ProcessServerStats {
481            total_processes: processes.len(),
482            running,
483            sleeping,
484            stopped,
485            zombies,
486            total_created: self.total_processes_created.load(Ordering::Relaxed),
487            total_exited: self.total_processes_exited.load(Ordering::Relaxed),
488            sessions: self.sessions.read().len(),
489            process_groups: self.process_groups.read().len(),
490        }
491    }
492
493    /// List all active process IDs
494    pub fn list_process_ids(&self) -> Vec<ProcessId> {
495        self.processes
496            .read()
497            .keys()
498            .map(|&pid| ProcessId(pid))
499            .collect()
500    }
501
502    /// Notify a process that a capability has been revoked
503    pub fn notify_capability_revoked(&self, _pid: ProcessId, _cap_id: u64) {
504        // Mark the capability as revoked in the process's capability space.
505        // In the current design, revocation is tracked globally in the
506        // RevocationList, so per-process notification is a best-effort signal
507        // that the process should re-validate its cached capabilities.
508        crate::security::audit::log_capability_op(_pid.0, _cap_id, 2); // 2 = revoke
509    }
510
511    // Helper functions
512
513    fn get_system_time(&self) -> u64 {
514        // Read the hardware timestamp counter and convert to approximate
515        // microseconds (assumes 2 GHz clock: 1 cycle = 0.5 ns, 2000 cycles = 1 us).
516        crate::arch::entropy::read_timestamp() / 2000
517    }
518
519    fn remove_from_session_and_group(&self, pid: ProcessId) {
520        // Remove from session
521        for session in self.sessions.write().values_mut() {
522            session.processes.retain(|&p| p != pid);
523        }
524
525        // Remove from process group
526        for pg in self.process_groups.write().values_mut() {
527            pg.processes.retain(|&p| p != pid);
528        }
529    }
530}
531
532/// Process server statistics
533#[derive(Debug)]
534pub struct ProcessServerStats {
535    pub total_processes: usize,
536    pub running: usize,
537    pub sleeping: usize,
538    pub stopped: usize,
539    pub zombies: usize,
540    pub total_created: u64,
541    pub total_exited: u64,
542    pub sessions: usize,
543    pub process_groups: usize,
544}
545
546/// Global process server instance using OnceLock for safe initialization.
547static PROCESS_SERVER: crate::sync::once_lock::OnceLock<ProcessServer> =
548    crate::sync::once_lock::OnceLock::new();
549
550/// Initialize the process server
551pub fn init() {
552    #[allow(unused_imports)]
553    use crate::println;
554
555    println!("[PROCESS_SERVER] Creating ProcessServer...");
556    let result = PROCESS_SERVER.set(ProcessServer::new());
557    if result.is_err() {
558        println!("[PROCESS_SERVER] WARNING: Already initialized! Skipping re-initialization.");
559        return;
560    }
561    println!("[PROCESS_SERVER] Process server initialized");
562}
563
564/// Try to get the global process server without panicking.
565///
566/// Returns `None` if the process server has not been initialized via [`init`].
567pub fn try_get_process_server() -> Option<&'static ProcessServer> {
568    PROCESS_SERVER.get()
569}
570
571/// Get the global process server.
572///
573/// Panics if the process server has not been initialized via [`init`].
574/// Prefer [`try_get_process_server`] in contexts where a panic is unacceptable.
575pub fn get_process_server() -> &'static ProcessServer {
576    PROCESS_SERVER
577        .get()
578        .expect("Process server not initialized: init() was not called")
579}