#include <getopt.h>
#include <limits.h>
#include <math.h>
-#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define OPTUTILS_EXIT_CODE EX_USAGE
+#define XALLOC_EXIT_CODE EX_OSERR
#include "c.h"
#include "closestream.h"
#include "pathnames.h"
#include "strutils.h"
#include "hwclock.h"
+#include "timeutils.h"
+#include "env.h"
+#include "xalloc.h"
#ifdef HAVE_LIBAUDIT
#include <libaudit.h>
static int hwaudit_fd = -1;
-static int hwaudit_on;
#endif
/* The struct that holds our hardware access routines */
-struct clock_ops *ur;
-
-#define FLOOR(arg) ((arg >= 0 ? (int) arg : ((int) arg) - 1));
+static 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,
43219 is a maximal safe value preventing exact_adjustment overflow.) */
#define MAX_DRIFT 2145.0
-const char *adj_file_name = NULL;
-
struct adjtime {
/*
* This is information we keep in the adjtime file that tells us how
* authority (as opposed to just doing a drift adjustment)
*/
/* line 3 */
- enum a_local_utc { LOCAL, UTC, UNKNOWN } local_utc;
+ enum a_local_utc { UTC = 0, LOCAL, UNKNOWN } local_utc;
/*
* To which time zone, local or UTC, we most recently set the
* hardware clock.
*/
};
-/*
- * We are running in debug mode, wherein we put a lot of information about
- * what we're doing to standard output.
- */
-int debug;
-
-/* Workaround for Award 4.50g BIOS bug: keep the year in a file. */
-bool badyear;
-
-/* User-specified epoch, used when rtc fails to return epoch. */
-unsigned long epoch_option = -1;
-
-/*
- * Almost all Award BIOS's made between 04/26/94 and 05/31/95 have a nasty
- * bug limiting the RTC year byte to the range 94-99. Any year between 2000
- * and 2093 gets changed to 2094, every time you start the system.
- *
- * With the --badyear option, we write the date to file and hope that the
- * file is updated at least once a year. I recommend putting this command
- * "hwclock --badyear" in the monthly crontab, just to be safe.
- *
- * -- Dave Coffin 11/12/98
- */
-static void write_date_to_file(struct tm *tm)
-{
- FILE *fp;
-
- if ((fp = fopen(_PATH_LASTDATE, "w"))) {
- fprintf(fp, "%02d.%02d.%04d\n", tm->tm_mday, tm->tm_mon + 1,
- tm->tm_year + 1900);
- if (close_stream(fp) != 0)
- warn(_("cannot write %s"), _PATH_LASTDATE);
- } else
- warn(_("cannot write %s"), _PATH_LASTDATE);
-}
-
-static void read_date_from_file(struct tm *tm)
-{
- int last_mday, last_mon, last_year;
- FILE *fp;
-
- if ((fp = fopen(_PATH_LASTDATE, "r"))) {
- if (fscanf(fp, "%d.%d.%d\n", &last_mday, &last_mon, &last_year)
- == 3) {
- tm->tm_year = last_year - 1900;
- if ((tm->tm_mon << 5) + tm->tm_mday <
- ((last_mon - 1) << 5) + last_mday)
- tm->tm_year++;
- }
- fclose(fp);
- }
- write_date_to_file(tm);
-}
-
/*
* time_t to timeval conversion.
*/
}
static bool
-hw_clock_is_utc(const bool utc, const bool local_opt,
+hw_clock_is_utc(const struct hwclock_control *ctl,
const struct adjtime adjtime)
{
bool ret;
- if (utc)
+ if (ctl->utc)
ret = TRUE; /* --utc explicitly given on command line */
- else if (local_opt)
+ else if (ctl->local_opt)
ret = FALSE; /* --localtime explicitly given */
else
/* get info from adjtime file - default is UTC */
ret = (adjtime.local_utc != LOCAL);
- if (debug)
+ if (ctl->debug)
printf(_("Assuming hardware clock is kept in %s time.\n"),
ret ? _("UTC") : _("local"));
return ret;
*
* return value 0 if all OK, !=0 otherwise.
*/
-static int read_adjtime(struct adjtime *adjtime_p)
+static int read_adjtime(const struct hwclock_control *ctl,
+ struct adjtime *adjtime_p)
{
FILE *adjfile;
- int rc; /* local return code */
- struct stat statbuf; /* We don't even use the contents of this. */
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 */
- long timeval;
-
- rc = stat(adj_file_name, &statbuf);
- if (rc < 0 && errno == ENOENT) {
- /* He doesn't have a adjtime file, so we'll use defaults. */
- adjtime_p->drift_factor = 0;
- adjtime_p->last_adj_time = 0;
- adjtime_p->not_adjusted = 0;
- adjtime_p->last_calib_time = 0;
- adjtime_p->local_utc = UTC;
- adjtime_p->dirty = FALSE; /* don't create a zero adjfile */
+ if (access(ctl->adj_file_name, R_OK) != 0)
return 0;
- }
- adjfile = fopen(adj_file_name, "r"); /* open file for reading */
+ adjfile = fopen(ctl->adj_file_name, "r"); /* open file for reading */
if (adjfile == NULL) {
- warn(_("cannot open %s"), adj_file_name);
+ warn(_("cannot open %s"), ctl->adj_file_name);
return EX_OSFILE;
}
-
if (!fgets(line1, sizeof(line1), adjfile))
line1[0] = '\0'; /* In case fgets fails */
if (!fgets(line2, sizeof(line2), adjfile))
fclose(adjfile);
- /* Set defaults in case values are missing from file */
- adjtime_p->drift_factor = 0;
- adjtime_p->last_adj_time = 0;
- adjtime_p->not_adjusted = 0;
- adjtime_p->last_calib_time = 0;
- timeval = 0;
-
sscanf(line1, "%lf %ld %lf",
&adjtime_p->drift_factor,
- &timeval, &adjtime_p->not_adjusted);
- adjtime_p->last_adj_time = timeval;
+ &adjtime_p->last_adj_time,
+ &adjtime_p->not_adjusted);
- sscanf(line2, "%ld", &timeval);
- adjtime_p->last_calib_time = timeval;
+ sscanf(line2, "%ld", &adjtime_p->last_calib_time);
if (!strcmp(line3, "UTC\n")) {
adjtime_p->local_utc = UTC;
}
}
- adjtime_p->dirty = FALSE;
-
- if (debug) {
+ if (ctl->debug) {
printf(_
("Last drift adjustment done at %ld seconds after 1969\n"),
(long)adjtime_p->last_adj_time);
*
* Return 0 if it worked, nonzero if it didn't.
*/
-static int synchronize_to_clock_tick(void)
+static int synchronize_to_clock_tick(const struct hwclock_control *ctl)
{
int rc;
- if (debug)
+ if (ctl->debug)
printf(_("Waiting for clock tick...\n"));
- rc = ur->synchronize_to_clock_tick();
+ rc = ur->synchronize_to_clock_tick(ctl);
- if (debug) {
+ if (ctl->debug) {
if (rc)
printf(_("...synchronization failed\n"));
else
* return *valid_p == true.
*/
static void
-mktime_tz(struct tm tm, const bool universal,
- bool * valid_p, time_t * systime_p)
+mktime_tz(const struct hwclock_control *ctl, struct tm tm,
+ bool *valid_p, time_t *systime_p)
{
- time_t mktime_result; /* The value returned by our mktime() call */
- char *zone; /* Local time zone name */
-
- /*
- * We use the C library function mktime(), but since it only works
- * on local time zone input, we may have to fake it out by
- * temporarily changing the local time zone to UTC.
- */
- zone = getenv("TZ"); /* remember original time zone */
- if (universal) {
- /* Set timezone to UTC as defined by the environment
- * variable TZUTC. TZUTC undefined gives the default UTC
- * zonefile which usually does not take into account leap
- * seconds. Define TZUTC to select your UTC zonefile which
- * does include leap seconds. For example, with recent GNU
- * libc's:
- * TZUTC=:/usr/share/zoneinfo/right/UTC
- */
- setenv("TZ", getenv("TZUTC"), TRUE);
- /*
- * Note: tzset() gets called implicitly by the time code,
- * but only the first time. When changing the environment
- * variable, better call tzset() explicitly.
- */
- tzset();
- }
- mktime_result = mktime(&tm);
- if (mktime_result == -1) {
+ if (ctl->universal)
+ *systime_p = timegm(&tm);
+ else
+ *systime_p = mktime(&tm);
+ if (*systime_p == -1) {
/*
* This apparently (not specified in mktime() documentation)
* means the 'tm' structure does not contain valid values
* mktime() returns -1).
*/
*valid_p = FALSE;
- *systime_p = 0;
- if (debug)
+ if (ctl->debug)
printf(_("Invalid values in hardware clock: "
"%4d/%.2d/%.2d %.2d:%.2d:%.2d\n"),
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
} else {
*valid_p = TRUE;
- *systime_p = mktime_result;
- if (debug)
+ if (ctl->debug)
printf(_
("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
"%ld 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);
}
- /* now put back the original zone. */
- if (zone)
- setenv("TZ", zone, TRUE);
- else
- unsetenv("TZ");
- tzset();
}
/*
* clock.
*/
static int
-read_hardware_clock(const bool universal, bool * valid_p, time_t * systime_p)
+read_hardware_clock(const struct hwclock_control *ctl,
+ bool * valid_p, time_t *systime_p)
{
struct tm tm;
int err;
- err = ur->read_hardware_clock(&tm);
+ err = ur->read_hardware_clock(ctl, &tm);
if (err)
return err;
- if (badyear)
- read_date_from_file(&tm);
-
- if (debug)
+ if (ctl->debug)
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);
- mktime_tz(tm, universal, valid_p, systime_p);
+ mktime_tz(ctl, tm, valid_p, systime_p);
return 0;
}
* according to <universal>.
*/
static void
-set_hardware_clock(const time_t newtime,
- const bool universal, const bool testing)
+set_hardware_clock(const struct hwclock_control *ctl, const time_t newtime)
{
struct tm new_broken_time;
/*
* in the time zone of caller's choice
*/
- if (universal)
+ if (ctl->universal)
new_broken_time = *gmtime(&newtime);
else
new_broken_time = *localtime(&newtime);
- if (debug)
+ if (ctl->debug)
printf(_("Setting Hardware Clock to %.2d:%.2d:%.2d "
"= %ld seconds since 1969\n"),
new_broken_time.tm_hour, new_broken_time.tm_min,
new_broken_time.tm_sec, (long)newtime);
- if (testing)
- printf(_("Clock not changed - testing only.\n"));
- else {
- if (badyear) {
- /*
- * Write the real year to a file, then write a fake
- * year between 1995 and 1998 to the RTC. This way,
- * Award BIOS boots on 29 Feb 2000 thinking that
- * it's 29 Feb 1996.
- */
- write_date_to_file(&new_broken_time);
- new_broken_time.tm_year =
- 95 + ((new_broken_time.tm_year + 1) & 3);
- }
- ur->set_hardware_clock(&new_broken_time);
- }
+ if (ctl->testing)
+ printf(_("Test mode: clock was not changed\n"));
+ else
+ ur->set_hardware_clock(ctl, &new_broken_time);
}
/*
* Idea for future enhancement.
*/
static void
-set_hardware_clock_exact(const time_t sethwtime,
- const struct timeval refsystime,
- const bool universal, const bool testing)
+set_hardware_clock_exact(const struct hwclock_control *ctl,
+ const time_t sethwtime,
+ const struct timeval refsystime)
{
/*
* The Hardware Clock can only be set to any integer time plus one
double ticksize;
/* FOR TESTING ONLY: inject random delays of up to 1000ms */
- if (debug >= 10) {
+ if (ctl->debug >= 10) {
int usec = random() % 1000000;
printf(_("sleeping ~%d usec\n"), usec);
- usleep(usec);
+ xusleep(usec);
}
gettimeofday(&nowsystime, NULL);
prevsystime = nowsystime;
if (ticksize < 0) {
- if (debug)
+ if (ctl->debug)
printf(_("time jumped backward %.6f seconds "
- "to %ld.%06d - retargeting\n"),
- ticksize, (long)nowsystime.tv_sec,
- (int)nowsystime.tv_usec);
+ "to %ld.%06ld - retargeting\n"),
+ ticksize, nowsystime.tv_sec,
+ nowsystime.tv_usec);
/* The retarget is handled at the end of the loop. */
} else if (deltavstarget < 0) {
/* deltavstarget < 0 if current time < target time */
- if (debug >= 2)
- printf(_("%ld.%06d < %ld.%06d (%.6f)\n"),
- (long)nowsystime.tv_sec,
- (int)nowsystime.tv_usec,
- (long)targetsystime.tv_sec,
- (int)targetsystime.tv_usec,
+ if (ctl->debug >= 2)
+ printf(_("%ld.%06ld < %ld.%06ld (%.6f)\n"),
+ nowsystime.tv_sec,
+ nowsystime.tv_usec,
+ targetsystime.tv_sec,
+ targetsystime.tv_usec,
deltavstarget);
continue; /* not there yet - keep spinning */
} else if (deltavstarget <= target_time_tolerance_secs) {
* We missed our window. Increase the tolerance and
* aim for the next opportunity.
*/
- if (debug)
- printf(_("missed it - %ld.%06d is too far "
- "past %ld.%06d (%.6f > %.6f)\n"),
- (long)nowsystime.tv_sec,
- (int)nowsystime.tv_usec,
- (long)targetsystime.tv_sec,
- (int)targetsystime.tv_usec,
+ if (ctl->debug)
+ 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,
deltavstarget,
target_time_tolerance_secs);
target_time_tolerance_secs += tolerance_incr_secs;
+ (int)(time_diff(nowsystime, refsystime)
- RTC_SET_DELAY_SECS /* don't count this */
+ 0.5 /* for rounding */);
- if (debug)
- printf(_("%ld.%06d is close enough to %ld.%06d (%.6f < %.6f)\n"
- "Set RTC to %ld (%ld + %d; refsystime = %ld.%06d)\n"),
- (long)nowsystime.tv_sec, (int)nowsystime.tv_usec,
- (long)targetsystime.tv_sec, (int)targetsystime.tv_usec,
+ if (ctl->debug)
+ 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,
deltavstarget, target_time_tolerance_secs,
- (long)newhwtime, (long)sethwtime,
+ newhwtime, sethwtime,
(int)(newhwtime - sethwtime),
- (long)refsystime.tv_sec, (int)refsystime.tv_usec);
+ refsystime.tv_sec, refsystime.tv_usec);
- set_hardware_clock(newhwtime, universal, testing);
+ set_hardware_clock(ctl, newhwtime);
}
/*
"either invalid (e.g. 50th day of month) or beyond the range "
"we can handle (e.g. Year 2095)."));
else {
- struct tm *lt;
- char *format = "%c";
- char ctime_now[200];
+ char buf[ISO_8601_BUFSIZ];
- lt = localtime(&hwctime.tv_sec);
- strftime(ctime_now, sizeof(ctime_now), format, lt);
- printf(_("%s .%06d seconds\n"), ctime_now, (int)hwctime.tv_usec);
+ strtimeval_iso(&hwctime, ISO_8601_DATE|ISO_8601_TIME|ISO_8601_DOTUSEC|
+ ISO_8601_TIMEZONE|ISO_8601_SPACE,
+ buf, sizeof(buf));
+ printf("%s\n", buf);
}
}
-/*
- * Interpret the value of the --date option, which is something like
- * "13:05:01". In fact, it can be any of the myriad ASCII strings that
- * specify a time which the "date" program can understand. The date option
- * value in question is our "dateopt" argument.
- *
- * The specified time is in the local time zone.
- *
- * Our output, "*time_p", is a seconds-into-epoch time.
- *
- * We use the "date" program to interpret the date string. "date" must be
- * runnable by issuing the command "date" to the /bin/sh shell. That means
- * in must be in the current PATH.
- *
- * If anything goes wrong (and many things can), we return return code 10
- * and arbitrary *time_p. Otherwise, return code is 0 and *time_p is valid.
- */
-static int interpret_date_string(const char *date_opt, time_t * const time_p)
-{
- FILE *date_child_fp;
- char date_resp[100];
- const char magic[] = "seconds-into-epoch=";
- char date_command[100];
- int retcode; /* our eventual return code */
- int rc; /* local return code */
-
- if (date_opt == NULL) {
- warnx(_("No --date option specified."));
- return 14;
- }
-
- /* prevent overflow - a security risk */
- if (strlen(date_opt) > sizeof(date_command) - 50) {
- warnx(_("--date argument too long"));
- return 13;
- }
-
- /* Quotes in date_opt would ruin the date command we construct. */
- if (strchr(date_opt, '"') != NULL) {
- warnx(_
- ("The value of the --date option is not a valid date.\n"
- "In particular, it contains quotation marks."));
- return 12;
- }
-
- sprintf(date_command, "date --date=\"%s\" +seconds-into-epoch=%%s",
- date_opt);
- if (debug)
- printf(_("Issuing date command: %s\n"), date_command);
-
- date_child_fp = popen(date_command, "r");
- if (date_child_fp == NULL) {
- warn(_("Unable to run 'date' program in /bin/sh shell. "
- "popen() failed"));
- return 10;
- }
-
- if (!fgets(date_resp, sizeof(date_resp), date_child_fp))
- date_resp[0] = '\0'; /* in case fgets fails */
- if (debug)
- printf(_("response from date command = %s\n"), date_resp);
- if (strncmp(date_resp, magic, sizeof(magic) - 1) != 0) {
- warnx(_("The date command issued by %s returned "
- "unexpected results.\n"
- "The command was:\n %s\n"
- "The response was:\n %s"),
- program_invocation_short_name, date_command, date_resp);
- retcode = 8;
- } else {
- long seconds_since_epoch;
- rc = sscanf(date_resp + sizeof(magic) - 1, "%ld",
- &seconds_since_epoch);
- if (rc < 1) {
- warnx(_("The date command issued by %s returned "
- "something other than an integer where the "
- "converted time value was expected.\n"
- "The command was:\n %s\n"
- "The response was:\n %s\n"),
- program_invocation_short_name, date_command,
- date_resp);
- retcode = 6;
- } else {
- retcode = 0;
- *time_p = seconds_since_epoch;
- if (debug)
- printf(_("date string %s equates to "
- "%ld seconds since 1969.\n"),
- date_opt, (long)*time_p);
- }
- }
- pclose(date_child_fp);
-
- return retcode;
-}
-
/*
* Set the System Clock to time 'newtime'.
*
* have.
*/
static int
-set_system_clock(const bool hclock_valid, const struct timeval newtime,
- const bool testing, const bool universal)
+set_system_clock(const struct hwclock_control *ctl, const bool hclock_valid,
+ const struct timeval newtime)
{
int retcode;
minuteswest -= 60;
#endif
- if (debug) {
+ if (ctl->debug) {
printf(_("Calling settimeofday:\n"));
printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"),
- (long)newtime.tv_sec, (long)newtime.tv_usec);
+ newtime.tv_sec, newtime.tv_usec);
printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest);
}
- if (testing) {
+ if (ctl->testing) {
printf(_
- ("Not setting system clock because running in test mode.\n"));
+ ("Test mode: clock was not changed\n"));
retcode = 0;
} else {
const struct timezone tz = { minuteswest, 0 };
* mode does not clobber the Hardware Clock with UTC. This
* is only available on first call of settimeofday after boot.
*/
- if (!universal)
+ if (!ctl->universal)
rc = settimeofday(tv_null, &tz);
if (!rc)
rc = settimeofday(&newtime, &tz);
* If 'testing' is true, don't actually update anything -- just say we would
* have.
*/
-static int set_system_clock_timezone(const bool universal, const bool testing)
+static int set_system_clock_timezone(const struct hwclock_control *ctl)
{
int retcode;
struct timeval tv;
int minuteswest;
gettimeofday(&tv, NULL);
- if (debug) {
+ if (ctl->debug) {
struct tm broken_time;
char ctime_now[200];
broken_time = *gmtime(&tv.tv_sec);
strftime(ctime_now, sizeof(ctime_now), "%Y/%m/%d %H:%M:%S",
&broken_time);
- printf(_("Current system time: %ld = %s\n"), (long)tv.tv_sec,
+ printf(_("Current system time: %ld = %s\n"), tv.tv_sec,
ctime_now);
}
minuteswest -= 60;
#endif
- if (debug) {
+ if (ctl->debug) {
struct tm broken_time;
char ctime_now[200];
gettimeofday(&tv, NULL);
- if (!universal)
+ if (!ctl->universal)
tv.tv_sec += minuteswest * 60;
broken_time = *gmtime(&tv.tv_sec);
printf(_("Calling settimeofday:\n"));
printf(_("\tUTC: %s\n"), ctime_now);
printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"),
- (long)tv.tv_sec, (long)tv.tv_usec);
+ tv.tv_sec, tv.tv_usec);
printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest);
}
- if (testing) {
+ if (ctl->testing) {
printf(_
- ("Not setting system clock because running in test mode.\n"));
+ ("Test mode: clock was not changed\n"));
retcode = 0;
} else {
const struct timezone tz_utc = { 0, 0 };
* compensate. If the systemtime is in fact in UTC, then this is wrong
* so we first do a dummy call to make sure the time is not shifted.
*/
- if (universal)
+ if (ctl->universal)
rc = settimeofday(tv_null, &tz_utc);
/* Now we set the real timezone. Due to the above dummy call, this will
* so don't adjust the drift factor.
*/
static void
-adjust_drift_factor(struct adjtime *adjtime_p,
+adjust_drift_factor(const struct hwclock_control *ctl,
+ struct adjtime *adjtime_p,
const struct timeval nowtime,
const bool hclock_valid,
- const struct timeval hclocktime,
- const bool update)
+ const struct timeval hclocktime)
{
- if (!update) {
- if (debug)
+ if (!ctl->update) {
+ if (ctl->debug)
printf(_("Not adjusting drift factor because the "
"--update-drift option was not used.\n"));
} else if (!hclock_valid) {
- if (debug)
+ if (ctl->debug)
printf(_("Not adjusting drift factor because the "
"Hardware Clock previously contained "
"garbage.\n"));
} else if (adjtime_p->last_calib_time == 0) {
- if (debug)
+ if (ctl->debug)
printf(_("Not adjusting drift factor because last "
"calibration time is zero,\n"
"so history is bad and calibration startover "
"is necessary.\n"));
} else if ((hclocktime.tv_sec - adjtime_p->last_calib_time) < 4 * 60 * 60) {
- if (debug)
+ if (ctl->debug)
printf(_("Not adjusting drift factor because it has "
"been less than four hours since the last "
"calibration.\n"));
- } else if (adjtime_p->last_calib_time != 0) {
+ } else {
/*
* At adjustment time we drift correct the hardware clock
* according to the contents of the adjtime file and refresh
drift_factor = adjtime_p->drift_factor + factor_adjust;
if (fabs(drift_factor) > MAX_DRIFT) {
- if (debug)
+ if (ctl->debug)
printf(_("Clock drift factor was calculated as "
"%f seconds/day.\n"
"It is far too much. Resetting to zero.\n"),
drift_factor);
drift_factor = 0;
} else {
- if (debug)
- printf(_("Clock drifted %.1f seconds in the past "
- "%.1f seconds\nin spite of a drift factor of "
+ if (ctl->debug)
+ printf(_("Clock drifted %f seconds in the past "
+ "%f seconds\nin spite of a drift factor of "
"%f seconds/day.\n"
"Adjusting drift factor by %f seconds/day\n"),
time_diff(nowtime, hclocktime),
*
*/
static void
-calculate_adjustment(const double factor,
+calculate_adjustment(const struct hwclock_control *ctl,
+ const double factor,
const time_t last_time,
const double not_adjusted,
const time_t systime, struct timeval *tdrift_p)
exact_adjustment =
((double)(systime - last_time)) * factor / (24 * 60 * 60)
+ not_adjusted;
- tdrift_p->tv_sec = FLOOR(exact_adjustment);
+ tdrift_p->tv_sec = (time_t) floor(exact_adjustment);
tdrift_p->tv_usec = (exact_adjustment -
(double)tdrift_p->tv_sec) * 1E6;
- if (debug) {
- printf(P_("Time since last adjustment is %d second\n",
- "Time since last adjustment is %d seconds\n",
- (int)(systime - last_time)),
- (int)(systime - last_time));
- printf(_("Calculated Hardware Clock drift is %ld.%06d seconds\n"),
- (long)tdrift_p->tv_sec, (int)tdrift_p->tv_usec);
+ if (ctl->debug) {
+ 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);
}
}
* But if the contents are clean (unchanged since read from disk), don't
* bother.
*/
-static void save_adjtime(const struct adjtime adjtime, const bool testing)
+static void save_adjtime(const struct hwclock_control *ctl,
+ const struct adjtime *adjtime)
{
- char newfile[412]; /* Stuff to write to disk file */
-
- if (adjtime.dirty) {
- /*
- * snprintf is not always available, but this is safe as
- * long as libc does not use more than 100 positions for %ld
- * or %f
- */
- sprintf(newfile, "%f %ld %f\n%ld\n%s\n",
- adjtime.drift_factor,
- (long)adjtime.last_adj_time,
- adjtime.not_adjusted,
- (long)adjtime.last_calib_time,
- (adjtime.local_utc == LOCAL) ? "LOCAL" : "UTC");
-
- if (testing) {
- printf(_
- ("Not updating adjtime file because of testing mode.\n"));
- printf(_("Would have written the following to %s:\n%s"),
- adj_file_name, newfile);
- } else {
- FILE *adjfile;
- int err = 0;
-
- adjfile = fopen(adj_file_name, "w");
- if (adjfile == NULL) {
- warn(_
- ("Could not open file with the clock adjustment parameters "
- "in it (%s) for writing"), adj_file_name);
- err = 1;
- } else {
- if (fputs(newfile, adjfile) < 0) {
- warn(_
- ("Could not update file with the clock adjustment "
- "parameters (%s) in it"),
- adj_file_name);
- err = 1;
- }
- if (close_stream(adjfile) != 0) {
- warn(_
- ("Could not update file with the clock adjustment "
- "parameters (%s) in it"),
- adj_file_name);
- err = 1;
- }
- }
- if (err)
- warnx(_
- ("Drift adjustment parameters not updated."));
+ char *content; /* Stuff to write to disk file */
+ FILE *fp;
+ int err = 0;
+
+ if (!adjtime->dirty)
+ return;
+
+ xasprintf(&content, "%f %ld %f\n%ld\n%s\n",
+ adjtime->drift_factor,
+ adjtime->last_adj_time,
+ adjtime->not_adjusted,
+ adjtime->last_calib_time,
+ (adjtime->local_utc == LOCAL) ? "LOCAL" : "UTC");
+
+ if (ctl->testing) {
+ if (ctl->debug){
+ printf(_("Test mode: %s was not updated with:\n%s"),
+ ctl->adj_file_name, content);
}
+ free(content);
+ return;
}
+
+ fp = fopen(ctl->adj_file_name, "w");
+ if (fp == NULL) {
+ warn(_("Could not open file with the clock adjustment parameters "
+ "in it (%s) for writing"), ctl->adj_file_name);
+ err = 1;
+ } else if (fputs(content, fp) < 0 || close_stream(fp) != 0) {
+ warn(_("Could not update file with the clock adjustment "
+ "parameters (%s) in it"), ctl->adj_file_name);
+ err = 1;
+ }
+ free(content);
+ if (err)
+ warnx(_("Drift adjustment parameters not updated."));
}
/*
*
*/
static void
-do_adjustment(struct adjtime *adjtime_p,
+do_adjustment(const struct hwclock_control *ctl, struct adjtime *adjtime_p,
const bool hclock_valid, const struct timeval hclocktime,
- const struct timeval read_time,
- const bool universal, const bool testing)
+ const struct timeval read_time)
{
if (!hclock_valid) {
warnx(_("The Hardware Clock does not contain a valid time, "
adjtime_p->not_adjusted = 0;
adjtime_p->dirty = TRUE;
} else if (adjtime_p->last_adj_time == 0) {
- if (debug)
+ if (ctl->debug)
printf(_("Not setting clock because last adjustment time is zero, "
"so history is bad.\n"));
} else if (fabs(adjtime_p->drift_factor) > MAX_DRIFT) {
- if (debug)
+ if (ctl->debug)
printf(_("Not setting clock because drift factor %f is far too high.\n"),
adjtime_p->drift_factor);
} else {
- set_hardware_clock_exact(hclocktime.tv_sec,
+ set_hardware_clock_exact(ctl, hclocktime.tv_sec,
time_inc(read_time,
- -(hclocktime.tv_usec / 1E6)),
- universal, testing);
+ -(hclocktime.tv_usec / 1E6)));
adjtime_p->last_adj_time = hclocktime.tv_sec;
adjtime_p->not_adjusted = 0;
adjtime_p->dirty = TRUE;
}
}
-static void determine_clock_access_method(const bool user_requests_ISA)
+static void determine_clock_access_method(const struct hwclock_control *ctl)
{
ur = NULL;
- if (user_requests_ISA)
+ if (ctl->directisa)
ur = probe_for_cmos_clock();
-
#ifdef __linux__
if (!ur)
- ur = probe_for_rtc_clock();
+ ur = probe_for_rtc_clock(ctl);
#endif
+ if (ur) {
+ if (ctl->debug)
+ puts(ur->interface_name);
- if (debug) {
- if (ur)
- puts(_(ur->interface_name));
- else
+ } else {
+ if (ctl->debug)
printf(_("No usable clock interface found.\n"));
+ warnx(_("Cannot access the Hardware Clock via "
+ "any known method."));
+ if (!ctl->debug)
+ warnx(_("Use the --debug option to see the "
+ "details of our search for an access "
+ "method."));
+ hwclock_exit(ctl, EX_SOFTWARE);
}
}
* Return rc == 0 if everything went OK, rc != 0 if not.
*/
static int
-manipulate_clock(const bool show, const bool adjust, const bool noadjfile,
- const bool set, const time_t set_time,
- const bool hctosys, const bool systohc, const bool systz,
- const struct timeval startup_time,
- const bool utc, const bool local_opt, const bool update,
- const bool testing, const bool predict, const bool get)
+manipulate_clock(const struct hwclock_control *ctl, const time_t set_time,
+ const struct timeval startup_time, struct adjtime *adjtime)
{
- /* Contents of the adjtime file, or what they should be. */
- struct adjtime adjtime;
- bool universal;
- /* Set if user lacks necessary authorization to access the clock */
- bool no_auth;
/* The time at which we read the Hardware Clock */
struct timeval read_time;
/*
/* local return code */
int rc = 0;
- if (!systz && !predict) {
- no_auth = ur->get_permissions();
- if (no_auth)
- return EX_NOPERM;
- }
-
- if (!noadjfile && !(systz && (utc || local_opt))) {
- rc = read_adjtime(&adjtime);
- if (rc)
- return rc;
- } else {
- /* A little trick to avoid writing the file if we don't have to */
- adjtime.dirty = FALSE;
- }
-
- universal = hw_clock_is_utc(utc, local_opt, adjtime);
+ if (!ctl->systz && !ctl->predict && ur->get_permissions())
+ return EX_NOPERM;
- if ((set || systohc || adjust) &&
- (adjtime.local_utc == UTC) != universal) {
- adjtime.local_utc = universal ? UTC : LOCAL;
- adjtime.dirty = TRUE;
+ if ((ctl->set || ctl->systohc || ctl->adjust) &&
+ (adjtime->local_utc == UTC) != ctl->universal) {
+ adjtime->local_utc = ctl->universal ? UTC : LOCAL;
+ adjtime->dirty = TRUE;
}
- if (show || get || adjust || hctosys || (!noadjfile && !systz && !predict)) {
+ if (ctl->show || ctl->get || ctl->adjust || ctl->hctosys
+ || (!ctl->noadjfile && !ctl->systz && !ctl->predict)) {
/* data from HW-clock are required */
- rc = synchronize_to_clock_tick();
+ rc = synchronize_to_clock_tick(ctl);
/*
- * 2 = synchronization timeout. We don't
- * error out if the user is attempting to
- * set the RTC - the RTC could be
- * functioning but contain invalid time data
- * so we still want to allow a user to set
- * the RTC time.
+ * We don't error out if the user is attempting to set the
+ * RTC and synchronization timeout happens - the RTC could
+ * be functioning but contain invalid time data so we still
+ * want to allow a user to set the RTC time.
*/
- if (rc && rc != 2 && !set && !systohc)
+ if (rc == RTC_BUSYWAIT_FAILED && !ctl->set && !ctl->systohc)
return EX_IOERR;
gettimeofday(&read_time, NULL);
* don't bother reading it again.
*/
if (!rc) {
- rc = read_hardware_clock(universal,
- &hclock_valid, &hclocktime.tv_sec);
- if (rc && !set && !systohc)
+ rc = read_hardware_clock(ctl, &hclock_valid,
+ &hclocktime.tv_sec);
+ if (rc && !ctl->set && !ctl->systohc)
return EX_IOERR;
}
}
* --predict. For --predict negate the drift correction, because we
* want to 'predict' a future Hardware Clock time that includes drift.
*/
- hclocktime = predict ? t2tv(set_time) : hclocktime;
- calculate_adjustment(adjtime.drift_factor,
- adjtime.last_adj_time,
- adjtime.not_adjusted,
+ hclocktime = ctl->predict ? t2tv(set_time) : hclocktime;
+ calculate_adjustment(ctl, adjtime->drift_factor,
+ adjtime->last_adj_time,
+ adjtime->not_adjusted,
hclocktime.tv_sec, &tdrift);
- if (!show && !predict)
+ if (!ctl->show && !ctl->predict)
hclocktime = time_inc(tdrift, hclocktime.tv_sec);
- if (show || get) {
+ if (ctl->show || ctl->get) {
display_time(hclock_valid,
time_inc(hclocktime, -time_diff
(read_time, startup_time)));
- } else if (set) {
- set_hardware_clock_exact(set_time, startup_time,
- universal, testing);
- if (!noadjfile)
- adjust_drift_factor(&adjtime,
+ } else if (ctl->set) {
+ set_hardware_clock_exact(ctl, set_time, startup_time);
+ if (!ctl->noadjfile)
+ adjust_drift_factor(ctl, adjtime,
time_inc(t2tv(set_time), time_diff
(read_time, startup_time)),
- hclock_valid, hclocktime, update);
- } else if (adjust) {
+ hclock_valid, hclocktime);
+ } else if (ctl->adjust) {
if (tdrift.tv_sec > 0 || tdrift.tv_sec < -1)
- do_adjustment(&adjtime, hclock_valid,
- hclocktime, read_time, universal, testing);
+ do_adjustment(ctl, adjtime, hclock_valid,
+ hclocktime, read_time);
else
printf(_("Needed adjustment is less than one second, "
"so not setting clock.\n"));
- } else if (systohc) {
+ } else if (ctl->systohc) {
struct timeval nowtime, reftime;
/*
* We can only set_hardware_clock_exact to a
gettimeofday(&nowtime, NULL);
reftime.tv_sec = nowtime.tv_sec;
reftime.tv_usec = 0;
- set_hardware_clock_exact((time_t)
- reftime.tv_sec,
- reftime, universal, testing);
- if (!noadjfile)
- adjust_drift_factor(&adjtime, nowtime,
- hclock_valid, hclocktime, update);
- } else if (hctosys) {
- rc = set_system_clock(hclock_valid, hclocktime,
- testing, universal);
+ set_hardware_clock_exact(ctl, (time_t) reftime.tv_sec, reftime);
+ if (!ctl->noadjfile)
+ adjust_drift_factor(ctl, adjtime, nowtime,
+ hclock_valid, hclocktime);
+ } else if (ctl->hctosys) {
+ rc = set_system_clock(ctl, hclock_valid, hclocktime);
if (rc) {
printf(_("Unable to set system clock.\n"));
return rc;
}
- } else if (systz) {
- rc = set_system_clock_timezone(universal, testing);
+ } else if (ctl->systz) {
+ rc = set_system_clock_timezone(ctl);
if (rc) {
printf(_("Unable to set system clock.\n"));
return rc;
}
- } else if (predict) {
+ } else if (ctl->predict) {
hclocktime = time_inc(hclocktime, (double)
-(tdrift.tv_sec + tdrift.tv_usec / 1E6));
- if (debug) {
+ if (ctl->debug) {
printf(_
("At %ld seconds after 1969, RTC is predicted to read %ld seconds after 1969.\n"),
- set_time, (long)hclocktime.tv_sec);
+ set_time, hclocktime.tv_sec);
}
display_time(TRUE, hclocktime);
}
- if (!noadjfile)
- save_adjtime(adjtime, testing);
+ if (!ctl->noadjfile)
+ save_adjtime(ctl, adjtime);
return 0;
}
-/*
- * Get or set the Hardware Clock epoch value in the kernel, as appropriate.
- * <getepoch>, <setepoch>, and <epoch> are hwclock invocation options.
- *
- * <epoch> == -1 if the user did not specify an "epoch" option.
+/**
+ * Get or set the kernel RTC driver's epoch on Alpha machines.
+ * ISA machines are hard coded for 1900.
*/
-#ifdef __linux__
-/*
- * Maintenance note: This should work on non-Alpha machines, but the
- * evidence today (98.03.04) indicates that the kernel only keeps the epoch
- * value on Alphas. If that is ever fixed, this function should be changed.
- */
-# ifndef __alpha__
-static void
-manipulate_epoch(const bool getepoch __attribute__ ((__unused__)),
- const bool setepoch __attribute__ ((__unused__)),
- const unsigned long epoch_opt __attribute__ ((__unused__)),
- const bool testing __attribute__ ((__unused__)))
-{
- warnx(_("The kernel keeps an epoch value for the Hardware Clock "
- "only on an Alpha machine.\nThis copy of hwclock was built for "
- "a machine other than Alpha\n(and thus is presumably not running "
- "on an Alpha now). No action taken."));
-}
-# else
+#if defined(__linux__) && defined(__alpha__)
static void
-manipulate_epoch(const bool getepoch,
- const bool setepoch,
- const unsigned long epoch_opt,
- const bool testing)
+manipulate_epoch(const struct hwclock_control *ctl)
{
- if (getepoch) {
+ if (ctl->getepoch) {
unsigned long epoch;
- if (get_epoch_rtc(&epoch, 0))
+ if (get_epoch_rtc(ctl, &epoch))
warnx(_
("Unable to get the epoch value from the kernel."));
else
printf(_("Kernel is assuming an epoch value of %lu\n"),
epoch);
- } else if (setepoch) {
- if (epoch_opt == -1)
+ } else if (ctl->setepoch) {
+ if (ctl->epoch_option == 0)
warnx(_
("To set the epoch value, you must use the 'epoch' "
"option to tell to what value to set it."));
- else if (testing)
+ else if (ctl->testing)
printf(_
- ("Not setting the epoch to %d - testing only.\n"),
- epoch_opt);
- else if (set_epoch_rtc(epoch_opt))
+ ("Not setting the epoch to %lu - testing only.\n"),
+ ctl->epoch_option);
+ else if (set_epoch_rtc(ctl))
printf(_
("Unable to set the epoch value in the kernel.\n"));
}
}
-# endif /* __alpha__ */
-#endif /* __linux__ */
-
-/*
- * Compare the system and CMOS time and output the drift
- * in 10 second intervals.
- */
-static int compare_clock (const bool utc, const bool local_opt)
-{
- struct tm tm;
- struct timeval tv;
- struct adjtime adjtime;
- double time1_sys, time2_sys;
- time_t time1_hw, time2_hw;
- bool hclock_valid = FALSE, universal, first_pass = TRUE;
- int rc;
-
- if (ur->get_permissions())
- return EX_NOPERM;
-
- /* dummy call for increased precision */
- gettimeofday(&tv, NULL);
-
- rc = read_adjtime(&adjtime);
- if (rc)
- return rc;
-
- universal = hw_clock_is_utc(utc, local_opt, adjtime);
-
- synchronize_to_clock_tick();
- ur->read_hardware_clock(&tm);
-
- gettimeofday(&tv, NULL);
- time1_sys = tv.tv_sec + tv.tv_usec / 1000000.0;
-
- mktime_tz(tm, universal, &hclock_valid, &time1_hw);
-
- while (1) {
- double res;
-
- synchronize_to_clock_tick();
- ur->read_hardware_clock(&tm);
-
- gettimeofday(&tv, NULL);
- time2_sys = tv.tv_sec + tv.tv_usec / 1000000.0;
-
- mktime_tz(tm, universal, &hclock_valid, &time2_hw);
-
- res = (((double) time1_hw - time1_sys) -
- ((double) time2_hw - time2_sys))
- / (double) (time2_hw - time1_hw);
-
- if (!first_pass)
- printf("%10.0f %10.6f %15.0f %4.0f\n",
- (double) time2_hw, time2_sys, res * 1e6, res *1e4);
- else {
- first_pass = FALSE;
- printf("hw-time system-time freq-offset-ppm tick\n");
- printf("%10.0f %10.6f\n", (double) time1_hw, time1_sys);
- }
- sleep(10);
- }
-
- return 0;
-}
+#endif /* __linux__ __alpha__ */
static void out_version(void)
{
printf(UTIL_LINUX_VERSION);
}
-/*
- * usage - Output (error and) usage information
- *
- * This function is called both directly from main to show usage information
- * and as fatal function from shhopt if some argument is not understood. In
- * case of normal usage info FMT should be NULL. In that case the info is
- * printed to stdout. If FMT is given usage will act like fprintf( stderr,
- * fmt, ... ), show a usage information and terminate the program
- * afterwards.
- */
-static void usage(const char *fmt, ...)
+static void __attribute__((__noreturn__))
+usage(const struct hwclock_control *ctl)
{
- FILE *usageto;
- va_list ap;
-
- usageto = fmt ? stderr : stdout;
-
- fputs(USAGE_HEADER, usageto);
- fputs(_(" hwclock [function] [option...]\n"), usageto);
-
- fputs(USAGE_SEPARATOR, usageto);
- fputs(_("Query or set the hardware clock.\n"), usageto);
-
- fputs(_("\nFunctions:\n"), usageto);
- fputs(_(" -h, --help show this help text and exit\n"
- " -r, --show read hardware clock and print result\n"
- " --get read hardware clock and print drift corrected result\n"
- " --set set the RTC to the time given with --date\n"), usageto);
- fputs(_(" -s, --hctosys set the system time from the hardware clock\n"
- " -w, --systohc set the hardware clock from the current system time\n"
- " --systz set the system time based on the current timezone\n"
- " --adjust adjust the RTC to account for systematic drift since\n"
- " the clock was last set or adjusted\n"), usageto);
- fputs(_(" -c, --compare periodically compare the system clock with the CMOS clock\n"), usageto);
-#ifdef __linux__
- fputs(_(" --getepoch print out the kernel's hardware clock epoch value\n"
- " --setepoch set the kernel's hardware clock epoch value to the \n"
- " value given with --epoch\n"), usageto);
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fputs(_(" hwclock [function] [option...]\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Query or set the RTC (Real Time Clock / Hardware Clock)\n"), out);
+
+ fputs(USAGE_FUNCTIONS, out);
+ fputs(_(" -r, --show display the RTC time\n"), out);
+ fputs(_(" --get display drift corrected RTC time\n"), out);
+ fputs(_(" --set set the RTC according to --date\n"), out);
+ fputs(_(" -s, --hctosys set the system time from the RTC\n"), out);
+ fputs(_(" -w, --systohc set the RTC from the system time\n"), out);
+ fputs(_(" --systz send timescale configurations to the kernel\n"), out);
+ fputs(_(" --adjust adjust the RTC to account for systematic drift\n"), out);
+#if defined(__linux__) && defined(__alpha__)
+ fputs(_(" --getepoch display the RTC epoch\n"), out);
+ fputs(_(" --setepoch set the RTC epoch according to --epoch\n"), out);
#endif
- fputs(_(" --predict predict RTC reading at time given with --date\n"
- " -V, --version display version information and exit\n"), usageto);
-
- fputs(USAGE_OPTIONS, usageto);
- fputs(_(" -u, --utc the hardware clock is kept in UTC\n"
- " --localtime the hardware clock is kept in local time\n"), usageto);
+ fputs(_(" --predict predict the drifted RTC time according to --date\n"), out);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -u, --utc inform hwclock the RTC timescale is UTC\n"), out);
+ fputs(_(" -l, --localtime inform hwclock the RTC timescale is Local\n"), out);
+ fprintf(out, _(
#ifdef __linux__
- fputs(_(" -f, --rtc <file> special /dev/... file to use instead of default\n"), usageto);
+ " -f, --rtc <file> use an alternate file to %1$s\n"
#endif
- fprintf(usageto, _(
- " --directisa access the ISA bus directly instead of %s\n"
- " --badyear ignore RTC's year because the BIOS is broken\n"
- " --date <time> specifies the time to which to set the hardware clock\n"
- " --epoch <year> specifies the year which is the beginning of the\n"
- " hardware clock's epoch value\n"), _PATH_RTC_DEV);
- fprintf(usageto, _(
- " --update-drift update drift factor in %1$s (requires\n"
- " --set or --systohc)\n"
- " --noadjfile do not access %1$s; this requires the use of\n"
- " either --utc or --localtime\n"
- " --adjfile <file> specifies the path to the adjust file;\n"
- " the default is %1$s\n"), _PATH_ADJTIME);
- fputs(_(" --test do not update anything, just show what would happen\n"
- " -D, --debug debugging mode\n" "\n"), usageto);
-#ifdef __alpha__
- fputs(_(" -J|--jensen, -A|--arc, -S|--srm, -F|--funky-toy\n"
- " tell hwclock the type of Alpha you have (see hwclock(8))\n"
- "\n"), usageto);
+ " --directisa use the ISA bus instead of %1$s access\n"), _PATH_RTC_DEV);
+ fputs(_(" --date <time> date/time input for --set and --predict\n"), out);
+#if defined(__linux__) && defined(__alpha__)
+ fputs(_(" --epoch <year> epoch input for --setepoch\n"), out);
#endif
-
- if (fmt) {
- va_start(ap, fmt);
- vfprintf(usageto, fmt, ap);
- va_end(ap);
- }
-
- fflush(usageto);
- hwclock_exit(fmt ? EX_USAGE : EX_OK);
+ fputs(_(" --update-drift update the RTC drift factor\n"), out);
+ fprintf(out, _(
+ " --noadjfile do not use %1$s\n"
+ " --adjfile <file> use an alternate file to %1$s\n"), _PATH_ADJTIME);
+ fputs(_(" --test dry run; use -D to view what would have happened\n"), out);
+ fputs(_(" -D, --debug use debug mode\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("hwclock(8)"));
+ hwclock_exit(ctl, EXIT_SUCCESS);
}
/*
*/
int main(int argc, char **argv)
{
+ struct hwclock_control ctl = { .show = 1 }; /* default op is show */
struct timeval startup_time;
+ struct adjtime adjtime = { 0 };
+ struct timespec when = { 0 };
/*
* The time we started up, in seconds into the epoch, including
* fractions.
*/
time_t set_time = 0; /* Time to which user said to set Hardware Clock */
-
- bool permitted; /* User is permitted to do the function */
int rc, c;
- /* Variables set by various options; show may also be set later */
- /* The options debug, badyear and epoch_option are global */
- bool show, set, systohc, hctosys, systz, adjust, getepoch, setepoch,
- predict, compare, get;
- bool utc, testing, local_opt, update, noadjfile, directisa;
- char *date_opt;
-#ifdef __alpha__
- bool ARCconsole, Jensen, SRM, funky_toy;
-#endif
/* Long only options. */
enum {
OPT_ADJFILE = CHAR_MAX + 1,
- OPT_BADYEAR,
OPT_DATE,
OPT_DIRECTISA,
OPT_EPOCH,
OPT_GET,
OPT_GETEPOCH,
- OPT_LOCALTIME,
OPT_NOADJFILE,
- OPT_PREDICT_HC,
+ OPT_PREDICT,
OPT_SET,
OPT_SETEPOCH,
OPT_SYSTZ,
};
static const struct option longopts[] = {
- {"adjust", 0, 0, 'a'},
- {"compare", 0, 0, 'c'},
- {"help", 0, 0, 'h'},
- {"show", 0, 0, 'r'},
- {"hctosys", 0, 0, 's'},
- {"utc", 0, 0, 'u'},
- {"version", 0, 0, 'v'},
- {"systohc", 0, 0, 'w'},
- {"debug", 0, 0, 'D'},
-#ifdef __alpha__
- {"ARC", 0, 0, 'A'},
- {"arc", 0, 0, 'A'},
- {"Jensen", 0, 0, 'J'},
- {"jensen", 0, 0, 'J'},
- {"SRM", 0, 0, 'S'},
- {"srm", 0, 0, 'S'},
- {"funky-toy", 0, 0, 'F'},
-#endif
- {"set", 0, 0, OPT_SET},
-#ifdef __linux__
- {"getepoch", 0, 0, OPT_GETEPOCH},
- {"setepoch", 0, 0, OPT_SETEPOCH},
+ { "adjust", no_argument, NULL, 'a' },
+ { "help", no_argument, NULL, 'h' },
+ { "localtime", no_argument, NULL, 'l' },
+ { "show", no_argument, NULL, 'r' },
+ { "hctosys", no_argument, NULL, 's' },
+ { "utc", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'v' },
+ { "systohc", no_argument, NULL, 'w' },
+ { "debug", no_argument, NULL, 'D' },
+ { "set", no_argument, NULL, OPT_SET },
+#if defined(__linux__) && defined(__alpha__)
+ { "getepoch", no_argument, NULL, OPT_GETEPOCH },
+ { "setepoch", no_argument, NULL, OPT_SETEPOCH },
+ { "epoch", required_argument, NULL, OPT_EPOCH },
#endif
- {"noadjfile", 0, 0, OPT_NOADJFILE},
- {"localtime", 0, 0, OPT_LOCALTIME},
- {"badyear", 0, 0, OPT_BADYEAR},
- {"directisa", 0, 0, OPT_DIRECTISA},
- {"test", 0, 0, OPT_TEST},
- {"date", 1, 0, OPT_DATE},
- {"epoch", 1, 0, OPT_EPOCH},
+ { "noadjfile", no_argument, NULL, OPT_NOADJFILE },
+ { "directisa", no_argument, NULL, OPT_DIRECTISA },
+ { "test", no_argument, NULL, OPT_TEST },
+ { "date", required_argument, NULL, OPT_DATE },
#ifdef __linux__
- {"rtc", 1, 0, 'f'},
+ { "rtc", required_argument, NULL, 'f' },
#endif
- {"adjfile", 1, 0, OPT_ADJFILE},
- {"systz", 0, 0, OPT_SYSTZ},
- {"predict-hc", 0, 0, OPT_PREDICT_HC},
- {"get", 0, 0, OPT_GET},
- {"update-drift",0, 0, OPT_UPDATE},
- {NULL, 0, NULL, 0}
+ { "adjfile", required_argument, NULL, OPT_ADJFILE },
+ { "systz", no_argument, NULL, OPT_SYSTZ },
+ { "predict", no_argument, NULL, OPT_PREDICT },
+ { "get", no_argument, NULL, OPT_GET },
+ { "update-drift", no_argument, NULL, OPT_UPDATE },
+ { NULL, 0, NULL, 0 }
};
- static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
{ 'a','r','s','w',
- OPT_GET, OPT_GETEPOCH, OPT_PREDICT_HC,
+ OPT_GET, OPT_GETEPOCH, OPT_PREDICT,
OPT_SET, OPT_SETEPOCH, OPT_SYSTZ },
- { 'u', OPT_LOCALTIME},
+ { 'l', 'u' },
{ OPT_ADJFILE, OPT_NOADJFILE },
{ OPT_NOADJFILE, OPT_UPDATE },
{ 0 }
};
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+ strutils_set_exitcode(EX_USAGE);
+
/* Remember what time we were invoked */
gettimeofday(&startup_time, NULL);
textdomain(PACKAGE);
atexit(close_stdout);
- /* Set option defaults */
- show = set = systohc = hctosys = systz = adjust = noadjfile = predict =
- compare = get = update = FALSE;
- getepoch = setepoch = utc = local_opt = directisa = testing = debug = FALSE;
-#ifdef __alpha__
- ARCconsole = Jensen = SRM = funky_toy = badyear = FALSE;
-#endif
- date_opt = NULL;
-
while ((c = getopt_long(argc, argv,
- "?hvVDacrsuwAJSFf:", longopts, NULL)) != -1) {
+ "hvVDalrsuwf:", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
switch (c) {
case 'D':
- ++debug;
+ ctl.debug++;
break;
case 'a':
- adjust = TRUE;
+ ctl.adjust = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
- case 'c':
- compare = TRUE;
+ case 'l':
+ ctl.local_opt = 1; /* --localtime */
break;
case 'r':
- show = TRUE;
+ ctl.show = 1;
break;
case 's':
- hctosys = TRUE;
+ ctl.hctosys = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
case 'u':
- utc = TRUE;
+ ctl.utc = 1;
break;
case 'w':
- systohc = TRUE;
- break;
-#ifdef __alpha__
- case 'A':
- ARCconsole = TRUE;
+ ctl.systohc = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
- case 'J':
- Jensen = TRUE;
- break;
- case 'S':
- SRM = TRUE;
- break;
- case 'F':
- funky_toy = TRUE;
- break;
-#endif
case OPT_SET:
- set = TRUE;
+ ctl.set = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
-#ifdef __linux__
+#if defined(__linux__) && defined(__alpha__)
case OPT_GETEPOCH:
- getepoch = TRUE;
+ ctl.getepoch = 1;
+ ctl.show = 0;
break;
case OPT_SETEPOCH:
- setepoch = TRUE;
+ ctl.setepoch = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
+ break;
+ case OPT_EPOCH:
+ ctl.epoch_option = /* --epoch */
+ strtoul_or_err(optarg, _("invalid epoch argument"));
break;
#endif
case OPT_NOADJFILE:
- noadjfile = TRUE;
- break;
- case OPT_LOCALTIME:
- local_opt = TRUE; /* --localtime */
- break;
- case OPT_BADYEAR:
- badyear = TRUE;
+ ctl.noadjfile = 1;
break;
case OPT_DIRECTISA:
- directisa = TRUE;
+ ctl.directisa = 1;
break;
case OPT_TEST:
- testing = TRUE; /* --test */
+ ctl.testing = 1; /* --test */
break;
case OPT_DATE:
- date_opt = optarg; /* --date */
- break;
- case OPT_EPOCH:
- epoch_option = /* --epoch */
- strtoul_or_err(optarg, _("invalid epoch argument"));
+ ctl.date_opt = optarg; /* --date */
break;
case OPT_ADJFILE:
- adj_file_name = optarg; /* --adjfile */
+ ctl.adj_file_name = optarg; /* --adjfile */
break;
case OPT_SYSTZ:
- systz = TRUE; /* --systz */
+ ctl.systz = 1; /* --systz */
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
- case OPT_PREDICT_HC:
- predict = TRUE; /* --predict-hc */
+ case OPT_PREDICT:
+ ctl.predict = 1; /* --predict */
+ ctl.show = 0;
break;
case OPT_GET:
- get = TRUE; /* --get */
+ ctl.get = 1; /* --get */
+ ctl.show = 0;
break;
case OPT_UPDATE:
- update = TRUE; /* --update-drift */
+ ctl.update = 1; /* --update-drift */
break;
#ifdef __linux__
case 'f':
- rtc_dev_name = optarg; /* --rtc */
+ ctl.rtc_dev_name = optarg; /* --rtc */
break;
#endif
case 'v': /* --version */
out_version();
return 0;
case 'h': /* --help */
- case '?':
+ usage(&ctl);
default:
- usage(NULL);
+ errtryhelp(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
-#ifdef HAVE_LIBAUDIT
- if (testing != TRUE) {
- if (adjust == TRUE || hctosys == TRUE || systohc == TRUE ||
- set == TRUE || setepoch == TRUE) {
- hwaudit_on = TRUE;
- }
- }
-#endif
if (argc > 0) {
- usage(_("%s takes no non-option arguments. "
- "You supplied %d.\n"), program_invocation_short_name,
- argc);
+ warnx(_("%d too many arguments given"), argc);
+ errtryhelp(EXIT_FAILURE);
}
- if (!adj_file_name)
- adj_file_name = _PATH_ADJTIME;
+ if (!ctl.adj_file_name)
+ ctl.adj_file_name = _PATH_ADJTIME;
- if (noadjfile && !utc && !local_opt) {
+ if (ctl.update && !ctl.set && !ctl.systohc) {
+ warnx(_("--update-drift requires --set or --systohc"));
+ hwclock_exit(&ctl, EX_USAGE);
+ }
+
+ if (ctl.noadjfile && !ctl.utc && !ctl.local_opt) {
warnx(_("With --noadjfile, you must specify "
"either --utc or --localtime"));
- hwclock_exit(EX_USAGE);
+ hwclock_exit(&ctl, EX_USAGE);
}
-#ifdef __alpha__
- set_cmos_epoch(ARCconsole, SRM);
- set_cmos_access(Jensen, funky_toy);
-#endif
- if (set || predict) {
- rc = interpret_date_string(date_opt, &set_time);
- /* (time-consuming) */
- if (rc != 0) {
- warnx(_("No usable set-to time. "
- "Cannot set clock."));
- hwclock_exit(EX_USAGE);
+ if (ctl.set || ctl.predict) {
+ if (!ctl.date_opt){
+ warnx(_("--date is required for --set or --predict"));
+ hwclock_exit(&ctl, EX_USAGE);
+ }
+ if (parse_date(&when, ctl.date_opt, NULL))
+ set_time = when.tv_sec;
+ else {
+ warnx(_("invalid date '%s'"), ctl.date_opt);
+ hwclock_exit(&ctl, EX_USAGE);
}
}
- if (!(show | set | systohc | hctosys | systz | adjust | getepoch
- | setepoch | predict | compare | get))
- show = 1; /* default to show */
-
- if (getuid() == 0)
- permitted = TRUE;
- else {
- /* program is designed to run setuid (in some situations) */
- if (set || systohc || adjust) {
- warnx(_("Sorry, only the superuser can change "
- "the Hardware Clock."));
- permitted = FALSE;
- } else if (systz || hctosys) {
- warnx(_("Sorry, only the superuser can change "
- "the System Clock."));
- permitted = FALSE;
- } else if (setepoch) {
- warnx(_("Sorry, only the superuser can change the "
- "Hardware Clock epoch in the kernel."));
- permitted = FALSE;
- } else
- permitted = TRUE;
- }
-
- if (!permitted)
- hwclock_exit(EX_NOPERM);
-
-#ifdef __linux__
- if (getepoch || setepoch) {
- manipulate_epoch(getepoch, setepoch, epoch_option, testing);
- hwclock_exit(EX_OK);
+#if defined(__linux__) && defined(__alpha__)
+ if (ctl.getepoch || ctl.setepoch) {
+ manipulate_epoch(&ctl);
+ hwclock_exit(&ctl, EX_OK);
}
#endif
- if (debug)
+ if (ctl.debug)
out_version();
- if (!systz && !predict) {
- determine_clock_access_method(directisa);
- if (!ur) {
- warnx(_("Cannot access the Hardware Clock via "
- "any known method."));
- if (!debug)
- warnx(_("Use the --debug option to see the "
- "details of our search for an access "
- "method."));
- hwclock_exit(EX_SOFTWARE);
- }
- }
-
- if (compare) {
- if (compare_clock(utc, local_opt))
- hwclock_exit(EX_NOPERM);
+ if (!ctl.systz && !ctl.predict)
+ determine_clock_access_method(&ctl);
- rc = EX_OK;
+ if (!ctl.noadjfile && !(ctl.systz && (ctl.utc || ctl.local_opt))) {
+ if ((rc = read_adjtime(&ctl, &adjtime)) != 0)
+ hwclock_exit(&ctl, rc);
} else
- rc = manipulate_clock(show, adjust, noadjfile, set, set_time,
- hctosys, systohc, systz, startup_time, utc,
- local_opt, update, testing, predict, get);
-
- hwclock_exit(rc);
+ /* Avoid writing adjtime file if we don't have to. */
+ adjtime.dirty = FALSE;
+ ctl.universal = hw_clock_is_utc(&ctl, adjtime);
+ rc = manipulate_clock(&ctl, set_time, startup_time, &adjtime);
+ hwclock_exit(&ctl, rc);
return rc; /* Not reached */
}
-#ifdef HAVE_LIBAUDIT
-/*
- * hwclock_exit calls either this function or plain exit depending
- * HAVE_LIBAUDIT see also clock.h
- */
-void __attribute__((__noreturn__)) hwaudit_exit(int status)
+void
+hwclock_exit(const struct hwclock_control *ctl
+#ifndef HAVE_LIBAUDIT
+ __attribute__((__unused__))
+#endif
+ , int status)
{
- if (hwaudit_on) {
+#ifdef HAVE_LIBAUDIT
+ if (ctl->hwaudit_on && !ctl->testing) {
audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG,
- "changing system time", NULL, NULL, NULL,
+ "op=change-system-time", NULL, NULL, NULL,
status ? 0 : 1);
close(hwaudit_fd);
}
+#endif
exit(status);
}
-#endif
/*
* History of this program:
* with any functions by these names, you will have unresolved external
* references when you link.
*
- * The program is designed to run setuid superuser, since we need to be able
- * to do direct I/O. (More to the point: we need permission to execute the
- * iopl() system call). (However, if you use one of the methods other than
- * direct ISA I/O to access the clock, no setuid is required).
- *
* Here's some info on how we must deal with the time that elapses while
* this program runs: There are two major delays as we run:
*