/*
* SPDX-License-Identifier: GPL-2.0-or-later
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
* Since 7a3000f7ba548cf7d74ac77cc63fe8de228a669e (v2.30) hwclock is linked
* with parse_date.y from gnullib. This gnulib code is distributed with GPLv3.
* Use --disable-hwclock-gplv3 to exclude this code.
*
+ * Copyright (C) 1992 Charles Hedrick, hedrick@cs.rutgers.edu
+ * Rob Hooft <hooft@chem.ruu.nl>
+ * Harald Koenig <koenig@nova.tat.physik.uni-tuebingen.de>
+ * Alan Modra <alan@spri.levels.unisa.edu.au>
*
- * clock.c was written by Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
- * Modified for clock adjustments - Rob Hooft <hooft@chem.ruu.nl>, Nov 1992
- * Improvements by Harald Koenig <koenig@nova.tat.physik.uni-tuebingen.de>
- * and Alan Modra <alan@spri.levels.unisa.edu.au>.
+ * Copyright (C) 2007-2023 Karel Zak <kzak@redhat.com>
*
* Major rewrite by Bryan Henderson <bryanh@giraffe-data.com>, 96.09.19.
* The new program is called hwclock. New features:
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
+#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
+#endif
#include <time.h>
#include <unistd.h>
+#include <inttypes.h>
#include "c.h"
#include "closestream.h"
UL_DEBUG_DEFINE_MASKNAMES(hwclock) = UL_DEBUG_EMPTY_MASKNAMES;
/* The struct that holds our hardware access routines */
-static struct clock_ops *ur;
+static const struct clock_ops *ur;
/* Maximal clock adjustment in seconds per day.
(adjtime() glibc call has 2145 seconds limit on i386, so it is good enough for us as well,
{
struct timeval newtime;
- newtime.tv_sec = addend.tv_sec + (int)increment;
- newtime.tv_usec = addend.tv_usec + (increment - (int)increment) * 1E6;
+ newtime.tv_sec = addend.tv_sec + (time_t)increment;
+ newtime.tv_usec = addend.tv_usec + (increment - (time_t)increment) * 1E6;
/*
* Now adjust it so that the microsecond value is between 0 and 1
char line1[81]; /* String: first line of adjtime file */
char line2[81]; /* String: second line of adjtime file */
char line3[81]; /* String: third line of adjtime file */
+ int64_t last_adj_time;
+ int64_t last_calib_time;
if (access(ctl->adj_file_name, R_OK) != 0)
return EXIT_SUCCESS;
fclose(adjfile);
- sscanf(line1, "%lf %ld %lf",
- &adjtime_p->drift_factor,
- &adjtime_p->last_adj_time,
- &adjtime_p->not_adjusted);
+ if (sscanf(line1, "%lf %"SCNd64" %lf",
+ &adjtime_p->drift_factor,
+ &last_adj_time,
+ &adjtime_p->not_adjusted) != 3)
+ warnx(_("Warning: unrecognized line in adjtime file: %s"), line1);
+
+ if (sscanf(line2, "%"SCNd64, &last_calib_time) != 1)
+ warnx(_("Warning: unrecognized line in adjtime file: %s"), line2);
- sscanf(line2, "%ld", &adjtime_p->last_calib_time);
+ adjtime_p->last_adj_time = (time_t)last_adj_time;
+ adjtime_p->last_calib_time = (time_t)last_calib_time;
if (!strcmp(line3, "UTC\n")) {
adjtime_p->local_utc = UTC;
}
if (ctl->verbose) {
- printf(_
- ("Last drift adjustment done at %ld seconds after 1969\n"),
- (long)adjtime_p->last_adj_time);
- printf(_("Last calibration done at %ld seconds after 1969\n"),
- (long)adjtime_p->last_calib_time);
+ printf(_("Last drift adjustment done at %"PRId64" seconds after 1969\n"),
+ (int64_t)adjtime_p->last_adj_time);
+ printf(_("Last calibration done at %"PRId64" seconds after 1969\n"),
+ (int64_t)adjtime_p->last_calib_time);
printf(_("Hardware clock is on %s time\n"),
(adjtime_p->local_utc ==
LOCAL) ? _("local") : (adjtime_p->local_utc ==
} else {
valid = 1;
if (ctl->verbose)
- printf(_
- ("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
- "%ld seconds since 1969\n"), tm.tm_year + 1900,
+ printf(_("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
+ "%"PRId64" seconds since 1969\n"), tm.tm_year + 1900,
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
- tm.tm_sec, (long)*systime_p);
+ tm.tm_sec, (int64_t)*systime_p);
}
return valid;
}
read_hardware_clock(const struct hwclock_control *ctl,
int *valid_p, time_t *systime_p)
{
- struct tm tm;
+ struct tm tm = { 0 };
int err;
err = ur->read_hardware_clock(ctl, &tm);
return err;
if (ctl->verbose)
- printf(_
- ("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
+ printf(_("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec);
*valid_p = mktime_tz(ctl, tm, systime_p);
static void
set_hardware_clock(const struct hwclock_control *ctl, const time_t newtime)
{
- struct tm new_broken_time;
+ struct tm new_broken_time = { 0 };
/*
* Time to which we will set Hardware Clock, in broken down format,
* in the time zone of caller's choice
if (ctl->verbose)
printf(_("Setting Hardware Clock to %.2d:%.2d:%.2d "
- "= %ld seconds since 1969\n"),
+ "= %"PRId64" seconds since 1969\n"),
new_broken_time.tm_hour, new_broken_time.tm_min,
- new_broken_time.tm_sec, (long)newtime);
+ new_broken_time.tm_sec, (int64_t)newtime);
if (!ctl->testing)
ur->set_hardware_clock(ctl, &new_broken_time);
pc = ul_new_path("/sys/class/rtc/%s", rtcname);
if (!pc)
goto unknown;
- rc = ul_path_scanf(pc, "name", "%128[^\n ]", &name);
+ rc = ul_path_scanf(pc, "name", "%128[^\n ]", name);
ul_unref_path(pc);
if (rc != 1 || !*name)
if (ticksize < 0) {
if (ctl->verbose)
printf(_("time jumped backward %.6f seconds "
- "to %ld.%06ld - retargeting\n"),
- ticksize, nowsystime.tv_sec,
- nowsystime.tv_usec);
+ "to %"PRId64".%06"PRId64" - retargeting\n"),
+ ticksize, (int64_t)nowsystime.tv_sec,
+ (int64_t)nowsystime.tv_usec);
/* The retarget is handled at the end of the loop. */
} else if (deltavstarget < 0) {
/* deltavstarget < 0 if current time < target time */
DBG(DELTA_VS_TARGET,
- ul_debug("%ld.%06ld < %ld.%06ld (%.6f)",
- nowsystime.tv_sec, nowsystime.tv_usec,
- targetsystime.tv_sec,
- targetsystime.tv_usec, deltavstarget));
+ ul_debug("%"PRId64".%06"PRId64" < %"PRId64".%06"PRId64" (%.6f)",
+ (int64_t)nowsystime.tv_sec, (int64_t)nowsystime.tv_usec,
+ (int64_t)targetsystime.tv_sec,
+ (int64_t)targetsystime.tv_usec, deltavstarget));
continue; /* not there yet - keep spinning */
} else if (deltavstarget <= target_time_tolerance_secs) {
/* Close enough to the target time; done waiting. */
* aim for the next opportunity.
*/
if (ctl->verbose)
- printf(_("missed it - %ld.%06ld is too far "
- "past %ld.%06ld (%.6f > %.6f)\n"),
- nowsystime.tv_sec,
- nowsystime.tv_usec,
- targetsystime.tv_sec,
- targetsystime.tv_usec,
+ printf(_("missed it - %"PRId64".%06"PRId64" is too far "
+ "past %"PRId64".%06"PRId64" (%.6f > %.6f)\n"),
+ (int64_t)nowsystime.tv_sec,
+ (int64_t)nowsystime.tv_usec,
+ (int64_t)targetsystime.tv_sec,
+ (int64_t)targetsystime.tv_usec,
deltavstarget,
target_time_tolerance_secs);
target_time_tolerance_secs += tolerance_incr_secs;
+ ceil(time_diff(nowsystime, refsystime)
- delay /* don't count this */);
if (ctl->verbose)
- printf(_("%ld.%06ld is close enough to %ld.%06ld (%.6f < %.6f)\n"
- "Set RTC to %ld (%ld + %d; refsystime = %ld.%06ld)\n"),
- nowsystime.tv_sec, nowsystime.tv_usec,
- targetsystime.tv_sec, targetsystime.tv_usec,
+ printf(_("%"PRId64".%06"PRId64" is close enough to %"PRId64".%06"PRId64" (%.6f < %.6f)\n"
+ "Set RTC to %"PRId64" (%"PRId64" + %d; refsystime = %"PRId64".%06"PRId64")\n"),
+ (int64_t)nowsystime.tv_sec, (int64_t)nowsystime.tv_usec,
+ (int64_t)targetsystime.tv_sec, (int64_t)targetsystime.tv_usec,
deltavstarget, target_time_tolerance_secs,
- newhwtime, sethwtime,
- (int)(newhwtime - sethwtime),
- refsystime.tv_sec, refsystime.tv_usec);
+ (int64_t)newhwtime, (int64_t)sethwtime,
+ (int)((int64_t)newhwtime - (int64_t)sethwtime),
+ (int64_t)refsystime.tv_sec, (int64_t)refsystime.tv_usec);
set_hardware_clock(ctl, newhwtime);
}
set_system_clock(const struct hwclock_control *ctl,
const struct timeval newtime)
{
- struct tm broken;
+ struct tm broken = { 0 };
int minuteswest;
int rc = 0;
minuteswest);
if (ctl->hctosys)
- printf(_("Calling settimeofday(%ld.%06ld, NULL) "
+ printf(_("Calling settimeofday(%"PRId64".%06"PRId64", NULL) "
"to set the System time.\n"),
- newtime.tv_sec, newtime.tv_usec);
+ (int64_t)newtime.tv_sec, (int64_t)newtime.tv_usec);
}
if (!ctl->testing) {
tdrift_p->tv_usec = (exact_adjustment -
(double)tdrift_p->tv_sec) * 1E6;
if (ctl->verbose) {
- printf(P_("Time since last adjustment is %ld second\n",
- "Time since last adjustment is %ld seconds\n",
- (systime - last_time)),
- (systime - last_time));
- printf(_("Calculated Hardware Clock drift is %ld.%06ld seconds\n"),
- tdrift_p->tv_sec, tdrift_p->tv_usec);
+ printf(P_("Time since last adjustment is %"PRId64" second\n",
+ "Time since last adjustment is %"PRId64" seconds\n",
+ ((int64_t)systime - (int64_t)last_time)),
+ ((int64_t)systime - (int64_t)last_time));
+ printf(_("Calculated Hardware Clock drift is %"PRId64".%06"PRId64" seconds\n"),
+ (int64_t)tdrift_p->tv_sec, (int64_t)tdrift_p->tv_usec);
}
}
char *content; /* Stuff to write to disk file */
FILE *fp;
- xasprintf(&content, "%f %ld %f\n%ld\n%s\n",
+ xasprintf(&content, "%f %"PRId64" %f\n%"PRId64"\n%s\n",
adjtime->drift_factor,
- adjtime->last_adj_time,
+ (int64_t)adjtime->last_adj_time,
adjtime->not_adjusted,
- adjtime->last_calib_time,
+ (int64_t)adjtime->last_calib_time,
(adjtime->local_utc == LOCAL) ? "LOCAL" : "UTC");
if (ctl->verbose){
}
if (!ctl->testing) {
+ int rc;
+
fp = fopen(ctl->adj_file_name, "w");
if (fp == NULL) {
warn(_("cannot open %s"), ctl->adj_file_name);
return EXIT_FAILURE;
}
- if (fputs(content, fp) < 0 || close_stream(fp) != 0) {
+ rc = fputs(content, fp) < 0;
+ rc += close_stream(fp);
+
+ if (rc) {
warn(_("cannot update %s"), ctl->adj_file_name);
return EXIT_FAILURE;
}
hclocktime = time_inc(hclocktime, (double)
-(tdrift.tv_sec + tdrift.tv_usec / 1E6));
if (ctl->verbose) {
- printf(_ ("Target date: %ld\n"), set_time);
- printf(_ ("Predicted RTC: %ld\n"), hclocktime.tv_sec);
+ printf(_("Target date: %"PRId64"\n"), (int64_t)set_time);
+ printf(_("Predicted RTC: %"PRId64"\n"), (int64_t)hclocktime.tv_sec);
}
return display_time(hclocktime);
}
}
#endif /* __linux__ __alpha__ */
+#ifdef __linux__
+static int
+manipulate_rtc_param(const struct hwclock_control *ctl)
+{
+ if (ctl->param_get_option) {
+ uint64_t id = 0, value = 0;
+
+ if (get_param_rtc(ctl, ctl->param_get_option, &id, &value)) {
+ warnx(_("unable to read the RTC parameter %s"),
+ ctl->param_get_option);
+ return 1;
+ }
+
+ printf(_("The RTC parameter 0x%jx is set to 0x%jx.\n"),
+ (uintmax_t) id, (uintmax_t) value);
+ return 0;
+
+ } else if (ctl->param_set_option) {
+ if (ctl->testing)
+ return 0;
+
+ return set_param_rtc(ctl, ctl->param_set_option);
+ }
+
+ return 1;
+}
+
+static int
+manipulate_rtc_voltage_low(const struct hwclock_control *ctl)
+{
+ if (ctl->vl_read) {
+ if (rtc_vl_read(ctl))
+ return 1;
+ }
+ if (ctl->vl_clear) {
+ if (rtc_vl_clear(ctl))
+ return 1;
+ }
+ return 0;
+}
+#endif
+
static void out_version(void)
{
printf(UTIL_LINUX_VERSION);
static void __attribute__((__noreturn__))
usage(void)
{
+#ifdef __linux__
+ const struct hwclock_param *param = get_hwclock_params();
+#endif
+
fputs(USAGE_HEADER, stdout);
printf(_(" %s [function] [option...]\n"), program_invocation_short_name);
puts(_("Time clocks utility."));
fputs(USAGE_FUNCTIONS, stdout);
- puts(_(" -r, --show display the RTC time"));
- puts(_(" --get display drift corrected RTC time"));
- puts(_(" --set set the RTC according to --date"));
- puts(_(" -s, --hctosys set the system time from the RTC"));
- puts(_(" -w, --systohc set the RTC from the system time"));
- puts(_(" --systz send timescale configurations to the kernel"));
- puts(_(" -a, --adjust adjust the RTC to account for systematic drift"));
+ puts(_(" -r, --show display the RTC time"));
+ puts(_(" --get display drift corrected RTC time"));
+ puts(_(" --set set the RTC according to --date"));
+ puts(_(" -s, --hctosys set the system time from the RTC"));
+ puts(_(" -w, --systohc set the RTC from the system time"));
+ puts(_(" --systz send timescale configurations to the kernel"));
+ puts(_(" -a, --adjust adjust the RTC to account for systematic drift"));
#if defined(__linux__) && defined(__alpha__)
- puts(_(" --getepoch display the RTC epoch"));
- puts(_(" --setepoch set the RTC epoch according to --epoch"));
+ puts(_(" --getepoch display the RTC epoch"));
+ puts(_(" --setepoch set the RTC epoch according to --epoch"));
+#endif
+#ifdef __linux__
+ puts(_(" --param-get <param> display the RTC parameter"));
+ puts(_(" --param-set <param>=<value> set the RTC parameter"));
+ puts(_(" --vl-read read voltage low information"));
+ puts(_(" --vl-clear clear voltage low information"));
#endif
- puts(_(" --predict predict the drifted RTC time according to --date"));
+ puts(_(" --predict predict the drifted RTC time according to --date"));
fputs(USAGE_OPTIONS, stdout);
- puts(_(" -u, --utc the RTC timescale is UTC"));
- puts(_(" -l, --localtime the RTC timescale is Local"));
+ puts(_(" -u, --utc the RTC timescale is UTC"));
+ puts(_(" -l, --localtime the RTC timescale is Local"));
#ifdef __linux__
printf(_(
- " -f, --rtc <file> use an alternate file to %1$s\n"), _PATH_RTC_DEV);
+ " -f, --rtc <file> use an alternate file to %1$s\n"), _PATH_RTC_DEV);
#endif
printf(_(
- " --directisa use the ISA bus instead of %1$s access\n"), _PATH_RTC_DEV);
- puts(_(" --date <time> date/time input for --set and --predict"));
- puts(_(" --delay <sec> delay used when set new RTC time"));
+ " --directisa use the ISA bus instead of %1$s access\n"), _PATH_RTC_DEV);
+ puts(_(" --date <time> date/time input for --set and --predict"));
+ puts(_(" --delay <sec> delay used when set new RTC time"));
#if defined(__linux__) && defined(__alpha__)
- puts(_(" --epoch <year> epoch input for --setepoch"));
+ puts(_(" --epoch <year> epoch input for --setepoch"));
#endif
- puts(_(" --update-drift update the RTC drift factor"));
+ puts(_(" --update-drift update the RTC drift factor"));
printf(_(
- " --noadjfile do not use %1$s\n"), _PATH_ADJTIME);
+ " --noadjfile do not use %1$s\n"), _PATH_ADJTIME);
printf(_(
- " --adjfile <file> use an alternate file to %1$s\n"), _PATH_ADJTIME);
- puts(_(" --test dry run; implies --verbose"));
- puts(_(" -v, --verbose display more details"));
+ " --adjfile <file> use an alternate file to %1$s\n"), _PATH_ADJTIME);
+ puts(_(" --test dry run; implies --verbose"));
+ puts(_(" -v, --verbose display more details"));
+
fputs(USAGE_SEPARATOR, stdout);
- printf(USAGE_HELP_OPTIONS(22));
- printf(USAGE_MAN_TAIL("hwclock(8)"));
+ fprintf(stdout, USAGE_HELP_OPTIONS(33));
+
+#ifdef __linux__
+ fputs(USAGE_ARGUMENTS, stdout);
+ fputs(_(" <param> is either a numeric RTC parameter value or one of these aliases:"), stdout);
+
+ while (param->name) {
+ fprintf(stdout, _(" - %1$s: %2$s (0x%3$x)\n"), param->name, param->help, param->id);
+ param++;
+ }
+
+ fputs(_(" See Kernel's include/uapi/linux/rtc.h for parameters and values."), stdout);
+ fputs(USAGE_ARG_SEPARATOR, stdout);
+ fputs(_(" <param> and <value> accept hexadecimal values if prefixed with 0x, otherwise decimal."), stdout);
+#endif
+ fprintf(stdout, USAGE_MAN_TAIL("hwclock(8)"));
exit(EXIT_SUCCESS);
}
OPT_GET,
OPT_GETEPOCH,
OPT_NOADJFILE,
+ OPT_PARAM_GET,
+ OPT_PARAM_SET,
+ OPT_VL_READ,
+ OPT_VL_CLEAR,
OPT_PREDICT,
OPT_SET,
OPT_SETEPOCH,
{ "getepoch", no_argument, NULL, OPT_GETEPOCH },
{ "setepoch", no_argument, NULL, OPT_SETEPOCH },
{ "epoch", required_argument, NULL, OPT_EPOCH },
+#endif
+#ifdef __linux__
+ { "param-get", required_argument, NULL, OPT_PARAM_GET },
+ { "param-set", required_argument, NULL, OPT_PARAM_SET },
+ { "vl-read", no_argument, NULL, OPT_VL_READ },
+ { "vl-clear", no_argument, NULL, OPT_VL_CLEAR },
#endif
{ "noadjfile", no_argument, NULL, OPT_NOADJFILE },
{ "directisa", no_argument, NULL, OPT_DIRECTISA },
case OPT_EPOCH:
ctl.epoch_option = optarg; /* --epoch */
break;
+#endif
+#ifdef __linux__
+ case OPT_PARAM_GET:
+ ctl.param_get_option = optarg;
+ ctl.show = 0;
+ break;
+ case OPT_PARAM_SET:
+ ctl.param_set_option = optarg;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
+ break;
+ case OPT_VL_READ:
+ ctl.vl_read = 1;
+ ctl.show = 0;
+ break;
+ case OPT_VL_CLEAR:
+ ctl.vl_clear = 1;
+ ctl.show = 0;
+ break;
#endif
case OPT_NOADJFILE:
ctl.noadjfile = 1;
}
if (argc -= optind) {
- warnx(_("%d too many arguments given"), argc);
+ warnx(_("too many arguments"));
errtryhelp(EXIT_FAILURE);
}
}
}
+#ifdef __linux__
+ if (ctl.param_get_option || ctl.param_set_option) {
+ if (manipulate_rtc_param(&ctl))
+ hwclock_exit(&ctl, EXIT_FAILURE);
+
+ hwclock_exit(&ctl, EXIT_SUCCESS);
+ }
+
+ if (ctl.vl_read || ctl.vl_clear) {
+ if (manipulate_rtc_voltage_low(&ctl))
+ hwclock_exit(&ctl, EXIT_FAILURE);
+
+ hwclock_exit(&ctl, EXIT_SUCCESS);
+ }
+#endif
+
#if defined(__linux__) && defined(__alpha__)
if (ctl.getepoch || ctl.setepoch) {
manipulate_epoch(&ctl);
if (ctl.verbose) {
out_version();
- printf(_("System Time: %ld.%06ld\n"),
- startup_time.tv_sec, startup_time.tv_usec);
+ printf(_("System Time: %"PRId64".%06"PRId64"\n"),
+ (int64_t)startup_time.tv_sec, (int64_t)startup_time.tv_usec);
}
if (!ctl.systz && !ctl.predict)