feat: add Statime daemon integration for Linux and non-Linux stubs

Co-authored-by: aider (openai/gpt-5) <aider@aider.chat>
This commit is contained in:
Chaos Rogers 2025-10-21 23:06:08 +01:00
parent 323cc61ae3
commit 0239b56c1a
2 changed files with 164 additions and 0 deletions

View file

@ -22,6 +22,7 @@ daemonize = "0.5.0"
num-rational = "0.4" num-rational = "0.4"
num-traits = "0.2" num-traits = "0.2"
libc = "0.2" libc = "0.2"
which = "6"
[[bin]] [[bin]]
name = "ptp_probe" name = "ptp_probe"

View file

@ -121,3 +121,166 @@ pub use linux::{is_supported, PtpClock};
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
pub use non_linux::{is_supported, PtpClock}; pub use non_linux::{is_supported, PtpClock};
// -----------------------------------------------------------------------------
// Statime daemon integration
// This provides a thin wrapper to spawn and manage the 'statime' PTP daemon
// as a child process from within this application (Linux only).
// On non-Linux targets, it returns PtpError::Unsupported.
// -----------------------------------------------------------------------------
#[cfg(target_os = "linux")]
mod statime_linux_mod {
use std::io;
use std::path::PathBuf;
use std::process::{Child, Command, Stdio};
use super::PtpError;
#[derive(Debug, Clone)]
pub struct StatimeOptions {
pub interface: String,
pub phc_index: Option<u32>,
pub extra_args: Vec<String>,
}
impl StatimeOptions {
pub fn new<S: Into<String>>(interface: S) -> Self {
Self {
interface: interface.into(),
phc_index: None,
extra_args: Vec::new(),
}
}
pub fn with_phc_index(mut self, idx: u32) -> Self {
self.phc_index = Some(idx);
self
}
pub fn with_args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.extra_args = args.into_iter().map(|s| s.into()).collect();
self
}
}
pub struct StatimeDaemon {
child: Child,
}
impl StatimeDaemon {
pub fn is_running(&mut self) -> bool {
self.child.try_wait().ok().flatten().is_none()
}
pub fn pid(&self) -> u32 {
self.child.id()
}
pub fn stop(&mut self) -> Result<(), PtpError> {
self.child.kill().map_err(PtpError::Io)?;
let _ = self.child.wait();
Ok(())
}
}
impl Drop for StatimeDaemon {
fn drop(&mut self) {
let _ = self.stop();
}
}
fn find_statime_binary() -> Result<PathBuf, io::Error> {
which::which("statime").or_else(|_| {
let fallback = PathBuf::from("/usr/local/bin/statime");
if fallback.exists() {
Ok(fallback)
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"statime binary not found in PATH or /usr/local/bin",
))
}
})
}
pub fn spawn_statime(opts: StatimeOptions) -> Result<StatimeDaemon, PtpError> {
let bin = find_statime_binary().map_err(PtpError::Io)?;
let mut cmd = Command::new(bin);
cmd.arg("-i").arg(&opts.interface);
if let Some(idx) = opts.phc_index {
// Prefer --phc-index if supported by the installed statime version.
cmd.arg("--phc-index").arg(idx.to_string());
}
for a in &opts.extra_args {
cmd.arg(a);
}
// Inherit stdio for visibility; detach stdin.
cmd.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
let child = cmd.spawn().map_err(PtpError::Io)?;
Ok(StatimeDaemon { child })
}
}
#[cfg(not(target_os = "linux"))]
mod statime_nonlinux_mod {
use super::PtpError;
#[derive(Debug, Clone)]
pub struct StatimeOptions {
pub interface: String,
pub phc_index: Option<u32>,
pub extra_args: Vec<String>,
}
impl StatimeOptions {
pub fn new<S: Into<String>>(interface: S) -> Self {
Self {
interface: interface.into(),
phc_index: None,
extra_args: Vec::new(),
}
}
pub fn with_phc_index(self, _idx: u32) -> Self {
self
}
pub fn with_args<I, S>(self, _args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self
}
}
pub struct StatimeDaemon;
impl StatimeDaemon {
pub fn is_running(&mut self) -> bool {
false
}
pub fn pid(&self) -> u32 {
0
}
pub fn stop(&mut self) -> Result<(), PtpError> {
Err(PtpError::Unsupported)
}
}
pub fn spawn_statime(_opts: StatimeOptions) -> Result<StatimeDaemon, PtpError> {
Err(PtpError::Unsupported)
}
}
#[cfg(target_os = "linux")]
pub use statime_linux_mod::{spawn_statime, StatimeDaemon, StatimeOptions};
#[cfg(not(target_os = "linux"))]
pub use statime_nonlinux_mod::{spawn_statime, StatimeDaemon, StatimeOptions};