use server_framework::Server; use types::bounded::CapSlot; use crate::protocol::{AppId, SessionRequest, SessionResponse}; const MAX_APPS: usize = 16; #[derive(Clone, Copy)] pub struct AppEntry { pub name: &'static str, pub path: &'static str, pub registered: bool, } #[derive(Clone, Copy)] struct RunningApp { /// The capability `Kill` is invoked on. process: CapSlot, } pub struct SessionServer { apps: [Option; MAX_APPS], app_count: usize, running: [Option; MAX_APPS], active: Option, /// Lets the server create processes; without one, Launch fails. create_cap: Option, } impl SessionServer { pub fn new(create_cap: Option) -> Self { Self { apps: [const { None }; MAX_APPS], app_count: 0, running: [const { None }; MAX_APPS], active: None, create_cap, } } /// Adds an app to the menu; `None` once the table is full. pub fn register_app(&mut self, name: &'static str, path: &'static str) -> Option { let slot = self.apps.get_mut(self.app_count)?; *slot = Some(AppEntry { name, path, registered: true, }); let id = AppId::new(self.app_count as u16); self.app_count += 1; Some(id) } pub fn app_count(&self) -> usize { self.app_count } pub fn active_app(&self) -> Option { self.active } /// The running-table entry for `app`; `None` for ids past the table, /// which come straight from clients. fn running_mut(&mut self, app: AppId) -> Option<&mut Option> { self.running.get_mut(app.raw() as usize) } fn launch_app(&mut self, app: AppId) -> SessionResponse { let idx = app.raw() as usize; if idx >= self.app_count { return SessionResponse::NotFound; } if self.apps.get(idx).is_none_or(Option::is_none) { return SessionResponse::NotFound; } if self.running_mut(app).is_none_or(|r| r.is_some()) { return SessionResponse::Error; } let Some(process) = self.create_cap.and_then(libkos::syscall::create_process) else { return SessionResponse::Error; }; if let Some(r) = self.running_mut(app) { *r = Some(RunningApp { process }); } self.active = Some(app); SessionResponse::Launched { app } } fn kill_app(&mut self, app: AppId) -> SessionResponse { match self.running_mut(app).and_then(Option::take) { Some(running) => { let _ = libkos::syscall::kill(running.process); if self.active == Some(app) { self.active = None; } SessionResponse::Killed } None => SessionResponse::NotFound, } } } impl Server for SessionServer { type Request = SessionRequest; type Response = SessionResponse; fn handle(&mut self, request: SessionRequest) -> SessionResponse { match request { SessionRequest::Launch { app } => self.launch_app(app), SessionRequest::Kill { app } => self.kill_app(app), SessionRequest::ListApps => SessionResponse::AppList { count: self.app_count, }, SessionRequest::ActiveApp => match self.active { Some(app) => SessionResponse::Active { app }, None => SessionResponse::NotFound, }, } } }