WIP
Some checks failed
Build and test / check (push) Failing after 1m6s
Build and test / build (push) Successful in 1m12s
Build and test / test (push) Failing after 1m17s
Build and test / docs (push) Failing after 2m44s

This commit is contained in:
2026-01-04 20:16:59 +09:00
parent b12752bd17
commit 977596b385
4 changed files with 127 additions and 13 deletions

19
Cargo.lock generated
View File

@@ -85,6 +85,12 @@ version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "cc"
version = "1.2.51"
@@ -556,6 +562,7 @@ name = "roowho2"
version = "0.1.0"
dependencies = [
"anyhow",
"bytes",
"chrono",
"clap",
"nix",
@@ -753,9 +760,21 @@ dependencies = [
"mio",
"pin-project-lite",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "type-map"
version = "0.5.1"

View File

@@ -15,10 +15,11 @@ autolib = false
[dependencies]
anyhow = "1.0.100"
bytes = "1.11.0"
chrono = { version = "0.4.42", features = ["serde"] }
clap = { version = "4.5.53", features = ["derive"] }
nix = { version = "0.30.1", features = ["hostname", "net"] }
tokio = { version = "1.49.0", features = ["net", "rt-multi-thread"] }
tokio = { version = "1.49.0", features = ["macros", "net", "rt-multi-thread"] }
# onc-rpc = "0.3.2"
# sd-notify = "0.4.5"
# serde = { version = "1.0.228", features = ["derive"] }

View File

@@ -1,10 +1,29 @@
fn main() {
println!(
"{:#?}",
roowho2_lib::server::rwhod::generate_rwhod_status_update(),
);
println!(
"{:#?}",
roowho2_lib::server::rwhod::determine_relevant_interfaces(),
use std::net::SocketAddrV4;
use roowho2_lib::proto::Whod;
const RWHOD_BROADCAST_PORT: u16 = 513;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let addr = SocketAddrV4::new(
std::net::Ipv4Addr::UNSPECIFIED,
RWHOD_BROADCAST_PORT,
);
let socket = tokio::net::UdpSocket::bind(addr).await?;
socket.set_broadcast(true)?;
let mut buf = [0u8; Whod::SIZE];
loop {
let (len, src) = socket.recv_from(&mut buf).await?;
if len == Whod::SIZE {
if let Ok(whod) = Whod::from_bytes(&buf) {
println!("Received whod packet from {}: {:?}", src, whod);
} else {
println!("Received malformed whod packet from {}", src);
}
} else {
println!("Received unexpected packet size {} from {}", len, src);
}
}
}

View File

@@ -56,13 +56,88 @@ impl Whod {
// NOTE: there was probably meant to be more packet types, but only status is defined.
pub const WHODTYPE_STATUS: u8 = 1;
pub fn to_bytes(&self) -> [u8; std::mem::size_of::<Whod>()] {
pub fn to_bytes(&self) -> [u8; Whod::SIZE] {
unsafe { std::mem::transmute_copy(self) }
}
// TODO: we should probably make a safer parser.
pub fn from_bytes(bytes: &[u8; std::mem::size_of::<Whod>()]) -> Self {
unsafe { std::mem::transmute_copy(bytes) }
// TODO: use the bytes crate to make this simpler and safer
pub fn from_bytes(bytes: &[u8; Whod::SIZE]) -> anyhow::Result<Self> {
let mut i = 0;
let wd_vers = bytes[i];
i += 1;
let wd_type = bytes[1];
i += 1;
i += 2; // skip wd_pad
let wd_sendtime = i32::from_le_bytes(bytes[i..i + 4].try_into()?);
i += 4;
let wd_recvtime = i32::from_le_bytes(bytes[i..i + 4].try_into()?);
i += 4;
let mut wd_hostname = [0u8; Self::MAX_HOSTNAME_LEN];
wd_hostname.copy_from_slice(&bytes[i..i + Self::MAX_HOSTNAME_LEN]);
i += Self::MAX_HOSTNAME_LEN;
let wd_loadav = [
i32::from_le_bytes(bytes[i..i + 4].try_into()?),
i32::from_le_bytes(bytes[i + 4..i + 8].try_into()?),
i32::from_le_bytes(bytes[i + 8..i + 12].try_into()?),
];
i += 4 * 3;
let wd_boottime = i32::from_le_bytes(bytes[i..i + 4].try_into()?);
i += 4;
let mut wd_we = array::from_fn(|_| Whoent {
we_utmp: Outmp {
out_line: [0u8; Outmp::MAX_TTY_NAME_LEN],
out_name: [0u8; Outmp::MAX_USER_ID_LEN],
out_time: 0,
},
we_idle: 0,
});
for entry in wd_we.iter_mut() {
let mut out_line = [0u8; Outmp::MAX_TTY_NAME_LEN];
out_line.copy_from_slice(&bytes[i..i + Outmp::MAX_TTY_NAME_LEN]);
i += Outmp::MAX_TTY_NAME_LEN;
let mut out_name = [0u8; Outmp::MAX_USER_ID_LEN];
out_name.copy_from_slice(&bytes[i..i + Outmp::MAX_USER_ID_LEN]);
i += Outmp::MAX_USER_ID_LEN;
let out_time = i32::from_le_bytes(bytes[i..i + 4].try_into()?);
i += 4;
let we_idle = i32::from_le_bytes(bytes[i..i + 4].try_into()?);
i += 4;
entry.we_utmp = Outmp {
out_line,
out_name,
out_time,
};
entry.we_idle = we_idle;
}
debug_assert!(i == Whod::SIZE);
Ok(Whod {
wd_vers,
wd_type,
wd_pad: [0u8; 2],
wd_sendtime,
wd_recvtime,
wd_hostname,
wd_loadav,
wd_boottime,
wd_we,
})
}
}