Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F73657
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Subscribers
None
View Options
diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml
new file mode 100644
index 0000000..7f731ef
--- /dev/null
+++ b/crates/api/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "api"
+version = "0.1.0"
+license.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+tokio.workspace = true
+libcollar.workspace = true
+store = { path = "../store" }
+tower-http = { version = "0.6.4", features = ["trace"] }
+axum = "0.8.4"
diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
new file mode 100644
index 0000000..a768ab8
--- /dev/null
+++ b/crates/api/src/lib.rs
@@ -0,0 +1,36 @@
+use std::sync::Arc;
+
+use tower_http::trace::TraceLayer;
+
+use axum::{
+ routing::get,
+ extract::{State, Request, Json, Path, Extension, Query},
+ routing::post,
+};
+
+pub use axum::serve;
+
+struct AppState {
+ collar: libcollar::State,
+}
+
+pub fn app(collar: libcollar::State) -> axum::Router {
+ let state = Arc::new(AppState { collar });
+
+ // build our application with a single route
+ let app = axum::Router::new()
+ .route("/", get(|| async { "collared." }))
+ .route("/ng/eiface/{name}", post(post_ng_eiface))
+ .with_state(state)
+ .layer(tower_http::trace::TraceLayer::new_for_http());
+
+ app
+}
+
+async fn post_ng_eiface(State(state): State<Arc<AppState>>, Path(name): Path<String>) -> Result<String, String> {
+ let fname = name.clone();
+ let result = state.collar.leash().gated(move || {
+ Ok(libcollar::ng::new_eiface(&name))
+ }).await;
+ Ok(format!("sup {} => {:?}", &fname, result))
+}
diff --git a/crates/libcollar/Cargo.toml b/crates/libcollar/Cargo.toml
new file mode 100644
index 0000000..2fbc331
--- /dev/null
+++ b/crates/libcollar/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "libcollar"
+version = "0.1.0"
+license.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+tokio.workspace = true
+store.workspace = true
+serde.workspace = true
+ifconfig = { path = "../ifconfig" }
+ng = { path = "../ng" }
diff --git a/src/error.rs b/crates/libcollar/src/error.rs
similarity index 100%
rename from src/error.rs
rename to crates/libcollar/src/error.rs
diff --git a/crates/libcollar/src/jail/jail.rs b/crates/libcollar/src/jail/jail.rs
new file mode 100644
index 0000000..1cd2474
--- /dev/null
+++ b/crates/libcollar/src/jail/jail.rs
@@ -0,0 +1,5 @@
+#[derive(Debug, Deserialize)]
+pub struct Jail {
+ store_id: u64,
+ name: String
+}
diff --git a/crates/libcollar/src/jail/mod.rs b/crates/libcollar/src/jail/mod.rs
new file mode 100644
index 0000000..483ab07
--- /dev/null
+++ b/crates/libcollar/src/jail/mod.rs
@@ -0,0 +1,2 @@
+mod jail;
+pub use jail;
diff --git a/crates/libcollar/src/lib.rs b/crates/libcollar/src/lib.rs
new file mode 100644
index 0000000..8f510d2
--- /dev/null
+++ b/crates/libcollar/src/lib.rs
@@ -0,0 +1,6 @@
+mod error;
+pub mod net;
+pub use error::{Error, Result};
+pub use ng;
+mod libcollar;
+pub use libcollar::*;
diff --git a/crates/libcollar/src/libcollar.rs b/crates/libcollar/src/libcollar.rs
new file mode 100644
index 0000000..e245bf5
--- /dev/null
+++ b/crates/libcollar/src/libcollar.rs
@@ -0,0 +1,17 @@
+use crate::Result;
+
+pub struct State {
+ leash: net::Leash,
+}
+
+pub fn new() -> Result<State> {
+ Ok(State {
+ leash: net::Leash::new(),
+ })
+}
+
+impl State {
+ pub fn leash(&self) -> &net::Leash {
+ &self.leash
+ }
+}
diff --git a/src/lib.rs b/crates/libcollar/src/net/leash.rs
similarity index 95%
rename from src/lib.rs
rename to crates/libcollar/src/net/leash.rs
index 40b40a4..fce5a5b 100644
--- a/src/lib.rs
+++ b/crates/libcollar/src/net/leash.rs
@@ -1,45 +1,43 @@
-pub mod error;
-
use std::sync::Arc;
use tokio::sync::Mutex;
-use crate::error::{Error, Result};
+use crate::{Error, Result};
pub struct NetworkLeash {
lock: Arc<Mutex<()>>
}
impl NetworkLeash {
pub fn new() -> Self {
tracing::trace!("Creating new NetworkLeash");
Self {
lock: Arc::new(Mutex::new(())),
}
}
pub async fn with_interface<F, R>(&self, name: &str, operation: F) -> Result<R>
where
F: FnOnce(&mut ifconfig::Iface) -> Result<R> + Send + 'static,
R: Send + 'static,
{
let _guard = self.lock.lock().await;
let name = name.to_string();
tokio::task::spawn_blocking(move || {
let mut iface = ifconfig::Iface::new(&name)?;
operation(&mut iface)
}).await?
}
pub async fn gated<F, R>(&self, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R> + Send + 'static,
R: Send + 'static,
{
let _guard = self.lock.lock().await;
tokio::task::spawn_blocking(move || {
operation()
}).await?
}
}
diff --git a/crates/libcollar/src/net/mod.rs b/crates/libcollar/src/net/mod.rs
new file mode 100644
index 0000000..f27d84d
--- /dev/null
+++ b/crates/libcollar/src/net/mod.rs
@@ -0,0 +1,2 @@
+mod leash;
+pub use leash::NetworkLeash as Leash;
diff --git a/crates/libcollar/src/net/network.rs b/crates/libcollar/src/net/network.rs
new file mode 100644
index 0000000..d81246c
--- /dev/null
+++ b/crates/libcollar/src/net/network.rs
@@ -0,0 +1,5 @@
+pub struct Network {
+ store_id: u64,
+ name: String,
+ handler: String,
+}
diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml
new file mode 100644
index 0000000..e58282d
--- /dev/null
+++ b/crates/store/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "store"
+version = "0.1.0"
+license.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+thiserror.workspace = true
+sled = "0.34.7"
diff --git a/crates/store/src/error.rs b/crates/store/src/error.rs
new file mode 100644
index 0000000..e43e59c
--- /dev/null
+++ b/crates/store/src/error.rs
@@ -0,0 +1,7 @@
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ StoreError(#[from] sled::Error)
+}
+
+pub type Result<T, E = Error> = ::std::result::Result<T, E>;
diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs
new file mode 100644
index 0000000..d6e4129
--- /dev/null
+++ b/crates/store/src/lib.rs
@@ -0,0 +1,8 @@
+mod error;
+mod store;
+
+pub use error::{Result, Error};
+pub use store::Store;
+pub use store::open;
+
+mod namespace;
diff --git a/crates/store/src/namespace.rs b/crates/store/src/namespace.rs
new file mode 100644
index 0000000..b9b849c
--- /dev/null
+++ b/crates/store/src/namespace.rs
@@ -0,0 +1,142 @@
+// This module implements "namespaces" which are buckets of unique values
+// that maps to keys elsewhere in storage.
+//
+// the `names` tree is a k/v of `name` -> `id` where `id` is a u64 from `generate_id`.
+// the `spaces` tree is a k/v of `id` -> `name` where `id` is a u64 the `names` tree id and `name` is a str.
+use crate::Result;
+
+#[derive(Debug, Clone)]
+pub struct NamespaceStore {
+ names: sled::Tree,
+ spaces: sled::Tree,
+}
+
+impl NamespaceStore {
+ pub(crate) fn open(db: &sled::Db) -> Result<Self> {
+ Ok(NamespaceStore {
+ names: db.open_tree("namespaces/1/n")?,
+ spaces: db.open_tree("namespaces/1/s")?,
+ })
+ }
+
+ // define a namespace.
+ // inserts a key `namespace` into `names`, where the value is a random u64.
+ pub fn define(&self, namespace: &str) -> Result<()> {
+ self.names.transaction(|db| {
+ match db.get(namespace)? {
+ Some(_) => Ok(()),
+ None => {
+ let id = db.generate_id()?;
+ db.insert(namespace, &id.to_be_bytes())?;
+ Ok(())
+ }
+ }
+ })?;
+ Ok(())
+ }
+
+ pub fn resolve(&self, namespace: &str, key: &str) -> Result<bool> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(false), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Check if key exists in spaces
+ Ok(spaces.get(&composite_key)?.is_some())
+ })?;
+ Ok(result)
+ }
+
+ pub fn reserve(&self, namespace: &str, key: &str, value: &str) -> Result<bool> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(false), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Check if key already exists
+ if spaces.get(&composite_key)?.is_some() {
+ return Ok(false); // key already exists
+ }
+
+ // Insert the key-value pair
+ spaces.insert(&composite_key, value.as_bytes())?;
+ Ok(true)
+ })?;
+ Ok(result)
+ }
+
+ pub fn get(&self, namespace: &str, key: &str) -> Result<Option<String>> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(None), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Get value from spaces
+ match spaces.get(&composite_key)? {
+ Some(value_bytes) => {
+ let value = String::from_utf8(value_bytes.to_vec())
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ Ok(Some(value))
+ }
+ None => Ok(None),
+ }
+ })?;
+ Ok(result)
+ }
+
+ pub fn remove(&self, namespace: &str, key: &str) -> Result<bool> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(false), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Remove the key-value pair
+ Ok(spaces.remove(&composite_key)?.is_some())
+ })?;
+ Ok(result)
+ }
+}
\ No newline at end of file
diff --git a/crates/store/src/store.rs b/crates/store/src/store.rs
new file mode 100644
index 0000000..ab9d6c2
--- /dev/null
+++ b/crates/store/src/store.rs
@@ -0,0 +1,42 @@
+use crate::Result;
+
+#[derive(Debug, Clone)]
+pub struct Db {
+ prefix: String,
+ db: sled::Db
+}
+
+#[derive(Debug, Clone)]
+pub struct Store {
+ db: Db,
+ namespaces: namespaces::NamespaceStore,
+}
+
+impl Db {
+ pub fn open(path: String, prefix: String) -> Result<Self> {
+ let db = sled::open(path)?;
+ Ok(Db { prefix, db })
+ }
+
+ pub fn open_tree(&self, name: &str) -> Result<sled::Tree> {
+ self.db.open_tree(self.tree_path(name))
+ }
+
+ pub fn tree_path(&self, name: &str) -> String {
+ format!("t1/{}/{}", self.prefix, name)
+ }
+}
+
+pub fn open() -> Result<Store> {
+ let db = Db::open("libcollar_store".to_string(), "bonefire".to_string())?;
+ Ok(Store {
+ db: db,
+ namespaces: crate::namespaces::open(&db)?,
+ })
+}
+
+impl Store {
+ fn tree_path(&self, name: &str) -> String {
+ make_tree_path(self.prefix, name)
+ }
+}
diff --git a/src/bin/collar-ng.rs b/src/bin/collar-ng.rs
index 1d08d62..e0e134c 100644
--- a/src/bin/collar-ng.rs
+++ b/src/bin/collar-ng.rs
@@ -1,68 +1,68 @@
use std::env::args;
use env_logger::Env;
use log::error;
-use ng;
+use libcollar::ng;
fn main() {
let env = Env::default()
.filter_or("LOG_LEVEL", "trace")
.write_style_or("LOG_STYLE", "always");
env_logger::init_from_env(env);
let mut argv = args();
let prog = argv.next().unwrap();
match argv.next() {
None => println!("Usage: {} <command> (args)", prog),
Some(str) => match str.as_str() {
"new-eiface" => {
match argv.next() {
None => println!("Usage: {} new-eiface <name>", prog),
Some(name) => match ng::new_eiface(&name) {
Ok(s) => println!("ok: {:#?}", s),
Err(e) => error!("error: {:?}", e),
}
}
},
"new-eiface-bridge" => {
match argv.next() {
None => println!("Usage: {} new-bridge <bridge> <name>", prog),
Some(bridge) => match argv.next() {
None => println!("Usage: {} new-bridge <bridge> <name>", prog),
Some(name) => match ng::new_bridge(&bridge, &name) {
Ok(s) => println!("ok: {}", s),
Err(e) => error!("error: {:?}", e)
}
}
}
}
"join-eiface-bridge" => {
match argv.next() {
None => println!("Usage: {} bridge-eiface <bridge> <name>", prog),
Some(bridge) => match argv.next() {
None => println!("Usage: {} bridge-eiface <bridge> <name>", prog),
Some(name) => match ng::bridge_eiface(&bridge, &name) {
Ok(s) => println!("ok: {}", s),
Err(e) => error!("error: {:?}", e)
}
}
}
}
"bridge" => {
match argv.next() {
None => println!("Usage: {} <path>", prog),
Some(path) => match ng::go(&path) {
Ok(s) => println!("{}", s),
Err(e) => error!("error: {:?}", e)
}
}
}
_ => println!("Unknown action: {}", str)
}
}
}
diff --git a/src/bin/collared.rs b/src/bin/collared.rs
index 62580e8..7b6fbbb 100644
--- a/src/bin/collared.rs
+++ b/src/bin/collared.rs
@@ -1,53 +1,27 @@
use std::env::args;
+use libcollar;
+use api::app;
use tokio::net::TcpListener;
-use tower_http::trace::TraceLayer;
-use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
-
-use std::sync::Arc;
-
-use axum::{
- routing::get,
- extract::{State, Request, Json, Path, Extension, Query},
- routing::post,
-};
-
-struct AppState {
- leash: collar::NetworkLeash,
-}
+use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
- tracing_subscriber::filter::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
- "collar=trace,tower_http=debug,axum::rejection=trace"
+ EnvFilter::try_from_default_env().unwrap_or_else(|_| {
+ "collar=trace,libcollar=trace,ng=trace,ifconfig=trace,api=trace,store=trace,tower_http=debug,axum::rejection=trace"
.into()
}),
)
.with(tracing_subscriber::fmt::layer())
.init();
- let leash = collar::NetworkLeash::new();
- let state = Arc::new(AppState { leash });
-
- // build our application with a single route
- let app = axum::Router::new()
- .route("/", get(|| async { "collared." }))
- .route("/ng/eiface/{name}", post(post_ng_eiface))
- .with_state(state)
- .layer(tower_http::trace::TraceLayer::new_for_http());
+ let app = app(libcollar::new().expect("Failed to initialize libcollar"));
- // run our app with hyper, listening globally on port 3000
- let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
+ let listen = "0.0.0.0:3000";
+ let listener = TcpListener::bind(listen).await.expect("Failed to bind listener");
tracing::info!("listening on {}", listener.local_addr().unwrap());
- axum::serve(listener, app).await.unwrap();
-}
-async fn post_ng_eiface(State(state): State<Arc<AppState>>, Path(name): Path<String>) -> Result<String, String> {
- let fname = name.clone();
- let result = state.leash.gated(move || {
- Ok(ng::new_eiface(&name))
- }).await;
- Ok(format!("sup {} => {:?}", &fname, result))
+ api::serve(listener, app).await.unwrap();
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jun 8, 8:09 AM (21 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
47577
Default Alt Text
(18 KB)
Attached To
rCOLLAR collar
Event Timeline
Log In to Comment