1use std::fmt;
8use crate::ast::*;
9
10impl fmt::Display for ChronyConfig {
11 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 for node in &self.nodes {
17 write!(f, "{node}")?;
18 }
19 Ok(())
20 }
21}
22
23impl fmt::Display for ConfigNode {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::Comment(s) => writeln!(f, "# {s}"),
28 Self::BlankLine => writeln!(f),
29 Self::Directive(d) => {
30 for comment in &d.leading_comments {
31 writeln!(f, "# {comment}")?;
32 }
33 fmt_directive_kind(f, &d.kind)?;
34 if let Some(ref tc) = d.trailing_comment {
35 write!(f, " # {tc}")?;
36 }
37 writeln!(f)
38 }
39 }
40 }
41}
42
43fn fmt_directive_kind(f: &mut fmt::Formatter<'_>, kind: &DirectiveKind) -> fmt::Result {
44 match kind {
45 DirectiveKind::Server(c) => write_server(f, c),
47 DirectiveKind::Pool(c) => {
48 write!(f, "pool {}", c.source.hostname)?;
49 write_server_options(f, &c.source)?;
50 if c.max_sources != 4 {
51 write!(f, " maxsources {}", c.max_sources)?;
52 }
53 Ok(())
54 }
55 DirectiveKind::Peer(c) => {
56 write!(f, "peer {}", c.hostname)?;
57 write_server_options(f, c)?;
58 Ok(())
59 }
60 DirectiveKind::InitStepSlew(c) => {
61 write!(f, "initstepslew {}", c.threshold)?;
62 for hostname in &c.hostnames {
63 write!(f, " {hostname}")?;
64 }
65 Ok(())
66 }
67 DirectiveKind::RefClock(c) => write_refclock(f, c),
68 DirectiveKind::Manual => write!(f, "manual"),
69 DirectiveKind::AcquisitionPort(c) => write!(f, "acquisitionport {}", c.port),
70 DirectiveKind::BindAcqAddress(c) => write!(f, "bindacqaddress {}", c.address),
71 DirectiveKind::BindAcqDevice(c) => write!(f, "bindacqdevice {}", c.interface),
72 DirectiveKind::Dscp(c) => write!(f, "dscp {}", c.dscp),
73 DirectiveKind::DumpDir(c) => write!(f, "dumpdir {}", c.directory),
74 DirectiveKind::MaxSamples(c) => write!(f, "maxsamples {}", c.samples),
75 DirectiveKind::MinSamples(c) => write!(f, "minsamples {}", c.samples),
76 DirectiveKind::NtsDumpDir(c) => write!(f, "ntsdumpdir {}", c.directory),
77 DirectiveKind::NtsRefresh(c) => write!(f, "ntsrefresh {}", c.interval),
78 DirectiveKind::NtsTrustedCerts(c) => {
79 if c.set_id != 0 {
80 write!(f, "ntstrustedcerts {} {}", c.set_id, c.path)
81 } else {
82 write!(f, "ntstrustedcerts {}", c.path)
83 }
84 }
85 DirectiveKind::NoSystemCert => write!(f, "nosystemcert"),
86 DirectiveKind::NoCertTimeCheck(c) => write!(f, "nocerttimecheck {}", c.limit),
87 DirectiveKind::Refresh(c) => write!(f, "refresh {}", c.interval),
88
89 DirectiveKind::AuthSelectMode(m) => write!(f, "authselectmode {}", format_enum(m)),
91 DirectiveKind::CombineLimit(c) => write!(f, "combinelimit {}", c.limit),
92 DirectiveKind::MaxDistance(c) => write!(f, "maxdistance {}", c.distance),
93 DirectiveKind::MaxJitter(c) => write!(f, "maxjitter {}", c.jitter),
94 DirectiveKind::MinSources(c) => write!(f, "minsources {}", c.sources),
95 DirectiveKind::ReselectDist(c) => write!(f, "reselectdist {}", c.distance),
96 DirectiveKind::StratumWeight(c) => write!(f, "stratumweight {}", c.distance),
97
98 DirectiveKind::ClockPrecision(c) => write!(f, "clockprecision {}", c.precision),
100 DirectiveKind::CorrTimeRatio(c) => write!(f, "corrtimeratio {}", c.ratio),
101 DirectiveKind::DriftFile(c) => {
102 write!(f, "driftfile {}", c.path)?;
103 if let Some(interval) = c.interval {
104 write!(f, " interval {interval}")?;
105 }
106 Ok(())
107 }
108 DirectiveKind::FallbackDrift(c) => write!(f, "fallbackdrift {} {}", c.min, c.max),
109 DirectiveKind::LeapSecMode(m) => write!(f, "leapsecmode {}", format_enum(m)),
110 DirectiveKind::LeapSecTz(c) => write!(f, "leapsectz {}", c.timezone),
111 DirectiveKind::LeapSecList(c) => write!(f, "leapseclist {}", c.file),
112 DirectiveKind::MakeStep(c) => write!(f, "makestep {} {}", c.threshold, c.limit),
113 DirectiveKind::MaxChange(c) => write!(f, "maxchange {} {} {}", c.offset, c.start, c.ignore),
114 DirectiveKind::MaxClockError(c) => write!(f, "maxclockerror {}", c.error_ppm),
115 DirectiveKind::MaxDrift(c) => write!(f, "maxdrift {}", c.drift_ppm),
116 DirectiveKind::MaxUpdateSkew(c) => write!(f, "maxupdateskew {}", c.skew_ppm),
117 DirectiveKind::MaxSlewRate(c) => write!(f, "maxslewrate {}", c.rate_ppm),
118 DirectiveKind::TempComp(c) => match c {
119 TempCompConfig::Coefficients {
120 file,
121 interval,
122 t0,
123 k0,
124 k1,
125 k2,
126 } => write!(f, "tempcomp {file} {interval} {t0} {k0} {k1} {k2}"),
127 TempCompConfig::PointFile {
128 file,
129 interval,
130 points_file,
131 } => write!(f, "tempcomp {file} {interval} {points_file}"),
132 },
133
134 DirectiveKind::Allow(c) => write_allow_deny(f, "allow", c),
136 DirectiveKind::Deny(c) => write_allow_deny(f, "deny", c),
137 DirectiveKind::BindAddress(c) => write!(f, "bindaddress {}", c.address),
138 DirectiveKind::BindDevice(c) => write!(f, "binddevice {}", c.interface),
139 DirectiveKind::Broadcast(c) => write!(f, "broadcast {} {} {}", c.interval, c.address, c.port),
140 DirectiveKind::ClientLogLimit(c) => write!(f, "clientloglimit {}", c.limit),
141 DirectiveKind::NoClientLog => write!(f, "noclientlog"),
142 DirectiveKind::Local(c) => {
143 write!(f, "local")?;
144 if c.stratum.get() != 10 {
145 write!(f, " stratum {}", c.stratum)?;
146 }
147 if c.orphan {
148 write!(f, " orphan")?;
149 }
150 if (c.distance - 1.0).abs() > 1e-12 {
151 write!(f, " distance {}", c.distance)?;
152 }
153 Ok(())
154 }
155 DirectiveKind::NtpSignDSocket(c) => write!(f, "ntpsigndsocket {}", c.directory),
156 DirectiveKind::NtsPort(c) => write!(f, "ntsport {}", c.port),
157 DirectiveKind::NtsServerCert(c) => write!(f, "ntsservercert {}", c.file),
158 DirectiveKind::NtsServerKey(c) => write!(f, "ntsserverkey {}", c.file),
159 DirectiveKind::NtsProcesses(c) => write!(f, "ntsprocesses {}", c.processes),
160 DirectiveKind::MaxNtsConnections(c) => write!(f, "maxntsconnections {}", c.connections),
161 DirectiveKind::NtsNtpServer(c) => write!(f, "ntsntpserver {}", c.hostname),
162 DirectiveKind::NtsRotate(c) => write!(f, "ntsrotate {}", c.interval),
163 DirectiveKind::Port(c) => write!(f, "port {}", c.port),
164 DirectiveKind::RateLimit(c) => write!(
165 f,
166 "ratelimit interval {} burst {} leak {}{}",
167 c.interval,
168 c.burst,
169 c.leak,
170 c.kod.map_or(String::new(), |k| format!(" kod {k}"))
171 ),
172 DirectiveKind::NtsRateLimit(c) => {
173 write!(f, "ntsratelimit interval {} burst {} leak {}", c.interval, c.burst, c.leak)
174 }
175 DirectiveKind::SmoothTime(c) => {
176 write!(f, "smoothtime {} {}", c.max_freq, c.max_wander)?;
177 if c.leap_only {
178 write!(f, " leaponly")?;
179 }
180 Ok(())
181 }
182
183 DirectiveKind::BindCmdAddress(c) => write!(f, "bindcmdaddress {}", c.address),
185 DirectiveKind::BindCmdDevice(c) => write!(f, "bindcmddevice {}", c.interface),
186 DirectiveKind::CmdAllow(c) => write_allow_deny(f, "cmdallow", c),
187 DirectiveKind::CmdDeny(c) => write_allow_deny(f, "cmddeny", c),
188 DirectiveKind::CmdPort(c) => write!(f, "cmdport {}", c.port),
189 DirectiveKind::CmdRateLimit(c) => {
190 write!(f, "cmdratelimit interval {} burst {} leak {}", c.interval, c.burst, c.leak)
191 }
192 DirectiveKind::OpenCommands(c) => {
193 write!(f, "opencommands")?;
194 for cmd in &c.commands {
195 write!(f, " {cmd}")?;
196 }
197 Ok(())
198 }
199
200 DirectiveKind::HwClockFile(c) => write!(f, "hwclockfile {}", c.file),
202 DirectiveKind::RtcAutoTrim(c) => write!(f, "rtcautotrim {}", c.threshold),
203 DirectiveKind::RtcDevice(c) => write!(f, "rtcdevice {}", c.device),
204 DirectiveKind::RtcFile(c) => write!(f, "rtcfile {}", c.file),
205 DirectiveKind::RtcOnUtc => write!(f, "rtconutc"),
206 DirectiveKind::RtcSync => write!(f, "rtcsync"),
207
208 DirectiveKind::Log(c) => {
210 write!(f, "log")?;
211 if c.raw_measurements {
212 write!(f, " rawmeasurements")?;
213 }
214 if c.measurements && !c.raw_measurements {
215 write!(f, " measurements")?;
216 }
217 if c.selection {
218 write!(f, " selection")?;
219 }
220 if c.statistics {
221 write!(f, " statistics")?;
222 }
223 if c.tracking {
224 write!(f, " tracking")?;
225 }
226 if c.rtc {
227 write!(f, " rtc")?;
228 }
229 if c.refclocks {
230 write!(f, " refclocks")?;
231 }
232 if c.tempcomp {
233 write!(f, " tempcomp")?;
234 }
235 Ok(())
236 }
237 DirectiveKind::LogBanner(c) => write!(f, "logbanner {}", c.limit),
238 DirectiveKind::LogChange(c) => write!(f, "logchange {}", c.threshold),
239 DirectiveKind::LogDir(c) => write!(f, "logdir {}", c.directory),
240
241 DirectiveKind::HwTimestamp(c) => {
243 write!(f, "hwtimestamp {}", c.interface)?;
244 if c.minpoll.get() != 0 {
245 write!(f, " minpoll {}", c.minpoll)?;
246 }
247 if c.nocrossts {
248 write!(f, " nocrossts")?;
249 }
250 Ok(())
251 }
252 DirectiveKind::HwTsTimeout(c) => write!(f, "hwtstimeout {}", c.timeout),
253 DirectiveKind::MaxTxBuffers(c) => write!(f, "maxtxbuffers {}", c.buffers),
254 DirectiveKind::PtpPort(c) => write!(f, "ptpport {}", c.port),
255 DirectiveKind::PtpDomain(c) => write!(f, "ptpdomain {}", c.domain),
256
257 DirectiveKind::NtsAeads(c) => {
259 write!(f, "ntsaeads")?;
260 for id in &c.ids {
261 write!(f, " {id}")?;
262 }
263 Ok(())
264 }
265
266 DirectiveKind::ConfDir(c) => write!(f, "confdir {}", c.directory),
268 DirectiveKind::Include(c) => write!(f, "include {}", c.pattern),
269 DirectiveKind::SourceDir(c) => write!(f, "sourcedir {}", c.directory),
270 DirectiveKind::LockAll => write!(f, "lock_all"),
271 DirectiveKind::MailOnChange(c) => write!(f, "mailonchange {} {}", c.email, c.threshold),
272 DirectiveKind::PidFile(c) => write!(f, "pidfile {}", c.file),
273 DirectiveKind::SchedPriority(c) => write!(f, "sched_priority {}", c.priority),
274 DirectiveKind::User(c) => write!(f, "user {}", c.user),
275 DirectiveKind::KeyFile(c) => write!(f, "keyfile {}", c.file),
276 DirectiveKind::DumpOnExit => write!(f, "dumponexit"),
277 }
278}
279
280fn format_enum<T: fmt::Debug>(val: &T) -> String {
281 format!("{val:?}").to_lowercase()
282}
283
284fn write_server(f: &mut fmt::Formatter<'_>, c: &ServerConfig) -> fmt::Result {
285 write!(f, "server {}", c.hostname)?;
286 write_server_options(f, c)
287}
288
289fn write_server_options(f: &mut fmt::Formatter<'_>, c: &ServerConfig) -> fmt::Result {
290 if c.iburst {
291 write!(f, " iburst")?;
292 }
293 if c.burst {
294 write!(f, " burst")?;
295 }
296 if c.prefer() {
297 write!(f, " prefer")?;
298 }
299 if c.noselect() {
300 write!(f, " noselect")?;
301 }
302 if c.trust() {
303 write!(f, " trust")?;
304 }
305 if c.require() {
306 write!(f, " require")?;
307 }
308 if c.nts {
309 write!(f, " nts")?;
310 }
311 if c.xleave() {
312 write!(f, " xleave")?;
313 }
314 if c.copy {
315 write!(f, " copy")?;
316 }
317 if c.auto_offline {
318 write!(f, " auto_offline")?;
319 }
320 if matches!(c.connectivity, SourceConnectivity::Offline) {
321 write!(f, " offline")?;
322 }
323 if c.minpoll.get() != 6 {
324 write!(f, " minpoll {}", c.minpoll)?;
325 }
326 if c.maxpoll.get() != 10 {
327 write!(f, " maxpoll {}", c.maxpoll)?;
328 }
329 if c.port.get() != 123 {
330 write!(f, " port {}", c.port)?;
331 }
332 if (c.max_delay - 3.0).abs() > 1e-12 {
333 write!(f, " maxdelay {}", c.max_delay)?;
334 }
335 if (c.offset - 0.0).abs() > 1e-12 {
336 write!(f, " offset {}", c.offset)?;
337 }
338 if let Some(ref ver) = c.version {
339 write!(f, " version {}", ver)?;
340 }
341 if c.authkey != 0 {
342 write!(f, " key {}", c.authkey)?;
343 }
344 if c.cert_set != 0 {
345 write!(f, " certset {}", c.cert_set)?;
346 }
347 if (c.max_delay_ratio - 0.0).abs() > 1e-12 {
348 write!(f, " maxdelayratio {}", c.max_delay_ratio)?;
349 }
350 if (c.max_delay_dev_ratio - 10.0).abs() > 1e-12 {
351 write!(f, " maxdelaydevratio {}", c.max_delay_dev_ratio)?;
352 }
353 if (c.max_delay_quant - 0.0).abs() > 1e-12 {
354 write!(f, " maxdelayquant {}", c.max_delay_quant)?;
355 }
356 if (c.min_delay - 0.0).abs() > 1e-12 {
357 write!(f, " mindelay {}", c.min_delay)?;
358 }
359 if (c.asymmetry - 1.0).abs() > 1e-12 {
360 write!(f, " asymmetry {}", c.asymmetry)?;
361 }
362 if c.poll_target.get() != 8 {
363 write!(f, " polltarget {}", c.poll_target)?;
364 }
365 if c.min_stratum.get() != 0 {
366 write!(f, " minstratum {}", c.min_stratum)?;
367 }
368 if c.presend.get() != 0 {
369 write!(f, " presend {}", c.presend)?;
370 }
371 if let Some(ref filter) = c.filter {
372 write!(f, " filter {filter}")?;
373 }
374 if let Some(ref ms) = c.min_samples {
375 write!(f, " minsamples {ms}")?;
376 }
377 if let Some(ref ms) = c.max_samples {
378 write!(f, " maxsamples {ms}")?;
379 }
380 if c.ext_fields != 0 {
381 write!(f, " extfield {:x}", c.ext_fields)?;
382 }
383 if !matches!(c.family, AddressFamily::Unspec) {
384 match c.family {
385 AddressFamily::Inet4 => write!(f, " ipv4")?,
386 AddressFamily::Inet6 => write!(f, " ipv6")?,
387 _ => {}
388 }
389 }
390 Ok(())
391}
392
393fn write_allow_deny(f: &mut fmt::Formatter<'_>, name: &str, c: &AllowDenyConfig) -> fmt::Result {
394 write!(f, "{name}")?;
395 if c.all {
396 write!(f, " all")?;
397 }
398 if let Some(ref s) = c.subnet {
399 write!(f, " {s}")?;
400 }
401 Ok(())
402}
403
404fn write_refclock(f: &mut fmt::Formatter<'_>, c: &RefClockConfig) -> fmt::Result {
405 write!(f, "refclock {} {}", format_enum(&c.driver), c.parameter)?;
406 if c.poll.get() != 4 {
407 write!(f, " poll {}", c.poll)?;
408 }
409 if c.dpoll.get() != 0 {
410 write!(f, " dpoll {}", c.dpoll)?;
411 }
412 if c.pps_forced {
413 write!(f, " pps")?;
414 }
415 if c.tai {
416 write!(f, " tai")?;
417 }
418 if c.local {
419 write!(f, " local")?;
420 }
421 if c.select_opts.has(SelectOptions::PREFER) {
422 write!(f, " prefer")?;
423 }
424 if c.select_opts.has(SelectOptions::NOSELECT) {
425 write!(f, " noselect")?;
426 }
427 if c.select_opts.has(SelectOptions::TRUST) {
428 write!(f, " trust")?;
429 }
430 if c.select_opts.has(SelectOptions::REQUIRE) {
431 write!(f, " require")?;
432 }
433 Ok(())
434}
435
436#[cfg(feature = "serde")]
439mod serde_impl {
440 use serde::ser::{Serialize, Serializer, SerializeStruct};
441 use crate::ast::*;
442
443 impl Serialize for ChronyConfig {
444 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
445 let directives: Vec<String> = self
446 .nodes
447 .iter()
448 .filter_map(|n| match n {
449 ConfigNode::Directive(d) => Some(d.kind.name().to_string()),
450 _ => None,
451 })
452 .collect();
453 let mut s = serializer.serialize_struct("ChronyConfig", 1)?;
454 s.serialize_field("directives", &directives)?;
455 s.end()
456 }
457 }
458}