This commit is contained in:
Andrei Cravtov committed 2026-04-20 17:39:35 +01:00
1 parent cb060dc532
commit dda297c268
2 files changed
+85 -21

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
//! Typed representation of commands sent to `babeld`'s local socket.
//!
//! This is the outbound counterpart to [`crate::babel::line`]:
//!
//! - [`crate::babel::line`] models what `babeld` emits
//! - this module models the runtime control lines that `babblerd` sends
//!
//! The scope here is intentionally narrow: this module only models the local-socket
//! commands that `babblerd` currently issues at runtime.
//!
//! NOTE: spawn-time `-C` configuration strings are still assembled in the process layer for now;
//! they are the next obvious thing to extract once the session loop is no longer
//! stringly-typed.
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BabelCommand {
Dump,
Monitor,
Unmonitor,
Quit,
Interface(Box<str>),
}
impl fmt::Display for BabelCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Dump => f.write_str("dump"),
Self::Monitor => f.write_str("monitor"),
Self::Unmonitor => f.write_str("unmonitor"),
Self::Quit => f.write_str("quit"),
Self::Interface(ifname) => write!(f, "interface {ifname}"),
}
}
}
#[cfg(test)]
mod tests {
use super::BabelCommand;
#[test]
fn renders_commands() {
assert_eq!(BabelCommand::Dump.to_string(), "dump");
assert_eq!(BabelCommand::Monitor.to_string(), "monitor");
assert_eq!(BabelCommand::Unmonitor.to_string(), "unmonitor");
assert_eq!(BabelCommand::Quit.to_string(), "quit");
assert_eq!(
BabelCommand::Interface("en2".into()).to_string(),
"interface en2"
);
}
}
+32 -21
View File
@@ -6,13 +6,15 @@ use tokio::time::Duration;
use futures_lite::FutureExt;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Lines};
use tokio::net::UnixStream;
use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::UnixStream;
use tokio::process::Command;
use tokio::sync::{broadcast, mpsc};
use crate::babel::line::{BabelLine, Status};
use crate::{BabbleError, Result};
pub mod command;
pub mod line;
#[tracing::instrument(skip_all)]
@@ -113,33 +115,41 @@ impl BabeldProcess {
read: &mut Lines<BufReader<OwnedReadHalf>>,
write: &mut OwnedWriteHalf,
send: &broadcast::Sender<String>,
cmd: &str,
cmd: &command::BabelCommand,
) -> io::Result<Option<bool>> {
write.write_all(cmd.as_bytes()).await?;
let encoded = cmd.to_string();
write.write_all(encoded.as_bytes()).await?;
write.write_all(b"\n").await?;
loop {
// Ok(None) is only ever returned when stream is closed.
// When parsing errors occur from babeld, those Ok(None)'s are ignored in the loop
let Some(line) = read.next_line().await? else {
tracing::warn!("babeld closed unexpectedly");
return Ok(None);
};
tracing::info!("[babel] {:?}", line);
// TODO: replace later with propper parsing
match line::parse::parse_line(&line) {
Ok(line) => tracing::info!("[parsed] {:?}", line),
Err(err) => tracing::error!(error=%err, "failed to parse babeld line"),
}
let ret = match line.as_str() {
"ok" => Ok(Some(true)),
"bad" => {
tracing::warn!("malformed message sent to babeld");
Ok(Some(false))
let ret = match line::parse::parse_line(&line) {
Ok(parsed) => {
tracing::info!("[parsed] {:?}", parsed);
match parsed {
BabelLine::Status(Status::Ok) => Ok(Some(true)),
BabelLine::Status(Status::Bad) => {
tracing::warn!("malformed message sent to babeld");
Ok(Some(false))
}
BabelLine::Status(Status::No(rest)) => {
tracing::warn!("message rejected: {rest:?}");
Ok(Some(false))
}
_ => Ok(None),
}
}
_ if line.starts_with("no") => {
tracing::warn!("message rejected");
Ok(Some(false))
Err(err) => {
tracing::error!(error=%err, "failed to parse babeld line");
Ok(None)
}
_ => Ok(None),
};
let Ok(_) = send.send(line) else {
return Ok(None);
@@ -168,7 +178,7 @@ impl BabeldProcess {
}
tracing::info!("babeld ok");
/* TODO(evan): push rather than pull
if Self::query(&mut babel_lines, &mut writer, "monitor\n")
if Self::query(&mut babel_lines, &mut writer, &command::BabelCommand::Monitor)
.await?
.is_none()
{
@@ -181,7 +191,7 @@ impl BabeldProcess {
loop {
tokio::select! {
_ = interval.tick() => {
if Self::query(&mut babel_lines, &mut writer, &send, "dump\n")
if Self::query(&mut babel_lines, &mut writer, &send, &command::BabelCommand::Dump)
.await?
.is_none()
{
@@ -195,7 +205,8 @@ impl BabeldProcess {
};
match babble {
Babble::AddIface(iface) => {
Self::query(&mut babel_lines, &mut writer, &send, format!("interface {iface}\n").as_ref()).await?;
let cmd = command::BabelCommand::Interface(iface.into_boxed_str());
Self::query(&mut babel_lines, &mut writer, &send, &cmd).await?;
}
}
},