From 497720b794620ea1e0801fd3380d142fa4eba32e Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Tue, 22 Nov 2022 23:38:13 +0800
Subject: [PATCH] Share embassy common code between std and picow

---
 embassy/demos/common/README.md                |   4 +
 embassy/demos/common/common.rs                | 183 ++++++++++++++++++
 .../demos/{std/src => common}/demo_menu.rs    |   0
 embassy/demos/picow/Cargo.lock                |   7 +
 embassy/demos/picow/Cargo.toml                |   6 +
 embassy/demos/picow/src/main.rs               | 153 ++-------------
 embassy/demos/std/src/main.rs                 | 158 ++-------------
 7 files changed, 222 insertions(+), 289 deletions(-)
 create mode 100644 embassy/demos/common/README.md
 create mode 100644 embassy/demos/common/common.rs
 rename embassy/demos/{std/src => common}/demo_menu.rs (100%)

diff --git a/embassy/demos/common/README.md b/embassy/demos/common/README.md
new file mode 100644
index 0000000..225de75
--- /dev/null
+++ b/embassy/demos/common/README.md
@@ -0,0 +1,4 @@
+# Embassy demos common
+
+The [picow](../picow) and [std](../std) demos share this common code. 
+Currently not a full crate, just source modules included.
diff --git a/embassy/demos/common/common.rs b/embassy/demos/common/common.rs
new file mode 100644
index 0000000..482eb77
--- /dev/null
+++ b/embassy/demos/common/common.rs
@@ -0,0 +1,183 @@
+#![feature(type_alias_impl_trait)]
+
+#[allow(unused_imports)]
+#[cfg(not(feature = "defmt"))]
+use {
+    log::{debug, error, info, log, trace, warn},
+};
+
+#[cfg(feature = "defmt")]
+use defmt::{debug, info, warn, panic, error, trace};
+
+use core::future::Future;
+
+use embassy_executor::{Spawner, Executor};
+use embassy_sync::mutex::Mutex;
+use embassy_sync::blocking_mutex::raw::{NoopRawMutex, CriticalSectionRawMutex};
+use embassy_sync::signal::Signal;
+use embassy_net::tcp::TcpSocket;
+use embassy_net::{Stack, Device, StackResources, ConfigStrategy};
+use embassy_futures::join::join;
+use static_cell::StaticCell;
+
+use menu::Runner as MenuRunner;
+use menu::Menu;
+
+use sunset::*;
+use sunset::error::TrapBug;
+use sunset_embassy::SSHServer;
+
+mod demo_menu;
+
+#[macro_export]
+macro_rules! singleton {
+    ($val:expr) => {{
+        type T = impl Sized;
+        static STATIC_CELL: StaticCell<T> = StaticCell::new();
+        STATIC_CELL.init_with(move || $val)
+    }};
+}
+
+pub struct SSHConfig {
+    keys: [SignKey; 1],
+}
+
+impl SSHConfig {
+    pub fn new() -> Result<Self> {
+        let keys = [SignKey::generate(KeyType::Ed25519)?];
+        Ok(Self {
+            keys
+        })
+    }
+}
+
+// main entry point
+pub async fn listener<D: Device>(stack: &'static Stack<D>, config: &SSHConfig) -> ! {
+    let mut rx_buffer = [0; 4096];
+    let mut tx_buffer = [0; 4096];
+
+    loop {
+        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
+
+        info!("Listening on TCP:22...");
+        if let Err(e) = socket.accept(22).await {
+            warn!("accept error: {:?}", e);
+            continue;
+        }
+
+        let r = session(&mut socket, &config).await;
+        if let Err(_e) = r {
+            // warn!("Ended with error: {:?}", e);
+            warn!("Ended with error");
+        }
+    }
+}
+
+struct DemoServer<'a> {
+    config: &'a SSHConfig,
+
+    sess: Option<u32>,
+    shell_started: bool,
+
+    shell: &'a DemoShell,
+}
+
+impl<'a> DemoServer<'a> {
+    fn new(shell: &'a DemoShell, config: &'a SSHConfig) -> Result<Self> {
+
+        Ok(Self {
+            sess: None,
+            config,
+            shell_started: false,
+            shell,
+        })
+    }
+}
+
+impl<'a> ServBehaviour for DemoServer<'a> {
+    fn hostkeys(&mut self) -> BhResult<&[SignKey]> {
+        Ok(&self.config.keys)
+    }
+
+    fn auth_unchallenged(&mut self, username: TextString) -> bool {
+        info!("Allowing auth for user {}", username.as_str().unwrap_or("bad"));
+        true
+    }
+
+    fn open_session(&mut self, chan: u32) -> ChanOpened {
+        if self.sess.is_some() {
+            ChanOpened::Failure(ChanFail::SSH_OPEN_ADMINISTRATIVELY_PROHIBITED)
+        } else {
+            self.sess = Some(chan);
+            ChanOpened::Success
+        }
+    }
+
+    fn sess_shell(&mut self, chan: u32) -> bool {
+        let r = !self.shell_started && self.sess == Some(chan);
+        self.shell_started = true;
+        self.shell.notify.signal(chan);
+        trace!("req want shell");
+        r
+    }
+
+    fn sess_pty(&mut self, chan: u32, _pty: &Pty) -> bool {
+        self.sess == Some(chan)
+    }
+}
+
+
+#[derive(Default)]
+struct DemoShell {
+    notify: Signal<CriticalSectionRawMutex, u32>,
+}
+
+impl DemoShell {
+    async fn run<'f>(&self, serv: &SSHServer<'f>) -> Result<()>
+    {
+        let session = async {
+            // wait for a shell to start
+            let chan = self.notify.wait().await;
+
+            let mut menu_buf = [0u8; 64];
+            let menu_out = demo_menu::Output::default();
+
+            let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out);
+
+            loop {
+                let mut b = [0u8; 20];
+                let lr = serv.read_channel_stdin(chan, &mut b).await?;
+                let b = &mut b[..lr];
+                for c in b.iter() {
+                    menu.input_byte(*c);
+                    menu.context.flush(serv, chan).await?;
+                }
+            }
+            Ok(())
+        };
+
+        session.await
+    }
+}
+
+
+async fn session(socket: &mut TcpSocket<'_>, config: &SSHConfig) -> sunset::Result<()> {
+    let shell = DemoShell::default();
+    let app = DemoServer::new(&shell, config)?;
+
+    let mut ssh_rxbuf = [0; 2000];
+    let mut ssh_txbuf = [0; 2000];
+    let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf)?;
+    let serv = &serv;
+
+    let app = Mutex::<NoopRawMutex, _>::new(app);
+
+    let session = shell.run(serv);
+
+    let app = &app as &Mutex::<NoopRawMutex, dyn ServBehaviour>;
+    let run = serv.run(socket, app);
+
+    join(run, session).await;
+
+    Ok(())
+}
diff --git a/embassy/demos/std/src/demo_menu.rs b/embassy/demos/common/demo_menu.rs
similarity index 100%
rename from embassy/demos/std/src/demo_menu.rs
rename to embassy/demos/common/demo_menu.rs
diff --git a/embassy/demos/picow/Cargo.lock b/embassy/demos/picow/Cargo.lock
index 6ead602..4932431 100644
--- a/embassy/demos/picow/Cargo.lock
+++ b/embassy/demos/picow/Cargo.lock
@@ -872,6 +872,12 @@ version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
+[[package]]
+name = "menu"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b03d7f798bfe97329ad6df937951142eec93886b37d87010502dd25e8cc75fd5"
+
 [[package]]
 name = "nb"
 version = "0.1.3"
@@ -1341,6 +1347,7 @@ dependencies = [
  "futures",
  "getrandom",
  "heapless",
+ "menu",
  "panic-probe",
  "pin-utils",
  "rand",
diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml
index 367297c..9dcb997 100644
--- a/embassy/demos/picow/Cargo.toml
+++ b/embassy/demos/picow/Cargo.toml
@@ -36,12 +36,18 @@ sunset = { path = "../../.." }
 getrandom = { version = "0.2", default-features = false, features = ["custom"]}
 pin-utils = "0.1"
 
+menu = "0.3"
+
 caprand = { path = "../../../../caprand" }
 
 critical-section = "1.1"
 rand = { version = "0.8", default-features = false, features = ["getrandom"] }
 sha2 = { version = "0.10", default-features = false }
 
+[features]
+default = ["defmt"]
+defmt = []
+
 [patch.crates-io]
 embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e7fdd500d8354a03fcd105c8298cf7b4798a4107" }
 embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e7fdd500d8354a03fcd105c8298cf7b4798a4107" }
diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs
index 95aa07a..e13f1bc 100644
--- a/embassy/demos/picow/src/main.rs
+++ b/embassy/demos/picow/src/main.rs
@@ -20,14 +20,6 @@ use embedded_io::asynch::{Read, Write};
 use static_cell::StaticCell;
 use {defmt_rtt as _, panic_probe as _};
 
-use core::str::FromStr;
-use core::cell::RefCell;
-use core::num::NonZeroU32;
-use futures::{task::noop_waker_ref,pending};
-use core::task::{Context,Poll,Waker,RawWaker,RawWakerVTable};
-use pin_utils::{pin_mut,unsafe_pinned};
-use core::pin::Pin;
-
 use rand::rngs::OsRng;
 use rand::RngCore;
 
@@ -35,16 +27,12 @@ use sunset::*;
 use sunset_embassy::SSHServer;
 
 mod wifi;
+#[path = "../../common/common.rs"]
+mod demo_common;
 
-const NUM_LISTENERS: usize = 4;
+use demo_common::SSHConfig;
 
-macro_rules! singleton {
-    ($val:expr) => {{
-        type T = impl Sized;
-        static STATIC_CELL: StaticCell<T> = StaticCell::new();
-        STATIC_CELL.init_with(move || $val)
-    }};
-}
+const NUM_LISTENERS: usize = 4;
 
 #[embassy_executor::task]
 async fn net_task(stack: &'static Stack<cyw43::NetDevice<'static>>) -> ! {
@@ -111,139 +99,20 @@ async fn main(spawner: Spawner) {
         seed
     ));
 
+    let config = &*singleton!(
+        demo_common::SSHConfig::new().unwrap()
+    );
+
     unwrap!(spawner.spawn(net_task(stack)));
 
     for _ in 0..NUM_LISTENERS {
-        spawner.spawn(listener(stack)).unwrap();
+        spawner.spawn(listener(stack, &config)).unwrap();
     }
 }
 
 // TODO: pool_size should be NUM_LISTENERS but needs a literal
 #[embassy_executor::task(pool_size = 4)]
-async fn listener(stack: &'static Stack<cyw43::NetDevice<'static>>) -> ! {
-    let mut rx_buffer = [0; 4096];
-    let mut tx_buffer = [0; 4096];
-
-    loop {
-        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
-        socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
-
-        info!("Listening on TCP:22...");
-        if let Err(e) = socket.accept(22).await {
-            warn!("accept error: {:?}", e);
-            continue;
-        }
-
-        let r = session(&mut socket).await;
-        if let Err(_e) = r {
-            // warn!("Ended with error: {:?}", e);
-            warn!("Ended with error");
-        }
-    }
-}
-
-struct DemoServer {
-    keys: [SignKey; 1],
-
-    sess: Option<u32>,
-    want_shell: bool,
-    shell_started: bool,
-
-    notify: Signal<CriticalSectionRawMutex, ()>,
-}
-
-impl DemoServer {
-    fn new() -> Result<Self> {
-        let keys = [SignKey::generate(KeyType::Ed25519)?];
-
-        Ok(Self {
-            sess: None,
-            keys,
-            want_shell: false,
-            shell_started: false,
-            notify: Signal::new(),
-        })
-    }
-}
-
-impl ServBehaviour for DemoServer {
-    fn hostkeys(&mut self) -> BhResult<&[SignKey]> {
-        Ok(&self.keys)
-    }
-
-
-    fn have_auth_password(&self, user: TextString) -> bool {
-        true
-    }
-
-    fn have_auth_pubkey(&self, user: TextString) -> bool {
-        true
-    }
-
-    fn auth_unchallenged(&mut self, username: TextString) -> bool {
-        let u = username.as_str().unwrap_or("mystery user");
-        info!("Allowing auth for user {}", u);
-        true
-    }
-
-    fn auth_password(&mut self, user: TextString, password: TextString) -> bool {
-        user.as_str().unwrap_or("") == "matt" && password.as_str().unwrap_or("") == "pw"
-    }
-
-    // fn auth_pubkey(&mut self, user: TextString, pubkey: &PubKey) -> bool {
-    //     if user.as_str().unwrap_or("") != "matt" {
-    //         return false
-    //     }
-
-    //     // key is tested1
-    //     pubkey.matches_openssh("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMkNdReJERy1rPGqdfTN73TnayPR+lTNhdZvOgkAOs5x")
-    //     .unwrap_or_else(|e| {
-    //         warn!("Failed loading openssh key: {e}");
-    //         false
-    //     })
-    // }
-
-    fn open_session(&mut self, chan: u32) -> ChanOpened {
-        if self.sess.is_some() {
-            ChanOpened::Failure(ChanFail::SSH_OPEN_ADMINISTRATIVELY_PROHIBITED)
-        } else {
-            self.sess = Some(chan);
-            ChanOpened::Success
-        }
-    }
-
-    fn sess_shell(&mut self, chan: u32) -> bool {
-        let r = !self.want_shell && self.sess == Some(chan);
-        self.want_shell = true;
-        trace!("req want shell");
-        r
-    }
-
-    fn sess_pty(&mut self, chan: u32, _pty: &Pty) -> bool {
-        self.sess == Some(chan)
-    }
+async fn listener(stack: &'static Stack<cyw43::NetDevice<'static>>, config: &'static SSHConfig) -> ! {
+    demo_common::listener(stack, config).await
 }
 
-async fn session(socket: &mut TcpSocket<'_>) -> sunset::Result<()> {
-    let mut app = DemoServer::new()?;
-
-    let mut ssh_rxbuf = [0; 2000];
-    let mut ssh_txbuf = [0; 2000];
-    let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf, &mut app)?;
-    let serv = &serv;
-
-    let app = Mutex::<NoopRawMutex, _>::new(app);
-    let app = &app as &Mutex::<NoopRawMutex, dyn ServBehaviour>;
-
-    let run = serv.run(socket, app);
-
-    let session = async {
-        loop {
-        }
-    };
-
-    // TODO: handle results
-    join(run, session).await;
-
-    Ok(())
-}
diff --git a/embassy/demos/std/src/main.rs b/embassy/demos/std/src/main.rs
index deac6e9..5f5d75a 100644
--- a/embassy/demos/std/src/main.rs
+++ b/embassy/demos/std/src/main.rs
@@ -18,18 +18,9 @@ use embassy_futures::join::join;
 use embedded_io::asynch::{Read, Write};
 use static_cell::StaticCell;
 
-use core::str::FromStr;
-use core::cell::RefCell;
-use core::num::NonZeroU32;
-use futures::{task::noop_waker_ref,pending};
-use core::task::{Context,Poll,Waker,RawWaker,RawWakerVTable};
-
 use rand::rngs::OsRng;
 use rand::RngCore;
 
-use menu::Runner as MenuRunner;
-use menu::Menu;
-
 use sunset::*;
 use sunset::error::TrapBug;
 use sunset_embassy::SSHServer;
@@ -37,17 +28,12 @@ use sunset_embassy::SSHServer;
 use crate::tuntap::TunTapDevice;
 
 mod tuntap;
-mod demo_menu;
+#[path = "../../common/common.rs"]
+mod demo_common;
 
-const NUM_LISTENERS: usize = 4;
+use demo_common::SSHConfig;
 
-macro_rules! singleton {
-    ($val:expr) => {{
-        type T = impl Sized;
-        static STATIC_CELL: StaticCell<T> = StaticCell::new();
-        STATIC_CELL.init_with(move || $val)
-    }};
-}
+const NUM_LISTENERS: usize = 4;
 
 #[embassy_executor::task]
 async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! {
@@ -75,148 +61,26 @@ async fn main_task(spawner: Spawner) {
         seed
     ));
 
+    let config = &*singleton!(
+        demo_common::SSHConfig::new().unwrap()
+    );
+
     // Launch network task
     spawner.spawn(net_task(stack)).unwrap();
 
     for _ in 0..NUM_LISTENERS {
-        spawner.spawn(listener(stack)).unwrap();
+        spawner.spawn(listener(stack, &config)).unwrap();
     }
 }
 
 // TODO: pool_size should be NUM_LISTENERS but needs a literal
 #[embassy_executor::task(pool_size = 4)]
-async fn listener(stack: &'static Stack<TunTapDevice>) -> ! {
-    let mut rx_buffer = [0; 4096];
-    let mut tx_buffer = [0; 4096];
-
-    loop {
-        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
-
-        info!("Listening on TCP:22...");
-        if let Err(e) = socket.accept(22).await {
-            warn!("accept error: {:?}", e);
-            continue;
-        }
-
-        let r = session(&mut socket).await;
-        if let Err(_e) = r {
-            // warn!("Ended with error: {:?}", e);
-            warn!("Ended with error");
-        }
-    }
-}
-
-struct DemoServer<'a> {
-    keys: [SignKey; 1],
-
-    sess: Option<u32>,
-    shell_started: bool,
-
-    shell: &'a DemoShell,
-}
-
-impl<'a> DemoServer<'a> {
-    fn new(shell: &'a DemoShell) -> Result<Self> {
-
-        let keys = [SignKey::generate(KeyType::Ed25519)?];
-
-        Ok(Self {
-            sess: None,
-            keys,
-            shell_started: false,
-            shell,
-        })
-    }
-}
-
-impl<'a> ServBehaviour for DemoServer<'a> {
-    fn hostkeys(&mut self) -> BhResult<&[SignKey]> {
-        Ok(&self.keys)
-    }
-
-    fn auth_unchallenged(&mut self, username: TextString) -> bool {
-        info!("Allowing auth for user {:?}", username.as_str());
-        true
-    }
-
-    fn open_session(&mut self, chan: u32) -> ChanOpened {
-        if self.sess.is_some() {
-            ChanOpened::Failure(ChanFail::SSH_OPEN_ADMINISTRATIVELY_PROHIBITED)
-        } else {
-            self.sess = Some(chan);
-            ChanOpened::Success
-        }
-    }
-
-    fn sess_shell(&mut self, chan: u32) -> bool {
-        let r = !self.shell_started && self.sess == Some(chan);
-        self.shell_started = true;
-        self.shell.notify.signal(chan);
-        trace!("req want shell");
-        r
-    }
-
-    fn sess_pty(&mut self, chan: u32, _pty: &Pty) -> bool {
-        self.sess == Some(chan)
-    }
-}
-
-
-#[derive(Default)]
-struct DemoShell {
-    notify: Signal<CriticalSectionRawMutex, u32>,
-}
+async fn listener(stack: &'static Stack<TunTapDevice>, config: &'static SSHConfig) -> ! {
 
-impl DemoShell {
-    async fn run<'f>(&self, serv: &SSHServer<'f>) -> Result<()>
-    {
-        let session = async {
-            // wait for a shell to start
-            let chan = self.notify.wait().await;
-
-            let mut menu_buf = [0u8; 64];
-            let mut menu_out = demo_menu::Output::default();
-
-            let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out);
-
-            loop {
-                let mut b = [0u8; 20];
-                let lr = serv.read_channel_stdin(chan, &mut b).await?;
-                let b = &mut b[..lr];
-                for c in b.iter() {
-                    menu.input_byte(*c);
-                    menu.context.flush(serv, chan).await?;
-                }
-            }
-            Ok(())
-        };
-
-        session.await
-    }
+    demo_common::listener(stack, config).await
 }
 
 
-async fn session(socket: &mut TcpSocket<'_>) -> sunset::Result<()> {
-    let shell = DemoShell::default();
-    let app = DemoServer::new(&shell)?;
-
-    let mut ssh_rxbuf = [0; 2000];
-    let mut ssh_txbuf = [0; 2000];
-    let serv = SSHServer::new(&mut ssh_rxbuf, &mut ssh_txbuf)?;
-    let serv = &serv;
-
-    let app = Mutex::<NoopRawMutex, _>::new(app);
-
-    let session = shell.run(serv);
-
-    let app = &app as &Mutex::<NoopRawMutex, dyn ServBehaviour>;
-    let run = serv.run(socket, app);
-
-    join(run, session).await;
-
-    Ok(())
-}
-
 static EXECUTOR: StaticCell<Executor> = StaticCell::new();
 
 fn main() {
-- 
GitLab