use clap::Parser;
#[derive(Parser, Debug)]
#[command(name = "ntptime-rs", about = "NTP kernel time management", version)]
struct Cli {
#[arg(short = 'v', long)]
verbose: bool,
}
fn status_str(code: i32) -> &'static str {
match code {
libc::TIME_OK => "OK",
libc::TIME_INS => "INS",
libc::TIME_DEL => "DEL",
libc::TIME_OOP => "OOP",
libc::TIME_WAIT => "WAIT",
libc::TIME_ERROR => "ERROR",
_ => "UNKNOWN",
}
}
fn status_flags_str(status: i32) -> String {
let mut flags = Vec::new();
if status & libc::STA_PLL != 0 {
flags.push("PLL");
}
if status & libc::STA_PPSFREQ != 0 {
flags.push("PPSFREQ");
}
if status & libc::STA_PPSTIME != 0 {
flags.push("PPSTIME");
}
if status & libc::STA_FLL != 0 {
flags.push("FLL");
}
if status & libc::STA_INS != 0 {
flags.push("INS");
}
if status & libc::STA_DEL != 0 {
flags.push("DEL");
}
if status & libc::STA_UNSYNC != 0 {
flags.push("UNSYNC");
}
if status & libc::STA_FREQHOLD != 0 {
flags.push("FREQHOLD");
}
if status & libc::STA_PPSSIGNAL != 0 {
flags.push("PPSSIGNAL");
}
if status & libc::STA_PPSJITTER != 0 {
flags.push("PPSJITTER");
}
if status & libc::STA_PPSWANDER != 0 {
flags.push("PPSWANDER");
}
if status & libc::STA_PPSERROR != 0 {
flags.push("PPSERROR");
}
if status & libc::STA_CLOCKERR != 0 {
flags.push("CLOCKERR");
}
if status & libc::STA_NANO != 0 {
flags.push("NANO");
}
if status & libc::STA_MODE != 0 {
flags.push("MODE");
}
if status & libc::STA_CLK != 0 {
flags.push("CLK");
}
flags.join(",")
}
fn main() {
let cli = Cli::parse();
let mut tmx: libc::timex = unsafe { std::mem::zeroed() };
let rc = unsafe { libc::adjtimex(&mut tmx) };
if rc < 0 {
eprintln!("adjtimex failed: {}", std::io::Error::last_os_error());
std::process::exit(1);
}
println!("ntp_gettime() returns code {} ({})", rc, status_str(rc));
let nano = (tmx.status & libc::STA_NANO) != 0;
let unit_str = if nano { "ns" } else { "us" };
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default();
let secs = now.as_secs() as i64;
let ut = unsafe { libc::localtime(&secs) };
if !ut.is_null() {
let tm = unsafe { *ut };
println!(
" time {:08x}.{:08x} {:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}",
secs as u64,
if nano {
now.subsec_nanos()
} else {
now.subsec_micros()
},
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
if nano {
now.subsec_nanos() / 1_000_000
} else {
now.subsec_micros()
},
);
}
println!(
" maximum error {} us, estimated error {} us",
tmx.maxerror, tmx.esterror
);
println!(" TAI offset: {}", tmx.tai);
let freq_ppm = tmx.freq as f64 / 65536.0;
println!(
" status: 0x{:04x} ({})",
tmx.status,
status_flags_str(tmx.status),
);
println!(
" pll offset: {} {}, frequency: {:.3} ppm, maximum jitter: {} {}",
tmx.offset,
unit_str,
freq_ppm,
if nano { tmx.jitter / 1000 } else { tmx.jitter },
unit_str,
);
println!(
" interval: {} s, sanity: {}",
tmx.constant,
if rc == libc::TIME_OK as i32 {
"PASS"
} else {
"FAIL"
}
);
if cli.verbose {
println!();
println!("ntp_adjtime() returns code {} ({})", rc, status_str(rc));
println!(" mode: 0x{:x} (none)", tmx.modes);
println!(
" offset: {}, freq: {}, maxerror: {}, esterror: {}",
tmx.offset, tmx.freq, tmx.maxerror, tmx.esterror
);
println!(
" status: 0x{:04x}, constant: {}, precision: {}",
tmx.status, tmx.constant, tmx.precision
);
println!(
" tolerance: {:.3} ppm, ppsfrequency: {}, jitter: {}",
tmx.tolerance as f64 / 65536.0,
tmx.ppsfreq,
tmx.jitter,
);
println!(
" shift: {}, stabil: {}, jitcnt: {}, calcnt: {}, errcnt: {}, stbcnt: {}",
tmx.shift, tmx.stabil, tmx.jitcnt, tmx.calcnt, tmx.errcnt, tmx.stbcnt,
);
}
}