#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 "hwclock.h"
#include "timeutils.h"
#include "env.h"
+#include "xalloc.h"
#ifdef HAVE_LIBAUDIT
#include <libaudit.h>
#endif
/* The struct that holds our hardware access routines */
-struct clock_ops *ur;
+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,
* 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.
*/
};
-/*
- * 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.
*/
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;
-
- if (access(ctl->adj_file_name, R_OK) != 0) {
- /* 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(ctl->adj_file_name, "r"); /* open file for reading */
if (adjfile == NULL) {
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 (ctl->debug) {
printf(_
("Last drift adjustment done at %ld seconds after 1969\n"),
if (err)
return err;
- if (ctl->badyear)
- read_date_from_file(&tm);
-
if (ctl->debug)
printf(_
("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
new_broken_time.tm_sec, (long)newtime);
if (ctl->testing)
- printf(_("Clock not changed - testing only.\n"));
- else {
- if (ctl->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);
- }
+ printf(_("Test mode: clock was not changed\n"));
+ else
ur->set_hardware_clock(ctl, &new_broken_time);
- }
}
/*
}
}
-/*
- * 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 struct hwclock_control *ctl,
- 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 (!ctl->date_opt) {
- warnx(_("No --date option specified."));
- return 14;
- }
-
- /* prevent overflow - a security risk */
- if (strlen(ctl->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(ctl->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\" +%s%%s",
- ctl->date_opt, magic);
- if (ctl->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 (ctl->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 (ctl->debug)
- printf(_("date string %s equates to "
- "%ld seconds since 1969.\n"),
- ctl->date_opt, *time_p);
- }
- }
- pclose(date_child_fp);
-
- return retcode;
-}
-
/*
* Set the System Clock to time 'newtime'.
*
}
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 };
}
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 };
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
static void save_adjtime(const struct hwclock_control *ctl,
const struct adjtime *adjtime)
{
- char newfile[412]; /* Stuff to write to disk file */
+ char *content; /* Stuff to write to disk file */
+ FILE *fp;
+ int err = 0;
- 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,
- adjtime->last_adj_time,
- adjtime->not_adjusted,
- adjtime->last_calib_time,
- (adjtime->local_utc == LOCAL) ? "LOCAL" : "UTC");
+ if (!adjtime->dirty)
+ return;
- if (ctl->testing) {
- printf(_
- ("Not updating adjtime file because of testing mode.\n"));
- printf(_("Would have written the following to %s:\n%s"),
- ctl->adj_file_name, newfile);
- } else {
- FILE *adjfile;
- int err = 0;
-
- adjfile = fopen(ctl->adj_file_name, "w");
- if (adjfile == 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(newfile, adjfile) < 0) {
- warn(_
- ("Could not update file with the clock adjustment "
- "parameters (%s) in it"),
- ctl->adj_file_name);
- err = 1;
- }
- if (close_stream(adjfile) != 0) {
- warn(_
- ("Could not update file with the clock adjustment "
- "parameters (%s) in it"),
- ctl->adj_file_name);
- err = 1;
- }
- }
- if (err)
- warnx(_
- ("Drift adjustment parameters not updated."));
+ 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."));
}
/*
if (ctl->directisa)
ur = probe_for_cmos_clock();
-
#ifdef __linux__
if (!ur)
ur = probe_for_rtc_clock(ctl);
#endif
+ if (ur) {
+ if (ctl->debug)
+ puts(ur->interface_name);
- if (ctl->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);
}
}
manipulate_clock(const struct hwclock_control *ctl, const time_t set_time,
const struct timeval startup_time, struct adjtime *adjtime)
{
- /* 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 (!ctl->systz && !ctl->predict) {
- no_auth = ur->get_permissions();
- if (no_auth)
- return EX_NOPERM;
- }
+ if (!ctl->systz && !ctl->predict && ur->get_permissions())
+ return EX_NOPERM;
if ((ctl->set || ctl->systohc || ctl->adjust) &&
(adjtime->local_utc == UTC) != ctl->universal) {
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 && !ctl->set && !ctl->systohc)
+ if (rc == RTC_BUSYWAIT_FAILED && !ctl->set && !ctl->systohc)
return EX_IOERR;
gettimeofday(&read_time, NULL);
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.
- */
-#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.
+/**
+ * Get or set the kernel RTC driver's epoch on Alpha machines.
+ * ISA machines are hard coded for 1900.
*/
-# ifndef __alpha__
-static void
-manipulate_epoch(const struct hwclock_control *ctl __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 struct hwclock_control *ctl)
{
if (ctl->getepoch) {
unsigned long epoch;
- if (get_epoch_rtc(ctl, &epoch, 0))
+ if (get_epoch_rtc(ctl, &epoch))
warnx(_
("Unable to get the epoch value from the kernel."));
else
("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 struct hwclock_control *ctl)
-{
- struct tm tm;
- struct timeval tv;
- double time1_sys, time2_sys;
- time_t time1_hw, time2_hw;
- bool hclock_valid = FALSE, first_pass = TRUE;
-
- if (ur->get_permissions())
- return EX_NOPERM;
-
- /* dummy call for increased precision */
- gettimeofday(&tv, NULL);
-
- synchronize_to_clock_tick(ctl);
- ur->read_hardware_clock(ctl, &tm);
-
- gettimeofday(&tv, NULL);
- time1_sys = tv.tv_sec + tv.tv_usec / 1000000.0;
-
- mktime_tz(ctl, tm, &hclock_valid, &time1_hw);
-
- while (1) {
- double res;
-
- synchronize_to_clock_tick(ctl);
- ur->read_hardware_clock(ctl, &tm);
-
- gettimeofday(&tv, NULL);
- time2_sys = tv.tv_sec + tv.tv_usec / 1000000.0;
-
- mktime_tz(ctl, tm, &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);
- }
- fflush(stdout);
- 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 struct hwclock_control *ctl, 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(ctl, 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 = { 0 };
+ 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.
/* 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);
atexit(close_stdout);
while ((c = getopt_long(argc, argv,
- "?hvVDacrsuwAJSFf:", longopts, NULL)) != -1) {
+ "hvVDalrsuwf:", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
break;
case 'a':
ctl.adjust = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
- case 'c':
- ctl.compare = 1;
+ case 'l':
+ ctl.local_opt = 1; /* --localtime */
break;
case 'r':
ctl.show = 1;
break;
case 's':
ctl.hctosys = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
case 'u':
ctl.utc = 1;
break;
case 'w':
ctl.systohc = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
-#ifdef __alpha__
- case 'A':
- ctl.ARCconsole = 1;
- break;
- case 'J':
- ctl.Jensen = 1;
- break;
- case 'S':
- ctl.SRM = 1;
- break;
- case 'F':
- ctl.funky_toy = 1;
- break;
-#endif
case OPT_SET:
ctl.set = 1;
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
-#ifdef __linux__
+#if defined(__linux__) && defined(__alpha__)
case OPT_GETEPOCH:
ctl.getepoch = 1;
+ ctl.show = 0;
break;
case OPT_SETEPOCH:
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:
ctl.noadjfile = 1;
break;
- case OPT_LOCALTIME:
- ctl.local_opt = 1; /* --localtime */
- break;
- case OPT_BADYEAR:
- ctl.badyear = 1;
- break;
case OPT_DIRECTISA:
ctl.directisa = 1;
break;
case OPT_DATE:
ctl.date_opt = optarg; /* --date */
break;
- case OPT_EPOCH:
- ctl.epoch_option = /* --epoch */
- strtoul_or_err(optarg, _("invalid epoch argument"));
- break;
case OPT_ADJFILE:
ctl.adj_file_name = optarg; /* --adjfile */
break;
case OPT_SYSTZ:
ctl.systz = 1; /* --systz */
+ ctl.show = 0;
+ ctl.hwaudit_on = 1;
break;
- case OPT_PREDICT_HC:
- ctl.predict = 1; /* --predict-hc */
+ case OPT_PREDICT:
+ ctl.predict = 1; /* --predict */
+ ctl.show = 0;
break;
case OPT_GET:
ctl.get = 1; /* --get */
+ ctl.show = 0;
break;
case OPT_UPDATE:
ctl.update = 1; /* --update-drift */
out_version();
return 0;
case 'h': /* --help */
- case '?':
+ usage(&ctl);
default:
- usage(&ctl, NULL);
+ errtryhelp(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
-#ifdef HAVE_LIBAUDIT
- if (!ctl.testing) {
- if (ctl.adjust || ctl.hctosys || ctl.systohc ||
- ctl.set || ctl.setepoch) {
- ctl.hwaudit_on = 1;
- }
- }
-#endif
if (argc > 0) {
- usage(&ctl, _("%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 (!ctl.adj_file_name)
ctl.adj_file_name = _PATH_ADJTIME;
+ 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(&ctl, EX_USAGE);
}
-#ifdef __alpha__
- set_cmos_epoch(&ctl);
- set_cmos_access(&ctl);
-#endif
if (ctl.set || ctl.predict) {
- rc = interpret_date_string(&ctl, &set_time);
- /* (time-consuming) */
- if (rc != 0) {
- warnx(_("No usable set-to time. "
- "Cannot set clock."));
+ 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 (!(ctl.show | ctl.set | ctl.systohc | ctl.hctosys |
- ctl.systz | ctl.adjust | ctl.getepoch | ctl.setepoch |
- ctl.predict | ctl.compare | ctl.get))
- ctl.show = 1; /* default to show */
-
-#ifdef __linux__
+#if defined(__linux__) && defined(__alpha__)
if (ctl.getepoch || ctl.setepoch) {
manipulate_epoch(&ctl);
hwclock_exit(&ctl, EX_OK);
if (ctl.debug)
out_version();
- if (!ctl.systz && !ctl.predict) {
+ if (!ctl.systz && !ctl.predict)
determine_clock_access_method(&ctl);
- if (!ur) {
- 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);
- }
- }
if (!ctl.noadjfile && !(ctl.systz && (ctl.utc || ctl.local_opt))) {
if ((rc = read_adjtime(&ctl, &adjtime)) != 0)
/* Avoid writing adjtime file if we don't have to. */
adjtime.dirty = FALSE;
ctl.universal = hw_clock_is_utc(&ctl, adjtime);
- if (ctl.compare) {
- if (compare_clock(&ctl))
- hwclock_exit(&ctl, EX_NOPERM);
-
- rc = EX_OK;
- } else
- rc = manipulate_clock(&ctl, set_time, startup_time, &adjtime);
-
+ rc = manipulate_clock(&ctl, set_time, startup_time, &adjtime);
hwclock_exit(&ctl, rc);
return rc; /* Not reached */
}
-void __attribute__((__noreturn__))
+void
hwclock_exit(const struct hwclock_control *ctl
#ifndef HAVE_LIBAUDIT
__attribute__((__unused__))
, int status)
{
#ifdef HAVE_LIBAUDIT
- if (ctl->hwaudit_on) {
+ if (ctl->hwaudit_on && !ctl->testing) {
audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG,
"op=change-system-time", NULL, NULL, NULL,
status ? 0 : 1);