1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::{
8 collections::BTreeMap,
9 string::{String, ToString},
10 vec::Vec,
11};
12use core::sync::atomic::{AtomicU64, Ordering};
13
14use super::namespace::NamespaceSet;
15use crate::{error::KernelError, process::ProcessId};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ContainerState {
19 Created,
20 Running,
21 Stopped,
22}
23
24impl core::fmt::Display for ContainerState {
25 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26 match self {
27 Self::Created => write!(f, "Created"),
28 Self::Running => write!(f, "Running"),
29 Self::Stopped => write!(f, "Stopped"),
30 }
31 }
32}
33
34#[cfg(feature = "alloc")]
35#[derive(Debug, Clone)]
36pub struct ContainerInfo {
37 pub id: u64,
38 pub name: String,
39 pub state: ContainerState,
40 pub root_pid: Option<ProcessId>,
41 pub process_count: usize,
42 pub hostname: String,
43}
44
45#[cfg(feature = "alloc")]
46pub struct Container {
47 pub id: u64,
48 pub name: String,
49 pub namespaces: NamespaceSet,
50 pub root_process: Option<ProcessId>,
51 pub state: ContainerState,
52 pub init_program: String,
53}
54
55#[cfg(feature = "alloc")]
56impl Container {
57 pub fn info(&self) -> ContainerInfo {
58 ContainerInfo {
59 id: self.id,
60 name: self.name.clone(),
61 state: self.state,
62 root_pid: self.root_process,
63 process_count: self.namespaces.pid.process_count(),
64 hostname: self.namespaces.uts.hostname().to_string(),
65 }
66 }
67}
68
69static NEXT_CONTAINER_ID: AtomicU64 = AtomicU64::new(1);
70pub(crate) static CONTAINER_MGR: spin::Mutex<Option<ContainerManager>> = spin::Mutex::new(None);
71
72#[cfg(feature = "alloc")]
73pub struct ContainerManager {
74 containers: BTreeMap<u64, Container>,
75}
76
77#[cfg(feature = "alloc")]
78impl ContainerManager {
79 pub fn new() -> Self {
80 Self {
81 containers: BTreeMap::new(),
82 }
83 }
84
85 pub fn create(&mut self, name: &str) -> Result<u64, KernelError> {
86 let id = NEXT_CONTAINER_ID.fetch_add(1, Ordering::Relaxed);
87 self.containers.insert(
88 id,
89 Container {
90 id,
91 name: String::from(name),
92 namespaces: NamespaceSet::new(name),
93 root_process: None,
94 state: ContainerState::Created,
95 init_program: String::new(),
96 },
97 );
98 crate::println!(" [container] Created container {} (id={})", name, id);
99 Ok(id)
100 }
101
102 pub fn start(&mut self, id: u64, program: &str) -> Result<(), KernelError> {
103 let container = self.containers.get_mut(&id).ok_or(KernelError::NotFound {
104 resource: "container",
105 id,
106 })?;
107 if container.state != ContainerState::Created {
108 return Err(KernelError::InvalidState {
109 expected: "Created",
110 actual: match container.state {
111 ContainerState::Running => "Running",
112 ContainerState::Stopped => "Stopped",
113 ContainerState::Created => "Created",
114 },
115 });
116 }
117 container.init_program = String::from(program);
118 let placeholder_pid = ProcessId(1000 + id);
119 let cpid = container.namespaces.pid.add_process(placeholder_pid);
120 container.root_process = Some(placeholder_pid);
121 container.state = ContainerState::Running;
122 crate::println!(
123 " [container] Started {} (id={}, root_pid={}, container_pid={})",
124 container.name,
125 id,
126 placeholder_pid.0,
127 cpid
128 );
129 Ok(())
130 }
131
132 pub fn stop(&mut self, id: u64) -> Result<(), KernelError> {
133 let container = self.containers.get_mut(&id).ok_or(KernelError::NotFound {
134 resource: "container",
135 id,
136 })?;
137 if container.state != ContainerState::Running {
138 return Err(KernelError::InvalidState {
139 expected: "Running",
140 actual: match container.state {
141 ContainerState::Created => "Created",
142 ContainerState::Stopped => "Stopped",
143 ContainerState::Running => "Running",
144 },
145 });
146 }
147 if let Some(root_pid) = container.root_process.take() {
148 container.namespaces.pid.remove_process(root_pid);
149 }
150 container.state = ContainerState::Stopped;
151 crate::println!(" [container] Stopped {} (id={})", container.name, id);
152 Ok(())
153 }
154
155 pub fn destroy(&mut self, id: u64) -> Result<(), KernelError> {
156 let container = self.containers.get(&id).ok_or(KernelError::NotFound {
157 resource: "container",
158 id,
159 })?;
160 if container.state == ContainerState::Running {
161 return Err(KernelError::InvalidState {
162 expected: "Created or Stopped",
163 actual: "Running",
164 });
165 }
166 let name = container.name.clone();
167 self.containers.remove(&id);
168 crate::println!(" [container] Destroyed {} (id={})", name, id);
169 Ok(())
170 }
171
172 pub fn list(&self) -> Vec<ContainerInfo> {
173 self.containers.values().map(|c| c.info()).collect()
174 }
175 pub fn container_count(&self) -> usize {
176 self.containers.len()
177 }
178 pub fn get(&self, id: u64) -> Option<&Container> {
179 self.containers.get(&id)
180 }
181 pub fn get_mut(&mut self, id: u64) -> Option<&mut Container> {
182 self.containers.get_mut(&id)
183 }
184}
185
186#[cfg(feature = "alloc")]
187impl Default for ContainerManager {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193pub fn init() {
194 #[cfg(feature = "alloc")]
195 {
196 let mut mgr = CONTAINER_MGR.lock();
197 *mgr = Some(ContainerManager::new());
198 }
199 crate::println!(" [container] Container manager initialized");
200}
201
202#[cfg(feature = "alloc")]
203pub fn with_container_manager<R, F: FnOnce(&mut ContainerManager) -> R>(
204 f: F,
205) -> Result<R, KernelError> {
206 let mut mgr = CONTAINER_MGR.lock();
207 match mgr.as_mut() {
208 Some(m) => Ok(f(m)),
209 None => Err(KernelError::NotInitialized {
210 subsystem: "container manager",
211 }),
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_container_lifecycle() {
221 let mut mgr = ContainerManager::new();
222 let id = mgr.create("test").unwrap();
223 assert_eq!(mgr.container_count(), 1);
224 assert!(mgr.start(id, "/bin/init").is_ok());
225 assert_eq!(mgr.get(id).unwrap().state, ContainerState::Running);
226 assert!(mgr.stop(id).is_ok());
227 assert_eq!(mgr.get(id).unwrap().state, ContainerState::Stopped);
228 assert!(mgr.destroy(id).is_ok());
229 assert_eq!(mgr.container_count(), 0);
230 }
231
232 #[test]
233 fn test_container_destroy_running_fails() {
234 let mut mgr = ContainerManager::new();
235 let id = mgr.create("test").unwrap();
236 mgr.start(id, "/bin/init").unwrap();
237 assert!(mgr.destroy(id).is_err());
238 }
239
240 #[test]
241 fn test_container_list() {
242 let mut mgr = ContainerManager::new();
243 mgr.create("web").unwrap();
244 mgr.create("db").unwrap();
245 assert_eq!(mgr.list().len(), 2);
246 }
247
248 #[test]
249 fn test_container_state_display() {
250 assert_eq!(alloc::format!("{}", ContainerState::Created), "Created");
251 assert_eq!(alloc::format!("{}", ContainerState::Running), "Running");
252 assert_eq!(alloc::format!("{}", ContainerState::Stopped), "Stopped");
253 }
254}