use std::mem; use std::os::unix::net::UnixDatagram; use std::path::PathBuf; use async_trait::async_trait; use libc::{c_double, c_int, timeval}; use tokio::select; use tokio_util::sync::CancellationToken; use tracing::{debug, info, warn}; use crate::{ChimemonMessage, ChimemonTarget, ChimemonTargetChannel, config::ChronySockConfig}; const CHRONY_MAGIC: c_int = 0x534f434b; pub struct ChronySockServer { name: String, sock_path: PathBuf, } #[repr(C)] #[derive(Debug)] pub struct ChronyTimeReport { tv: timeval, offset: c_double, pulse: c_int, leap: c_int, _pad: c_int, magic: c_int, } impl ChronySockServer {} #[async_trait] impl ChimemonTarget for ChronySockServer { type Config = ChronySockConfig; fn new(name: &str, config: ChronySockConfig) -> Self { ChronySockServer { name: name.to_owned(), sock_path: config.sock.into(), } } async fn run(mut self, mut chan: ChimemonTargetChannel, cancel: CancellationToken) { info!("Chrony refclock task started"); loop { select! { _ = cancel.cancelled() => { return } msg = chan.recv() => { match msg { Ok(ChimemonMessage::TimeReport(tr)) => { if tr.valid { { let frame = ChronyTimeReport { tv: timeval { tv_sec: TryInto::::try_into( tr.system_time.timestamp(), ) .unwrap(), tv_usec: tr.system_time.timestamp_subsec_micros() as libc::suseconds_t, }, offset: tr.offset.num_nanoseconds().unwrap() as f64 / 1e9, leap: if tr.leap_flag { 1 } else { 0 }, pulse: 0, _pad: 0, magic: CHRONY_MAGIC, }; let bs = unsafe { std::slice::from_raw_parts( (&frame as *const ChronyTimeReport) as *const u8, mem::size_of::(), ) }; debug!("Sending to chrony sock {:#?}", frame); let sock = UnixDatagram::unbound().unwrap(); sock.send_to(bs, &self.sock_path).unwrap(); } } }, Err(e) => warn!("Error receiving from channel: {}", e.to_string()), _ => continue, } } } } } }