In unprivileged containers even after e8096330 "sys_linux: don't keep
CAP_SYS_TIME with -x option" default installations
will still run without an explicit -x being set and therefore fail by
missing CAP_SYS_TIME.

In some use cases users want the NTP server service to "just work" which
in a non-CAP_SYS_TIME environment means that chrony has to fall back.

By that a user will get an NTP server working independent to the
environment, that will control the local time if it is able to do so.

This is not set as default as the fallback is considered a loss of time
control that users should opt-in, but the new config allows an admin and
setup tools to opt into -x like behavior without loosing the feature to
control time when running in an environment that is able to do so.

This allows to drop the CAP_SYS_TIME condition to let the service run in
e.g. containers as well, but handle the effecive behavior via -x/-X (or
none of these).
This allows a much more visual explanation of why the service doesn't
run.

Signed-off-by: Christian Ehrhardt <christian.ehrha...@canonical.com>
---
 doc/chronyd.adoc         |  7 +++++++
 examples/chronyd.service |  1 -
 main.c                   | 10 +++++++---
 sys.c                    | 26 +++++++++++++++++++++-----
 sys.h                    |  2 +-
 sys_linux.c              | 28 +++++++++++++++++++---------
 sys_linux.h              |  4 +++-
 sys_macosx.c             |  8 ++++----
 sys_macosx.h             |  2 +-
 sys_netbsd.c             |  8 ++++----
 sys_netbsd.h             |  2 +-
 sys_solaris.c            |  6 +++---
 sys_solaris.h            |  2 +-
 sys_timex.c              | 39 +++++++++++++++++++++++++++------------
 sys_timex.h              | 16 ++++++++--------
 15 files changed, 107 insertions(+), 54 deletions(-)

diff --git a/doc/chronyd.adoc b/doc/chronyd.adoc
index f5bb360..b0fd3ac 100644
--- a/doc/chronyd.adoc
+++ b/doc/chronyd.adoc
@@ -163,6 +163,13 @@ relative to the estimated true time, and be able to 
operate as an NTP server.
 This allows *chronyd* to run without the capability to adjust or set the system
 clock (e.g. in some containers).
 
+*-X*::
+This option allows *chronyd* to disable the control of the system clock if
+it fails to do so. *chronyd* will try to initialize the clock, but if failing
+fall back to to the mode as described by the *-x* option.
+This comes handy if one wants a config to work as NTP server in any
+environment, but control the local clock only if it is possible.
+
 *-v*::
 With this option *chronyd* will print version number to the terminal and exit.
 
diff --git a/examples/chronyd.service b/examples/chronyd.service
index 4ffe3b1..b86bef6 100644
--- a/examples/chronyd.service
+++ b/examples/chronyd.service
@@ -3,7 +3,6 @@ Description=NTP client/server
 Documentation=man:chronyd(8) man:chrony.conf(5)
 After=ntpdate.service sntp.service ntpd.service
 Conflicts=ntpd.service systemd-timesyncd.service
-ConditionCapability=CAP_SYS_TIME
 
 [Service]
 Type=forking
diff --git a/main.c b/main.c
index a2202e9..289a01d 100644
--- a/main.c
+++ b/main.c
@@ -406,7 +406,8 @@ int main
   int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
   int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
   int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
-  int clock_control = 1, system_log = 1;
+  int clock_control = 1, clock_fallback = 0;
+  int system_log = 1;
   int config_args = 0;
 
   do_platform_checks();
@@ -427,7 +428,7 @@ int main
   optind = 1;
 
   /* Parse short command-line options */
-  while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
+  while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vxX")) != -1) {
     switch (opt) {
       case '4':
       case '6':
@@ -490,6 +491,9 @@ int main
       case 'x':
         clock_control = 0;
         break;
+      case 'X':
+        clock_fallback = 1;
+        break;
       default:
         print_help(progname);
         return opt != 'h';
@@ -546,7 +550,7 @@ int main
   PRV_Initialise();
   LCL_Initialise();
   SCH_Initialise();
-  SYS_Initialise(clock_control);
+  SYS_Initialise(clock_control, clock_fallback);
   RTC_Initialise(do_init_rtc);
   SRC_Initialise();
   RCL_Initialise();
diff --git a/sys.c b/sys.c
index 4d68b37..42fc6ba 100644
--- a/sys.c
+++ b/sys.c
@@ -50,24 +50,40 @@ static int null_driver;
 /* ================================================== */
 
 void
-SYS_Initialise(int clock_control)
+SYS_Initialise(int clock_control, int clock_fallback)
 {
+  int initalized = 0;
+
   null_driver = !clock_control;
   if (null_driver) {
     SYS_Null_Initialise();
     return;
   }
 #if defined(LINUX)
-  SYS_Linux_Initialise();
+  initalized = SYS_Linux_Initialise();
 #elif defined(SOLARIS)
-  SYS_Solaris_Initialise();
+  initalized = SYS_Solaris_Initialise();
 #elif defined(NETBSD) || defined(FREEBSD)
-  SYS_NetBSD_Initialise();
+  initalized = SYS_NetBSD_Initialise();
 #elif defined(MACOSX)
-  SYS_MacOSX_Initialise();
+  initalized = SYS_MacOSX_Initialise();
 #else
 #error Unknown system
 #endif
+
+  if (!initalized) {
+    LOG(LOGS_WARN, "Failed to initialize control of local system clock");
+#if defined(LINUX) && defined (FEAT_PRIVDROP)
+    SYS_Linux_ReportTimeAdjustBlockers();
+#endif
+    if (!clock_fallback) {
+      LOG_FATAL("No Fallback (-X) allowed, init failed");
+    } else {
+      LOG(LOGS_WARN, "Falling back by disabling control of the system clock");
+      null_driver = 1;
+      SYS_Null_Initialise();
+    }
+  }
 }
 
 /* ================================================== */
diff --git a/sys.h b/sys.h
index cb726f2..f201f2a 100644
--- a/sys.h
+++ b/sys.h
@@ -30,7 +30,7 @@
 #define GOT_SYS_H
 
 /* Called at the start of the run to do initialisation */
-extern void SYS_Initialise(int clock_control);
+extern void SYS_Initialise(int clock_control, int clock_fallback);
 
 /* Called at the end of the run to do final clean-up */
 extern void SYS_Finalise(void);
diff --git a/sys_linux.c b/sys_linux.c
index f445727..3fd48d9 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -340,7 +340,7 @@ get_version_specific_details(void)
 
 /* ================================================== */
 
-static void
+static int
 reset_adjtime_offset(void)
 {
   struct timex txc;
@@ -349,7 +349,7 @@ reset_adjtime_offset(void)
   txc.modes = ADJ_OFFSET_SINGLESHOT;
   txc.offset = 0;
 
-  SYS_Timex_Adjust(&txc, 0);
+  return !(SYS_Timex_Adjust(&txc, 2) < 0);
 }
 
 /* ================================================== */
@@ -383,23 +383,24 @@ test_step_offset(void)
 /* ================================================== */
 /* Initialisation code for this module */
 
-void
+int
 SYS_Linux_Initialise(void)
 {
   get_version_specific_details();
 
-  reset_adjtime_offset();
+  if (!reset_adjtime_offset())
+    return 0;
 
   if (have_setoffset && !test_step_offset()) {
     LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
     have_setoffset = 0;
   }
 
-  SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
-                                    1.0 / tick_update_hz,
-                                    read_frequency, set_frequency,
-                                    have_setoffset ? apply_step_offset : NULL,
-                                    0.0, 0.0, NULL, NULL);
+  return SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / 
nominal_tick,
+                                           1.0 / tick_update_hz,
+                                           read_frequency, set_frequency,
+                                           have_setoffset ? apply_step_offset 
: NULL,
+                                           0.0, 0.0, NULL, NULL);
 }
 
 /* ================================================== */
@@ -413,6 +414,15 @@ SYS_Linux_Finalise(void)
 
 /* ================================================== */
 
+void SYS_Linux_ReportTimeAdjustBlockers(void)
+{
+  if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
+    return;
+  LOG(LOGS_WARN, "CAP_SYS_TIME not present");
+}
+
+/* ================================================== */
+
 #ifdef FEAT_PRIVDROP
 void
 SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
diff --git a/sys_linux.h b/sys_linux.h
index 799ae9a..13c7067 100644
--- a/sys_linux.h
+++ b/sys_linux.h
@@ -27,10 +27,12 @@
 #ifndef GOT_SYS_LINUX_H
 #define GOT_SYS_LINUX_H
 
-extern void SYS_Linux_Initialise(void);
+extern int SYS_Linux_Initialise(void);
 
 extern void SYS_Linux_Finalise(void);
 
+extern void SYS_Linux_ReportTimeAdjustBlockers(void);
+
 extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
 
 extern void SYS_Linux_EnableSystemCallFilter(int level);
diff --git a/sys_macosx.c b/sys_macosx.c
index 00ce302..27ea81a 100644
--- a/sys_macosx.c
+++ b/sys_macosx.c
@@ -479,7 +479,7 @@ test_adjtime()
 
 /* ================================================== */
 
-void
+int
 SYS_MacOSX_Initialise(void)
 {
 #ifdef HAVE_MACOS_SYS_TIMEX
@@ -488,14 +488,14 @@ SYS_MacOSX_Initialise(void)
     have_bad_adjtime = !test_adjtime();
     if (have_bad_adjtime) {
       LOG(LOGS_WARN, "adjtime() is buggy - using timex driver");
-      SYS_Timex_Initialise();
+      return SYS_Timex_Initialise();
     } else {
-      SYS_NetBSD_Initialise();
+      return SYS_NetBSD_Initialise();
     }
-    return;
   }
 #endif
   legacy_MacOSX_Initialise();
+  return 1;
 }
 
 /* ================================================== */
diff --git a/sys_macosx.h b/sys_macosx.h
index 5555616..748df8a 100644
--- a/sys_macosx.h
+++ b/sys_macosx.h
@@ -32,7 +32,7 @@
 
 void SYS_MacOSX_SetScheduler(int SchedPriority);
 void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid);
-void SYS_MacOSX_Initialise(void);
+int SYS_MacOSX_Initialise(void);
 void SYS_MacOSX_Finalise(void);
 
 #endif
diff --git a/sys_netbsd.c b/sys_netbsd.c
index 840d6a5..35379e5 100644
--- a/sys_netbsd.c
+++ b/sys_netbsd.c
@@ -113,10 +113,10 @@ get_offset_correction(struct timespec *raw,
 void
 SYS_NetBSD_Initialise(void)
 {
-  SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE,
-                                    NULL, NULL, NULL,
-                                    MIN_FASTSLEW_OFFSET, MAX_ADJTIME_SLEWRATE,
-                                    accrue_offset, get_offset_correction);
+  return SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE,
+                                           NULL, NULL, NULL,
+                                           MIN_FASTSLEW_OFFSET, 
MAX_ADJTIME_SLEWRATE,
+                                           accrue_offset, 
get_offset_correction);
 }
 
 /* ================================================== */
diff --git a/sys_netbsd.h b/sys_netbsd.h
index 052f5b7..8b00cdf 100644
--- a/sys_netbsd.h
+++ b/sys_netbsd.h
@@ -28,7 +28,7 @@
 #ifndef GOT_SYS_NETBSD_H
 #define GOT_SYS_NETBSD_H
 
-void SYS_NetBSD_Initialise(void);
+int SYS_NetBSD_Initialise(void);
 
 void SYS_NetBSD_Finalise(void);
 
diff --git a/sys_solaris.c b/sys_solaris.c
index 21197b9..1047376 100644
--- a/sys_solaris.c
+++ b/sys_solaris.c
@@ -35,12 +35,12 @@
 
 /* ================================================== */
 
-void
+int
 SYS_Solaris_Initialise(void)
 {
   /* The kernel allows the frequency to be set in the full range off int32_t */
-  SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
-                                    0.0, 0.0, NULL, NULL);
+  return SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
+                                           0.0, 0.0, NULL, NULL);
 }
 
 /* ================================================== */
diff --git a/sys_solaris.h b/sys_solaris.h
index 46015ba..6d0532b 100644
--- a/sys_solaris.h
+++ b/sys_solaris.h
@@ -27,7 +27,7 @@
 #ifndef GOT_SYS_SOLARIS_H
 #define GOT_SYS_SOLARIS_H
 
-void SYS_Solaris_Initialise(void);
+int SYS_Solaris_Initialise(void);
 
 void SYS_Solaris_Finalise(void);
 
diff --git a/sys_timex.c b/sys_timex.c
index e54ad24..2754812 100644
--- a/sys_timex.c
+++ b/sys_timex.c
@@ -181,7 +181,7 @@ set_sync_status(int synchronised, double est_error, double 
max_error)
 
 /* ================================================== */
 
-static void
+static int
 initialise_timex(void)
 {
   struct timex txc;
@@ -193,26 +193,30 @@ initialise_timex(void)
   txc.modes = MOD_OFFSET | MOD_STATUS;
   txc.status = STA_PLL | sys_status;
   txc.offset = 0;
-  SYS_Timex_Adjust(&txc, 0);
+  if (SYS_Timex_Adjust(&txc, 2) < 0)
+    return 0;
 
   /* Turn PLL off */
   txc.modes = MOD_STATUS;
   txc.status = sys_status;
-  SYS_Timex_Adjust(&txc, 0);
+  if (SYS_Timex_Adjust(&txc, 2) < 0)
+    return 0;
+
+  return 1;
 }
 
 /* ================================================== */
 
-void
+int
 SYS_Timex_Initialise(void)
 {
-  SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL, NULL, 
NULL,
-                                    0.0, 0.0, NULL, NULL);
+  return SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL,
+                                           NULL, NULL, 0.0, 0.0, NULL, NULL);
 }
 
 /* ================================================== */
 
-void
+int
 SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double 
max_set_freq_delay,
                                   lcl_ReadFrequencyDriver sys_read_freq,
                                   lcl_SetFrequencyDriver sys_set_freq,
@@ -221,7 +225,8 @@ SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, 
double max_set_freq_d
                                   lcl_AccrueOffsetDriver sys_accrue_offset,
                                   lcl_OffsetCorrectionDriver 
sys_get_offset_correction)
 {
-  initialise_timex();
+  if (!initialise_timex())
+    return 0;
 
   SYS_Generic_CompleteFreqDriver(max_set_freq_ppm, max_set_freq_delay,
                                  sys_read_freq ? sys_read_freq : 
read_frequency,
@@ -230,6 +235,7 @@ SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, 
double max_set_freq_d
                                  min_fastslew_offset, max_fastslew_rate,
                                  sys_accrue_offset, sys_get_offset_correction,
                                  set_leap, set_sync_status);
+  return 1;
 }
 
 /* ================================================== */
@@ -256,10 +262,19 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
   state = NTP_ADJTIME(txc);
 
   if (state < 0) {
-    if (!ignore_error)
-      LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, 
strerror(errno));
-    else
-      DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, 
strerror(errno));
+    switch (ignore_error) {
+      case 0:
+        LOG_FATAL(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, 
strerror(errno));
+        break;
+      case 1:
+        DEBUG_LOG(NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, 
strerror(errno));
+        break;
+      case 2:
+        LOG(LOGS_WARN,NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, 
strerror(errno));
+        break;
+      default:
+        assert(0);
+    }
   }
 
   return state;
diff --git a/sys_timex.h b/sys_timex.h
index b8617a2..0921b43 100644
--- a/sys_timex.h
+++ b/sys_timex.h
@@ -29,16 +29,16 @@
 
 #include "localp.h"
 
-extern void SYS_Timex_Initialise(void);
+extern int SYS_Timex_Initialise(void);
 
 /* Initialise with some driver functions replaced with special versions */
-extern void SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double 
max_set_freq_delay,
-                                              lcl_ReadFrequencyDriver 
sys_read_freq,
-                                              lcl_SetFrequencyDriver 
sys_set_freq,
-                                              lcl_ApplyStepOffsetDriver 
sys_apply_step_offset,
-                                              double min_fastslew_offset, 
double max_fastslew_rate,
-                                              lcl_AccrueOffsetDriver 
sys_accrue_offset,
-                                              lcl_OffsetCorrectionDriver 
sys_get_offset_correction);
+extern int SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double 
max_set_freq_delay,
+                                             lcl_ReadFrequencyDriver 
sys_read_freq,
+                                             lcl_SetFrequencyDriver 
sys_set_freq,
+                                             lcl_ApplyStepOffsetDriver 
sys_apply_step_offset,
+                                             double min_fastslew_offset, 
double max_fastslew_rate,
+                                             lcl_AccrueOffsetDriver 
sys_accrue_offset,
+                                             lcl_OffsetCorrectionDriver 
sys_get_offset_correction);
 
 extern void SYS_Timex_Finalise(void);
 
-- 
2.7.4


-- 
To unsubscribe email chrony-dev-requ...@chrony.tuxfamily.org with "unsubscribe" 
in the subject.
For help email chrony-dev-requ...@chrony.tuxfamily.org with "help" in the 
subject.
Trouble?  Email listmas...@chrony.tuxfamily.org.

Reply via email to