chiark / gitweb /
reorg, code motion
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 22 Aug 2020 21:31:49 +0000 (22:31 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 22 Aug 2020 21:31:49 +0000 (22:31 +0100)
src/cmdlistener.rs

index 077bde0088025396131c4ed8f9c01abb0db5cedb..9b3d7a8094cdbf78e14935efa6b62c0b684f9d28 100644 (file)
@@ -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<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) {
@@ -61,22 +64,160 @@ impl CommandStream<'_> {
   }
 }
 
-/*
-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)]
@@ -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<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,
@@ -523,79 +510,89 @@ fn execute_game_insn(cs: &CommandStream,
     },
   }
 }
+//---------- 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 => { },
+    }
   }
 }