-
-#![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<u32, ConnectionEuidDiscoverEerror>,
desc : &'d str,
scope : Option<ManagementScope>,
- amu : Option<InstanceRef>,
chan : MgmtChannel,
}
-type CSE = anyhow::Error;
-
impl CommandStream<'_> {
#[throws(CSE)]
pub fn mainloop(mut self) {
}
}
-/*
-impl From<serde_lexpr::Error> 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 {}>", euid),
+ |p| p.name);
+ <Result<_,AE>>::Ok(show_username)
+ })().unwrap_or_else(|e| format!("<error: {}>", e));
+ write!(&mut desc, " user={}", user_desc)?;
+
+ let chan = MgmtChannel::new(conn)?;
+
+ let cs = CommandStream {
+ scope: None, desc: &desc,
+ chan, euid,
+ };
+ cs.mainloop()?;
+
+ <Result<_,StartupError>>::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)]
}
}
-#[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<PieceId, PieceUpdateOp<()>>,
- logs : bool,
- raw : Vec<PreparedUpdateEntry>,
-}
-
-#[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,
},
}
}
+//---------- game update processing ----------
+#[derive(Debug,Default)]
+struct UpdateHandlerBulk {
+ pieces : slotmap::SparseSecondaryMap<PieceId, PieceUpdateOp<()>>,
+ logs : bool,
+ raw : Vec<PreparedUpdateEntry>,
+}
-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 {}>", euid),
- |p| p.name);
- <Result<_,AE>>::Ok(show_username)
- })().unwrap_or_else(|e| format!("<error: {}>", 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()?;
-
- <Result<_,StartupError>>::Ok(())
- })() {
- Ok(()) => eprintln!("command connection {}: disconnected", &desc),
- Err(e) => eprintln!("command connection {}: error: {:?}", &desc, e),
- }
- });
+ buf.raw_updates(bulk.raw);
+ },
+ Online => { },
+ }
}
}