summaryrefslogtreecommitdiffstats
path: root/rtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'rtc.c')
-rw-r--r--rtc.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/rtc.c b/rtc.c
new file mode 100644
index 0000000..33d04bf
--- /dev/null
+++ b/rtc.c
@@ -0,0 +1,240 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "rtc.h"
+#include "local.h"
+#include "logging.h"
+#include "conf.h"
+
+#if defined LINUX && defined FEAT_RTC
+#include "rtc_linux.h"
+#endif /* defined LINUX */
+
+/* ================================================== */
+
+static int driver_initialised = 0;
+static int driver_preinit_ok = 0;
+
+static struct {
+ int (*init)(void);
+ void (*fini)(void);
+ int (*time_pre_init)(time_t driftfile_time);
+ void (*time_init)(void (*after_hook)(void*), void *anything);
+ void (*start_measurements)(void);
+ int (*write_parameters)(void);
+ int (*get_report)(RPT_RTC_Report *report);
+ int (*trim)(void);
+} driver =
+{
+#if defined LINUX && defined FEAT_RTC
+ RTC_Linux_Initialise,
+ RTC_Linux_Finalise,
+ RTC_Linux_TimePreInit,
+ RTC_Linux_TimeInit,
+ RTC_Linux_StartMeasurements,
+ RTC_Linux_WriteParameters,
+ RTC_Linux_GetReport,
+ RTC_Linux_Trim
+#else
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+#endif
+};
+
+/* ================================================== */
+/* Get the last modification time of the driftfile */
+
+static time_t
+get_driftfile_time(void)
+{
+ struct stat buf;
+ char *drift_file;
+
+ drift_file = CNF_GetDriftFile();
+ if (!drift_file)
+ return 0;
+
+ if (stat(drift_file, &buf))
+ return 0;
+
+ return buf.st_mtime;
+}
+
+/* ================================================== */
+/* Set the system time to the driftfile time if it's in the future */
+
+static void
+apply_driftfile_time(time_t t)
+{
+ struct timespec now;
+
+ LCL_ReadCookedTime(&now, NULL);
+
+ if (now.tv_sec < t) {
+ if (LCL_ApplyStepOffset(now.tv_sec - t))
+ LOG(LOGS_INFO, "System time restored from driftfile");
+ }
+}
+
+/* ================================================== */
+
+void
+RTC_Initialise(int initial_set)
+{
+ time_t driftfile_time;
+ char *file_name;
+
+ /* If the -s option was specified, try to do an initial read of the RTC and
+ set the system time to it. Also, read the last modification time of the
+ driftfile (i.e. system time when chronyd was previously stopped) and set
+ the system time to it if it's in the future to bring the clock closer to
+ the true time when the RTC is broken (e.g. it has no battery), is missing,
+ or there is no RTC driver. */
+ if (initial_set) {
+ driftfile_time = get_driftfile_time();
+
+ if (driver.time_pre_init && driver.time_pre_init(driftfile_time)) {
+ driver_preinit_ok = 1;
+ } else {
+ driver_preinit_ok = 0;
+ if (driftfile_time)
+ apply_driftfile_time(driftfile_time);
+ }
+ }
+
+ driver_initialised = 0;
+
+ /* This is how we tell whether the user wants to load the RTC
+ driver, if he is on a machine where it is an option. */
+ file_name = CNF_GetRtcFile();
+
+ if (file_name) {
+ if (CNF_GetRtcSync()) {
+ LOG_FATAL("rtcfile directive cannot be used with rtcsync");
+ }
+
+ if (driver.init) {
+ if ((driver.init)()) {
+ driver_initialised = 1;
+ }
+ } else {
+ LOG(LOGS_ERR, "RTC not supported on this operating system");
+ }
+ }
+}
+
+/* ================================================== */
+
+void
+RTC_Finalise(void)
+{
+ if (driver.fini) {
+ (driver.fini)();
+ }
+}
+
+/* ================================================== */
+/* Start the processing to get a single measurement from the real time
+ clock, and use it to trim the system time, based on knowing the
+ drift rate of the RTC and the error the last time we set it. If the
+ TimePreInit routine has succeeded, we can be sure that the trim required
+ is not *too* large.
+
+ We are called with a hook to a function to be called after the
+ initialisation is complete. We also call this if we cannot do the
+ initialisation. */
+
+void
+RTC_TimeInit(void (*after_hook)(void *), void *anything)
+{
+ if (driver_initialised && driver_preinit_ok) {
+ (driver.time_init)(after_hook, anything);
+ } else {
+ (after_hook)(anything);
+ }
+}
+
+/* ================================================== */
+/* Start the RTC measurement process */
+
+void
+RTC_StartMeasurements(void)
+{
+ if (driver_initialised) {
+ (driver.start_measurements)();
+ }
+ /* Benign if driver not present */
+}
+
+/* ================================================== */
+/* Write RTC information out to RTC file. Return 0 for success, 1 if
+ RTC driver not running, or 2 if the file cannot be written. */
+
+int
+RTC_WriteParameters(void)
+{
+ if (driver_initialised) {
+ return (driver.write_parameters)();
+ } else {
+ return RTC_ST_NODRV;
+ }
+}
+
+/* ================================================== */
+
+int
+RTC_GetReport(RPT_RTC_Report *report)
+{
+ if (driver_initialised) {
+ return (driver.get_report)(report);
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+
+int
+RTC_Trim(void)
+{
+ if (driver_initialised) {
+ return (driver.trim)();
+ } else {
+ return 0;
+ }
+}
+
+/* ================================================== */
+