From: Ian Jackson Date: Sat, 22 Aug 2020 21:31:49 +0000 (+0100) Subject: reorg, code motion X-Git-Tag: otter-0.2.0~1103 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=0e3599674fefc88e797d093889a59efe1ad72175;p=otter.git reorg, code motion --- diff --git a/src/cmdlistener.rs b/src/cmdlistener.rs index 077bde00..9b3d7a80 100644 --- a/src/cmdlistener.rs +++ b/src/cmdlistener.rs @@ -1,42 +1,45 @@ - -#![allow(dead_code)] - -pub use crate::from_instance_lock_error; +// management API implementation use crate::imports::*; -//use std::os::unix::prelude; -use std::os::unix::io::AsRawFd; +// ---------- newtypes, type aliases, basic definitions ---------- -pub use std::os::unix::net::UnixStream; +use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixListener; use uds::UnixStreamExt; -//use uds::UnixListenerExt; use pwd::Passwd; -//use serde_json::ser::Serializer; -//use serde_json::de::{IoRead,StreamDeserializer}; +pub use crate::from_instance_lock_error; +pub use std::os::unix::net::UnixStream; pub const SOCKET_PATH : &str = "command.socket"; // xxx +type CSE = anyhow::Error; + +use MgmtCommand::*; +use MgmtResponse::*; +use MgmtError::*; + +type ME = MgmtError; +from_instance_lock_error!{MgmtError} + +const USERLIST : &str = "/etc/userlist"; + +// ---------- entrypoint for the rest of the program ---------- + pub struct CommandListener { listener : UnixListener, } -#[derive(Debug,Error,Clone)] -#[error("connection euid lookup failed (at connection initiation): {0}")] -pub struct ConnectionEuidDiscoverEerror(String); +// ---------- core listener implementation ---------- struct CommandStream<'d> { euid : Result, desc : &'d str, scope : Option, - amu : Option, chan : MgmtChannel, } -type CSE = anyhow::Error; - impl CommandStream<'_> { #[throws(CSE)] pub fn mainloop(mut self) { @@ -61,22 +64,160 @@ impl CommandStream<'_> { } } -/* -impl From for MgmtError { - fn from(je: serde_lexpr::Error) -> ME { - ParseFailed(format!("{}", &je)) +impl CommandListener { + #[throws(StartupError)] + pub fn new() -> Self { + let path = SOCKET_PATH; + match fs::remove_file(path) { + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()), + r => r, + } + .with_context(|| format!("remove socket {:?} before we bind", &path))?; + let listener = UnixListener::bind(path) + .with_context(|| format!("bind command socket {:?}", &path))?; + + fs::set_permissions(path, unix::fs::PermissionsExt::from_mode(0o666)) + .with_context(|| format!("chmod sommand socket {:?}", &path))?; + + CommandListener { listener } + } + + #[throws(StartupError)] + pub fn spawn(mut self) { + thread::spawn(move ||{ + loop { + self.accept_one().unwrap_or_else( + |e| eprintln!("accept/spawn failed: {:?}", e) + ); + } + }) + } + + #[throws(CSE)] + fn accept_one(&mut self) { + let (conn, _caller) = self.listener.accept().context("accept")?; + let mut desc = format!("{:>5}", conn.as_raw_fd()); + eprintln!("command connection {}: accepted", &desc); + thread::spawn(move||{ + match (||{ + let euid = conn.initial_peer_credentials() + .map(|creds| creds.euid()) + .map_err(|e| ConnectionEuidDiscoverEerror(format!("{}", e))); + + #[derive(Error,Debug)] + struct EuidLookupError(String); + display_as_debug!{EuidLookupError} + impl From<&E> for EuidLookupError where E : Display { + fn from(e: &E) -> Self { EuidLookupError(format!("{}",e)) } + } + + let user_desc : String = (||{ + let euid = euid.clone()?; + let pwent = Passwd::from_uid(euid); + let show_username = + pwent.map_or_else(|| format!("", euid), + |p| p.name); + >::Ok(show_username) + })().unwrap_or_else(|e| format!("", e)); + write!(&mut desc, " user={}", user_desc)?; + + let chan = MgmtChannel::new(conn)?; + + let cs = CommandStream { + scope: None, desc: &desc, + chan, euid, + }; + cs.mainloop()?; + + >::Ok(()) + })() { + Ok(()) => eprintln!("command connection {}: disconnected", &desc), + Err(e) => eprintln!("command connection {}: error: {:?}", &desc, e), + } + }); } } -*/ -use MgmtCommand::*; -use MgmtResponse::*; -use MgmtError::*; +// ---------- core management channel implementation ---------- -type ME = MgmtError; -from_instance_lock_error!{MgmtError} +// ---------- management command implementations -const USERLIST : &str = "/etc/userlist"; +#[throws(ME)] +fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { + eprintln!("command connection {}: executing {:?}", &cs.desc, &cmd); + + match cmd { + Noop => Fine, + + SetScope(wanted_scope) => { + let authorised : AuthorisedSatisfactory = + authorise_scope(cs, &wanted_scope)?; + cs.scope = Some(authorised.into_inner()); + Fine + }, + + CreateGame { name, insns } => { + let gs = crate::gamestate::GameState { + table_size : DEFAULT_TABLE_SIZE, + pieces : Default::default(), + players : Default::default(), + log : Default::default(), + gen : Generation(0), + max_z: ZCoord(0.), + }; + + let name = InstanceName { + scope : cs.get_scope()?.clone(), + scoped_name : name, + }; + + let gref = Instance::new(name, gs)?; + let mut ig = gref.lock()?; + + execute_for_game(cs, &mut ig, insns, MgmtGameUpdateMode::Bulk) + .map_err(|e|{ + let name = ig.name.clone(); + Instance::destroy_game(ig) + .unwrap_or_else(|e| + eprintln!("failed to tidy up failecd creation of {:?}: {:?}", + &name, &e)); + e + })?; + + Fine + }, + + ListGames { all } => { + let scope = if all == Some(true) { + let _authorise : AuthorisedSatisfactory = + authorise_scope(cs, &ManagementScope::Server)?; + None + } else { + let scope = cs.get_scope()?; + Some(scope) + }; + let mut games = Instance::list_names(scope); + games.sort_unstable(); + GamesList(games) + }, + + MgmtCommand::AlterGame { name, insns, how} => { + let name = InstanceName { + scope: cs.get_scope()?.clone(), + scoped_name: name + }; + let gref = Instance::lookup_by_name(&name)?; + let mut g = gref.lock()?; + execute_for_game(cs, &mut g, insns, how)? + }, + } +} + +//---------- authorisation ---------- + +#[derive(Debug,Error,Clone)] +#[error("connection euid lookup failed (at connection initiation): {0}")] +pub struct ConnectionEuidDiscoverEerror(String); impl CommandStream<'_> { #[throws(AuthorisationError)] @@ -188,160 +329,6 @@ fn do_authorise_scope(cs: &CommandStream, wanted: &ManagementScope) } } -#[throws(ME)] -fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { - eprintln!("command connection {}: executing {:?}", &cs.desc, &cmd); - - match cmd { - Noop => Fine, - - SetScope(wanted_scope) => { - let authorised : AuthorisedSatisfactory = - authorise_scope(cs, &wanted_scope)?; - cs.scope = Some(authorised.into_inner()); - Fine - }, - - CreateGame { name, insns } => { - let gs = crate::gamestate::GameState { - table_size : DEFAULT_TABLE_SIZE, - pieces : Default::default(), - players : Default::default(), - log : Default::default(), - gen : Generation(0), - max_z: ZCoord(0.), - }; - - let name = InstanceName { - scope : cs.get_scope()?.clone(), - scoped_name : name, - }; - - let gref = Instance::new(name, gs)?; - let mut ig = gref.lock()?; - - execute_for_game(cs, &mut ig, insns, MgmtGameUpdateMode::Bulk) - .map_err(|e|{ - let name = ig.name.clone(); - Instance::destroy_game(ig) - .unwrap_or_else(|e| - eprintln!("failed to tidy up failecd creation of {:?}: {:?}", - &name, &e)); - e - })?; - - Fine - }, - - ListGames { all } => { - let scope = if all == Some(true) { - let _authorise : AuthorisedSatisfactory = - authorise_scope(cs, &ManagementScope::Server)?; - None - } else { - let scope = cs.get_scope()?; - Some(scope) - }; - let mut games = Instance::list_names(scope); - games.sort_unstable(); - GamesList(games) - }, - - MgmtCommand::AlterGame { name, insns, how} => { - let name = InstanceName { - scope: cs.get_scope()?.clone(), - scoped_name: name - }; - let gref = Instance::lookup_by_name(&name)?; - let mut g = gref.lock()?; - execute_for_game(cs, &mut g, insns, how)? - }, - } -} - -#[derive(Debug,Default)] -struct UpdateHandlerBulk { - pieces : slotmap::SparseSecondaryMap>, - logs : bool, - raw : Vec, -} - -#[derive(Debug)] -enum UpdateHandler { - Bulk(UpdateHandlerBulk), - Online, -} - -impl UpdateHandler { - fn from_how(how: MgmtGameUpdateMode) -> Self { - use UpdateHandler::*; - match how { - MgmtGameUpdateMode::Bulk => Bulk(Default::default()), - MgmtGameUpdateMode::Online => Online, - } - } - - #[throws(SVGProcessingError)] - fn accumulate(&mut self, g: &mut Instance, - updates: ExecuteGameChangeUpdates) { - let mut raw = updates.raw.unwrap_or_default(); - use UpdateHandler::*; - match self { - Bulk(bulk) => { - for (upiece, uuop) in updates.pcs { - use PieceUpdateOp::*; - let ne = match (bulk.pieces.get(upiece), uuop) { - ( None , e ) => Some( e ), - ( Some( Insert(()) ) , Delete() ) => None, - ( Some( Insert(()) ) , _ ) => Some( Insert(()) ), - ( Some( Delete( ) ) , _ ) => Some( Modify(()) ), - ( _ , _ ) => Some( Modify(()) ), - }; - match ne { - Some(ne) => { bulk.pieces.insert(upiece, ne); }, - None => { bulk.pieces.remove(upiece); }, - }; - } - bulk.logs |= updates.log.len() != 0; - bulk.raw.append(&mut raw); - }, - Online => { - let estimate = updates.pcs.len() + updates.log.len(); - let mut buf = PrepareUpdatesBuffer::new(g, None, Some(estimate)); - for (upiece, uuop) in updates.pcs { - let lens = TransparentLens { }; - buf.piece_update(upiece, uuop, &lens); - } - buf.log_updates(updates.log); - buf.raw_updates(raw); - }, - } - } - - #[throws(SVGProcessingError)] - fn complete(self, _cs: &CommandStream, g: &mut InstanceGuard) { - use UpdateHandler::*; - match self { - Bulk(bulk) => { - let mut buf = PrepareUpdatesBuffer::new(g, None, None); - for (upiece, uuop) in bulk.pieces { - let lens = TransparentLens { }; - buf.piece_update(upiece, uuop, &lens); - } - - if bulk.logs { - buf.log_updates(vec![LogEntry { - html: "The facilitator (re)configured the game".to_owned(), - // xxx use cs.desc - }]); - } - - buf.raw_updates(bulk.raw); - }, - Online => { }, - } - } -} #[throws(ME)] fn execute_for_game(cs: &CommandStream, ig: &mut InstanceGuard, @@ -523,79 +510,89 @@ fn execute_game_insn(cs: &CommandStream, }, } } +//---------- game update processing ---------- +#[derive(Debug,Default)] +struct UpdateHandlerBulk { + pieces : slotmap::SparseSecondaryMap>, + logs : bool, + raw : Vec, +} -impl CommandListener { - #[throws(StartupError)] - pub fn new() -> Self { - let path = SOCKET_PATH; - match fs::remove_file(path) { - Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()), - r => r, - } - .with_context(|| format!("remove socket {:?} before we bind", &path))?; - let listener = UnixListener::bind(path) - .with_context(|| format!("bind command socket {:?}", &path))?; - - fs::set_permissions(path, unix::fs::PermissionsExt::from_mode(0o666)) - .with_context(|| format!("chmod sommand socket {:?}", &path))?; +#[derive(Debug)] +enum UpdateHandler { + Bulk(UpdateHandlerBulk), + Online, +} - CommandListener { listener } +impl UpdateHandler { + fn from_how(how: MgmtGameUpdateMode) -> Self { + use UpdateHandler::*; + match how { + MgmtGameUpdateMode::Bulk => Bulk(Default::default()), + MgmtGameUpdateMode::Online => Online, + } } - #[throws(StartupError)] - pub fn spawn(mut self) { - thread::spawn(move ||{ - loop { - self.accept_one().unwrap_or_else( - |e| eprintln!("accept/spawn failed: {:?}", e) - ); - } - }) + #[throws(SVGProcessingError)] + fn accumulate(&mut self, g: &mut Instance, + updates: ExecuteGameChangeUpdates) { + let mut raw = updates.raw.unwrap_or_default(); + use UpdateHandler::*; + match self { + Bulk(bulk) => { + for (upiece, uuop) in updates.pcs { + use PieceUpdateOp::*; + let ne = match (bulk.pieces.get(upiece), uuop) { + ( None , e ) => Some( e ), + ( Some( Insert(()) ) , Delete() ) => None, + ( Some( Insert(()) ) , _ ) => Some( Insert(()) ), + ( Some( Delete( ) ) , _ ) => Some( Modify(()) ), + ( _ , _ ) => Some( Modify(()) ), + }; + match ne { + Some(ne) => { bulk.pieces.insert(upiece, ne); }, + None => { bulk.pieces.remove(upiece); }, + }; + } + bulk.logs |= updates.log.len() != 0; + bulk.raw.append(&mut raw); + }, + Online => { + let estimate = updates.pcs.len() + updates.log.len(); + let mut buf = PrepareUpdatesBuffer::new(g, None, Some(estimate)); + for (upiece, uuop) in updates.pcs { + let lens = TransparentLens { }; + buf.piece_update(upiece, uuop, &lens); + } + buf.log_updates(updates.log); + buf.raw_updates(raw); + }, + } } - #[throws(CSE)] - fn accept_one(&mut self) { - let (conn, _caller) = self.listener.accept().context("accept")?; - let mut desc = format!("{:>5}", conn.as_raw_fd()); - eprintln!("command connection {}: accepted", &desc); - thread::spawn(move||{ - match (||{ - let euid = conn.initial_peer_credentials() - .map(|creds| creds.euid()) - .map_err(|e| ConnectionEuidDiscoverEerror(format!("{}", e))); - - #[derive(Error,Debug)] - struct EuidLookupError(String); - display_as_debug!{EuidLookupError} - impl From<&E> for EuidLookupError where E : Display { - fn from(e: &E) -> Self { EuidLookupError(format!("{}",e)) } + #[throws(SVGProcessingError)] + fn complete(self, _cs: &CommandStream, g: &mut InstanceGuard) { + use UpdateHandler::*; + match self { + Bulk(bulk) => { + let mut buf = PrepareUpdatesBuffer::new(g, None, None); + for (upiece, uuop) in bulk.pieces { + let lens = TransparentLens { }; + buf.piece_update(upiece, uuop, &lens); } - let user_desc : String = (||{ - let euid = euid.clone()?; - let pwent = Passwd::from_uid(euid); - let show_username = - pwent.map_or_else(|| format!("", euid), - |p| p.name); - >::Ok(show_username) - })().unwrap_or_else(|e| format!("", e)); - write!(&mut desc, " user={}", user_desc)?; - - let chan = MgmtChannel::new(conn)?; + if bulk.logs { + buf.log_updates(vec![LogEntry { + html: "The facilitator (re)configured the game".to_owned(), + // xxx use cs.desc + }]); + } - let cs = CommandStream { - scope: None, amu: None, desc: &desc, - chan, euid, - }; - cs.mainloop()?; - - >::Ok(()) - })() { - Ok(()) => eprintln!("command connection {}: disconnected", &desc), - Err(e) => eprintln!("command connection {}: error: {:?}", &desc, e), - } - }); + buf.raw_updates(bulk.raw); + }, + Online => { }, + } } }