use std::fmt;
use crate::ast::*;
impl fmt::Display for ChronyConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for node in &self.nodes {
write!(f, "{node}")?;
}
Ok(())
}
}
impl fmt::Display for ConfigNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Comment(s) => writeln!(f, "# {s}"),
Self::BlankLine => writeln!(f),
Self::Directive(d) => {
for comment in &d.leading_comments {
writeln!(f, "# {comment}")?;
}
fmt_directive_kind(f, &d.kind)?;
if let Some(ref tc) = d.trailing_comment {
write!(f, " # {tc}")?;
}
writeln!(f)
}
}
}
}
fn fmt_directive_kind(f: &mut fmt::Formatter<'_>, kind: &DirectiveKind) -> fmt::Result {
match kind {
DirectiveKind::Server(c) => write_server(f, c),
DirectiveKind::Pool(c) => {
write!(f, "pool {}", c.source.hostname)?;
write_server_options(f, &c.source)?;
if c.max_sources != 4 {
write!(f, " maxsources {}", c.max_sources)?;
}
Ok(())
}
DirectiveKind::Peer(c) => {
write!(f, "peer {}", c.hostname)?;
write_server_options(f, c)?;
Ok(())
}
DirectiveKind::InitStepSlew(c) => {
write!(f, "initstepslew {}", c.threshold)?;
for hostname in &c.hostnames {
write!(f, " {hostname}")?;
}
Ok(())
}
DirectiveKind::RefClock(c) => write_refclock(f, c),
DirectiveKind::Manual => write!(f, "manual"),
DirectiveKind::AcquisitionPort(c) => write!(f, "acquisitionport {}", c.port),
DirectiveKind::BindAcqAddress(c) => write!(f, "bindacqaddress {}", c.address),
DirectiveKind::BindAcqDevice(c) => write!(f, "bindacqdevice {}", c.interface),
DirectiveKind::Dscp(c) => write!(f, "dscp {}", c.dscp),
DirectiveKind::DumpDir(c) => write!(f, "dumpdir {}", c.directory),
DirectiveKind::MaxSamples(c) => write!(f, "maxsamples {}", c.samples),
DirectiveKind::MinSamples(c) => write!(f, "minsamples {}", c.samples),
DirectiveKind::NtsDumpDir(c) => write!(f, "ntsdumpdir {}", c.directory),
DirectiveKind::NtsRefresh(c) => write!(f, "ntsrefresh {}", c.interval),
DirectiveKind::NtsTrustedCerts(c) => {
if c.set_id != 0 {
write!(f, "ntstrustedcerts {} {}", c.set_id, c.path)
} else {
write!(f, "ntstrustedcerts {}", c.path)
}
}
DirectiveKind::NoSystemCert => write!(f, "nosystemcert"),
DirectiveKind::NoCertTimeCheck(c) => write!(f, "nocerttimecheck {}", c.limit),
DirectiveKind::Refresh(c) => write!(f, "refresh {}", c.interval),
DirectiveKind::AuthSelectMode(m) => write!(f, "authselectmode {}", format_enum(m)),
DirectiveKind::CombineLimit(c) => write!(f, "combinelimit {}", c.limit),
DirectiveKind::MaxDistance(c) => write!(f, "maxdistance {}", c.distance),
DirectiveKind::MaxJitter(c) => write!(f, "maxjitter {}", c.jitter),
DirectiveKind::MinSources(c) => write!(f, "minsources {}", c.sources),
DirectiveKind::ReselectDist(c) => write!(f, "reselectdist {}", c.distance),
DirectiveKind::StratumWeight(c) => write!(f, "stratumweight {}", c.distance),
DirectiveKind::ClockPrecision(c) => write!(f, "clockprecision {}", c.precision),
DirectiveKind::CorrTimeRatio(c) => write!(f, "corrtimeratio {}", c.ratio),
DirectiveKind::DriftFile(c) => {
write!(f, "driftfile {}", c.path)?;
if let Some(interval) = c.interval {
write!(f, " interval {interval}")?;
}
Ok(())
}
DirectiveKind::FallbackDrift(c) => write!(f, "fallbackdrift {} {}", c.min, c.max),
DirectiveKind::LeapSecMode(m) => write!(f, "leapsecmode {}", format_enum(m)),
DirectiveKind::LeapSecTz(c) => write!(f, "leapsectz {}", c.timezone),
DirectiveKind::LeapSecList(c) => write!(f, "leapseclist {}", c.file),
DirectiveKind::MakeStep(c) => write!(f, "makestep {} {}", c.threshold, c.limit),
DirectiveKind::MaxChange(c) => write!(f, "maxchange {} {} {}", c.offset, c.start, c.ignore),
DirectiveKind::MaxClockError(c) => write!(f, "maxclockerror {}", c.error_ppm),
DirectiveKind::MaxDrift(c) => write!(f, "maxdrift {}", c.drift_ppm),
DirectiveKind::MaxUpdateSkew(c) => write!(f, "maxupdateskew {}", c.skew_ppm),
DirectiveKind::MaxSlewRate(c) => write!(f, "maxslewrate {}", c.rate_ppm),
DirectiveKind::TempComp(c) => match c {
TempCompConfig::Coefficients {
file,
interval,
t0,
k0,
k1,
k2,
} => write!(f, "tempcomp {file} {interval} {t0} {k0} {k1} {k2}"),
TempCompConfig::PointFile {
file,
interval,
points_file,
} => write!(f, "tempcomp {file} {interval} {points_file}"),
},
DirectiveKind::Allow(c) => write_allow_deny(f, "allow", c),
DirectiveKind::Deny(c) => write_allow_deny(f, "deny", c),
DirectiveKind::BindAddress(c) => write!(f, "bindaddress {}", c.address),
DirectiveKind::BindDevice(c) => write!(f, "binddevice {}", c.interface),
DirectiveKind::Broadcast(c) => write!(f, "broadcast {} {} {}", c.interval, c.address, c.port),
DirectiveKind::ClientLogLimit(c) => write!(f, "clientloglimit {}", c.limit),
DirectiveKind::NoClientLog => write!(f, "noclientlog"),
DirectiveKind::Local(c) => {
write!(f, "local")?;
if c.stratum.get() != 10 {
write!(f, " stratum {}", c.stratum)?;
}
if c.orphan {
write!(f, " orphan")?;
}
if (c.distance - 1.0).abs() > 1e-12 {
write!(f, " distance {}", c.distance)?;
}
Ok(())
}
DirectiveKind::NtpSignDSocket(c) => write!(f, "ntpsigndsocket {}", c.directory),
DirectiveKind::NtsPort(c) => write!(f, "ntsport {}", c.port),
DirectiveKind::NtsServerCert(c) => write!(f, "ntsservercert {}", c.file),
DirectiveKind::NtsServerKey(c) => write!(f, "ntsserverkey {}", c.file),
DirectiveKind::NtsProcesses(c) => write!(f, "ntsprocesses {}", c.processes),
DirectiveKind::MaxNtsConnections(c) => write!(f, "maxntsconnections {}", c.connections),
DirectiveKind::NtsNtpServer(c) => write!(f, "ntsntpserver {}", c.hostname),
DirectiveKind::NtsRotate(c) => write!(f, "ntsrotate {}", c.interval),
DirectiveKind::Port(c) => write!(f, "port {}", c.port),
DirectiveKind::RateLimit(c) => write!(
f,
"ratelimit interval {} burst {} leak {}{}",
c.interval,
c.burst,
c.leak,
c.kod.map_or(String::new(), |k| format!(" kod {k}"))
),
DirectiveKind::NtsRateLimit(c) => {
write!(f, "ntsratelimit interval {} burst {} leak {}", c.interval, c.burst, c.leak)
}
DirectiveKind::SmoothTime(c) => {
write!(f, "smoothtime {} {}", c.max_freq, c.max_wander)?;
if c.leap_only {
write!(f, " leaponly")?;
}
Ok(())
}
DirectiveKind::BindCmdAddress(c) => write!(f, "bindcmdaddress {}", c.address),
DirectiveKind::BindCmdDevice(c) => write!(f, "bindcmddevice {}", c.interface),
DirectiveKind::CmdAllow(c) => write_allow_deny(f, "cmdallow", c),
DirectiveKind::CmdDeny(c) => write_allow_deny(f, "cmddeny", c),
DirectiveKind::CmdPort(c) => write!(f, "cmdport {}", c.port),
DirectiveKind::CmdRateLimit(c) => {
write!(f, "cmdratelimit interval {} burst {} leak {}", c.interval, c.burst, c.leak)
}
DirectiveKind::OpenCommands(c) => {
write!(f, "opencommands")?;
for cmd in &c.commands {
write!(f, " {cmd}")?;
}
Ok(())
}
DirectiveKind::HwClockFile(c) => write!(f, "hwclockfile {}", c.file),
DirectiveKind::RtcAutoTrim(c) => write!(f, "rtcautotrim {}", c.threshold),
DirectiveKind::RtcDevice(c) => write!(f, "rtcdevice {}", c.device),
DirectiveKind::RtcFile(c) => write!(f, "rtcfile {}", c.file),
DirectiveKind::RtcOnUtc => write!(f, "rtconutc"),
DirectiveKind::RtcSync => write!(f, "rtcsync"),
DirectiveKind::Log(c) => {
write!(f, "log")?;
if c.raw_measurements {
write!(f, " rawmeasurements")?;
}
if c.measurements && !c.raw_measurements {
write!(f, " measurements")?;
}
if c.selection {
write!(f, " selection")?;
}
if c.statistics {
write!(f, " statistics")?;
}
if c.tracking {
write!(f, " tracking")?;
}
if c.rtc {
write!(f, " rtc")?;
}
if c.refclocks {
write!(f, " refclocks")?;
}
if c.tempcomp {
write!(f, " tempcomp")?;
}
Ok(())
}
DirectiveKind::LogBanner(c) => write!(f, "logbanner {}", c.limit),
DirectiveKind::LogChange(c) => write!(f, "logchange {}", c.threshold),
DirectiveKind::LogDir(c) => write!(f, "logdir {}", c.directory),
DirectiveKind::HwTimestamp(c) => {
write!(f, "hwtimestamp {}", c.interface)?;
if c.minpoll.get() != 0 {
write!(f, " minpoll {}", c.minpoll)?;
}
if c.nocrossts {
write!(f, " nocrossts")?;
}
Ok(())
}
DirectiveKind::HwTsTimeout(c) => write!(f, "hwtstimeout {}", c.timeout),
DirectiveKind::MaxTxBuffers(c) => write!(f, "maxtxbuffers {}", c.buffers),
DirectiveKind::PtpPort(c) => write!(f, "ptpport {}", c.port),
DirectiveKind::PtpDomain(c) => write!(f, "ptpdomain {}", c.domain),
DirectiveKind::NtsAeads(c) => {
write!(f, "ntsaeads")?;
for id in &c.ids {
write!(f, " {id}")?;
}
Ok(())
}
DirectiveKind::ConfDir(c) => write!(f, "confdir {}", c.directory),
DirectiveKind::Include(c) => write!(f, "include {}", c.pattern),
DirectiveKind::SourceDir(c) => write!(f, "sourcedir {}", c.directory),
DirectiveKind::LockAll => write!(f, "lock_all"),
DirectiveKind::MailOnChange(c) => write!(f, "mailonchange {} {}", c.email, c.threshold),
DirectiveKind::PidFile(c) => write!(f, "pidfile {}", c.file),
DirectiveKind::SchedPriority(c) => write!(f, "sched_priority {}", c.priority),
DirectiveKind::User(c) => write!(f, "user {}", c.user),
DirectiveKind::KeyFile(c) => write!(f, "keyfile {}", c.file),
DirectiveKind::DumpOnExit => write!(f, "dumponexit"),
}
}
fn format_enum<T: fmt::Debug>(val: &T) -> String {
format!("{val:?}").to_lowercase()
}
fn write_server(f: &mut fmt::Formatter<'_>, c: &ServerConfig) -> fmt::Result {
write!(f, "server {}", c.hostname)?;
write_server_options(f, c)
}
fn write_server_options(f: &mut fmt::Formatter<'_>, c: &ServerConfig) -> fmt::Result {
if c.iburst {
write!(f, " iburst")?;
}
if c.burst {
write!(f, " burst")?;
}
if c.prefer() {
write!(f, " prefer")?;
}
if c.noselect() {
write!(f, " noselect")?;
}
if c.trust() {
write!(f, " trust")?;
}
if c.require() {
write!(f, " require")?;
}
if c.nts {
write!(f, " nts")?;
}
if c.xleave() {
write!(f, " xleave")?;
}
if c.copy {
write!(f, " copy")?;
}
if c.auto_offline {
write!(f, " auto_offline")?;
}
if matches!(c.connectivity, SourceConnectivity::Offline) {
write!(f, " offline")?;
}
if c.minpoll.get() != 6 {
write!(f, " minpoll {}", c.minpoll)?;
}
if c.maxpoll.get() != 10 {
write!(f, " maxpoll {}", c.maxpoll)?;
}
if c.port.get() != 123 {
write!(f, " port {}", c.port)?;
}
if (c.max_delay - 3.0).abs() > 1e-12 {
write!(f, " maxdelay {}", c.max_delay)?;
}
if (c.offset - 0.0).abs() > 1e-12 {
write!(f, " offset {}", c.offset)?;
}
if let Some(ref ver) = c.version {
write!(f, " version {}", ver)?;
}
if c.authkey != 0 {
write!(f, " key {}", c.authkey)?;
}
if c.cert_set != 0 {
write!(f, " certset {}", c.cert_set)?;
}
if (c.max_delay_ratio - 0.0).abs() > 1e-12 {
write!(f, " maxdelayratio {}", c.max_delay_ratio)?;
}
if (c.max_delay_dev_ratio - 10.0).abs() > 1e-12 {
write!(f, " maxdelaydevratio {}", c.max_delay_dev_ratio)?;
}
if (c.max_delay_quant - 0.0).abs() > 1e-12 {
write!(f, " maxdelayquant {}", c.max_delay_quant)?;
}
if (c.min_delay - 0.0).abs() > 1e-12 {
write!(f, " mindelay {}", c.min_delay)?;
}
if (c.asymmetry - 1.0).abs() > 1e-12 {
write!(f, " asymmetry {}", c.asymmetry)?;
}
if c.poll_target.get() != 8 {
write!(f, " polltarget {}", c.poll_target)?;
}
if c.min_stratum.get() != 0 {
write!(f, " minstratum {}", c.min_stratum)?;
}
if c.presend.get() != 0 {
write!(f, " presend {}", c.presend)?;
}
if let Some(ref filter) = c.filter {
write!(f, " filter {filter}")?;
}
if let Some(ref ms) = c.min_samples {
write!(f, " minsamples {ms}")?;
}
if let Some(ref ms) = c.max_samples {
write!(f, " maxsamples {ms}")?;
}
if c.ext_fields != 0 {
write!(f, " extfield {:x}", c.ext_fields)?;
}
if !matches!(c.family, AddressFamily::Unspec) {
match c.family {
AddressFamily::Inet4 => write!(f, " ipv4")?,
AddressFamily::Inet6 => write!(f, " ipv6")?,
_ => {}
}
}
Ok(())
}
fn write_allow_deny(f: &mut fmt::Formatter<'_>, name: &str, c: &AllowDenyConfig) -> fmt::Result {
write!(f, "{name}")?;
if c.all {
write!(f, " all")?;
}
if let Some(ref s) = c.subnet {
write!(f, " {s}")?;
}
Ok(())
}
fn write_refclock(f: &mut fmt::Formatter<'_>, c: &RefClockConfig) -> fmt::Result {
write!(f, "refclock {} {}", format_enum(&c.driver), c.parameter)?;
if c.poll.get() != 4 {
write!(f, " poll {}", c.poll)?;
}
if c.dpoll.get() != 0 {
write!(f, " dpoll {}", c.dpoll)?;
}
if c.pps_forced {
write!(f, " pps")?;
}
if c.tai {
write!(f, " tai")?;
}
if c.local {
write!(f, " local")?;
}
if c.select_opts.has(SelectOptions::PREFER) {
write!(f, " prefer")?;
}
if c.select_opts.has(SelectOptions::NOSELECT) {
write!(f, " noselect")?;
}
if c.select_opts.has(SelectOptions::TRUST) {
write!(f, " trust")?;
}
if c.select_opts.has(SelectOptions::REQUIRE) {
write!(f, " require")?;
}
Ok(())
}
#[cfg(feature = "serde")]
mod serde_impl {
use serde::ser::{Serialize, Serializer, SerializeStruct};
use crate::ast::*;
impl Serialize for ChronyConfig {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let directives: Vec<String> = self
.nodes
.iter()
.filter_map(|n| match n {
ConfigNode::Directive(d) => Some(d.kind.name().to_string()),
_ => None,
})
.collect();
let mut s = serializer.serialize_struct("ChronyConfig", 1)?;
s.serialize_field("directives", &directives)?;
s.end()
}
}
}