📄 hwclock.c
字号:
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();}static voidread_hardware_clock(const bool universal, bool *valid_p, time_t *systime_p){/*---------------------------------------------------------------------------- Read the hardware clock and return the current time via <tm> argument. Use the method indicated by <method> argument to access the hardware clock.-----------------------------------------------------------------------------*/ struct tm tm; int err; err = ur->read_hardware_clock(&tm); if (badyear) read_date_from_file(&tm); if (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);}static voidset_hardware_clock(const time_t newtime, const bool universal, const bool testing) {/*---------------------------------------------------------------------------- Set the Hardware Clock to the time <newtime>, in local time zone or UTC, according to <universal>.----------------------------------------------------------------------------*/ int err; struct tm new_broken_time; /* Time to which we will set Hardware Clock, in broken down format, in the time zone of caller's choice */ if (universal) new_broken_time = *gmtime(&newtime); else new_broken_time = *localtime(&newtime); if (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); } err = ur->set_hardware_clock(&new_broken_time); }}static voidset_hardware_clock_exact(const time_t sethwtime, const struct timeval refsystime, const bool universal, const bool testing) {/*---------------------------------------------------------------------------- Set the Hardware Clock to the time "sethwtime", in local time zone or UTC, according to "universal". Wait for a fraction of a second so that "sethwtime" is the value of the Hardware Clock as of system time "refsystime", which is in the past. For example, if "sethwtime" is 14:03:05 and "refsystime" is 12:10:04.5 and the current system time is 12:10:06.0: Wait .5 seconds (to make exactly 2 seconds since "refsystime") and then set the Hardware Clock to 14:03:07, thus getting a precise and retroactive setting of the clock. (Don't be confused by the fact that the system clock and the Hardware Clock differ by two hours in the above example. That's just to remind you that there are two independent time scales here). This function ought to be able to accept set times as fractional times. Idea for future enhancement.-----------------------------------------------------------------------------*/ time_t newhwtime; struct timeval beginsystime, nowsystime; time_resync: gettimeofday(&beginsystime, NULL); newhwtime = sethwtime + (int) time_diff(beginsystime, refsystime) + 1; if (debug) printf(_("Time elapsed since reference time has been %.6f seconds.\n" "Delaying further to reach the next full second.\n"), time_diff(beginsystime, refsystime)); /* Now delay some more until Hardware Clock time newhwtime arrives */ do { float tdiff; gettimeofday(&nowsystime, NULL); tdiff = time_diff(nowsystime, beginsystime); if (tdiff < 0) goto time_resync; /* probably time was reset */ } while (time_diff(nowsystime, refsystime) < newhwtime - sethwtime); set_hardware_clock(newhwtime, universal, testing);}static voiddisplay_time(const bool hclock_valid, const time_t systime, const double sync_duration) {/*---------------------------------------------------------------------------- Put the time "systime" on standard output in display format. Except if hclock_valid == false, just tell standard output that we don't know what time it is. Include in the output the adjustment "sync_duration".-----------------------------------------------------------------------------*/ if (!hclock_valid) fprintf(stderr, _("The Hardware Clock registers contain values that are " "either invalid (e.g. 50th day of month) or beyond the range " "we can handle (e.g. Year 2095).\n")); else { struct tm *lt; char *format = "%c"; char ctime_now[200]; lt = localtime(&systime); strftime(ctime_now, sizeof(ctime_now), format, lt); printf(_("%s %.6f seconds\n"), ctime_now, -(sync_duration)); }}static intinterpret_date_string(const char *date_opt, time_t * const time_p) {/*---------------------------------------------------------------------------- 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.----------------------------------------------------------------------------*/ 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) { fprintf(stderr, _("No --date option specified.\n")); return 14; } /* prevent overflow - a security risk */ if (strlen(date_opt) > sizeof(date_command) - 50) { fprintf(stderr, _("--date argument too long\n")); return 13; } /* Quotes in date_opt would ruin the date command we construct. */ if (strchr(date_opt, '"') != NULL) { fprintf(stderr, _("The value of the --date option is not a valid date.\n" "In particular, it contains quotation marks.\n")); 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) { outsyserr(_("Unable to run 'date' program in /bin/sh shell. " "popen() failed")); return 10; } date_resp[0] = '\0'; /* in case fgets fails */ fgets(date_resp, sizeof(date_resp), date_child_fp); if (debug) printf(_("response from date command = %s\n"), date_resp); if (strncmp(date_resp, magic, sizeof(magic)-1) != 0) { fprintf(stderr, _("The date command issued by %s returned " "unexpected results.\n" "The command was:\n %s\n" "The response was:\n %s\n"), MYNAME, 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) { fprintf(stderr, _("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"), MYNAME, 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); } } fclose(date_child_fp); return retcode;} static int set_system_clock(const bool hclock_valid, const time_t newtime, const bool testing) {/*---------------------------------------------------------------------------- Set the System Clock to time 'newtime'. Also set the kernel time zone value to the value indicated by the TZ environment variable and/or /usr/lib/zoneinfo/, interpreted as tzset() would interpret them. EXCEPT: if hclock_valid is false, just issue an error message saying there is no valid time in the Hardware Clock to which to set the system time. If 'testing' is true, don't actually update anything -- just say we would have.-----------------------------------------------------------------------------*/ int retcode; if (!hclock_valid) { fprintf(stderr, _("The Hardware Clock does not contain a valid time, so " "we cannot set the System Time from it.\n")); retcode = 1; } else { struct timeval tv; struct tm *broken; int minuteswest; int rc; tv.tv_sec = newtime; tv.tv_usec = 0; broken = localtime(&newtime);#ifdef HAVE_tm_gmtoff minuteswest = -broken->tm_gmtoff/60; /* GNU extension */#else minuteswest = timezone/60; if (broken->tm_isdst) minuteswest -= 60;#endif if (debug) { printf(_("Calling settimeofday:\n")); printf(_("\ttv.tv_sec = %ld, tv.tv_usec = %ld\n"), (long) tv.tv_sec, (long) tv.tv_usec); printf(_("\ttz.tz_minuteswest = %d\n"), minuteswest); } if (testing) { printf(_("Not setting system clock because running in test mode.\n")); retcode = 0; } else { const struct timezone tz = { minuteswest, 0 }; rc = settimeofday(&tv, &tz); if (rc) { if (errno == EPERM) { fprintf(stderr, _("Must be superuser to set system clock.\n")); retcode = EX_NOPERM; } else { outsyserr(_("settimeofday() failed")); retcode = 1; } } else retcode = 0; } } return retcode;}static voidadjust_drift_factor(struct adjtime *adjtime_p, const time_t nowtime, const bool hclock_valid, const time_t hclocktime, const double sync_delay) {/*------------------------------------------------------------------------ Update the drift factor in <*adjtime_p> to reflect the fact that the Hardware Clock was calibrated to <nowtime> and before that was set to <hclocktime>. We record in the adjtime file the time at which we last calibrated the clock so we can compute the drift rate each time we calibrate. EXCEPT: if <hclock_valid> is false, assume Hardware Clock was not set before to anything meaningful and regular adjustments have not been done, so don't adjust the drift factor. ------------------------------------------------------------------------*/ if (!hclock_valid) { if (debug) printf(_("Not adjusting drift factor because the " "Hardware Clock previously contained " "garbage.\n")); } else if (adjtime_p->last_calib_time == 0) { if (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 - adjtime_p->last_calib_time) < 23 * 60 * 60) { if (debug) printf(_("Not adjusting drift factor because it has " "been less than a day since the last " "calibration.\n")); } else if (adjtime_p->last_calib_time != 0) { /* * At adjustment time we adjust the hardware clock according * to the contents of /etc/adjtime. * * At calibration time we set the hardware clock and * update /etc/adjtime, that is, for each calibration * (except the first) we also do an adjustment. * * We are now at calibration time. * * Let us do computation in doubles. (Floats almost suffice, * but 195 days + 1 second equals 195 days in floats.) */ const double sec_per_day = 24.0 * 60.0 * 60.0; double atime_per_htime; double adj_days, cal_days; double exp_drift, unc_drift; double factor_adjust; /* Adjusted time units per hardware time unit */ atime_per_htime = 1.0 + adjtime_p->drift_factor / sec_per_day; /* Days since last adjustment (in hardware clock time) */ adj_days = (double)(hclocktime - adjtime_p->last_adj_time) / sec_per_day; /* Expected drift (sec) since last adjustment */ exp_drift = adj_days * adjtime_p->drift_factor + adjtime_p->not_adjusted; /* Uncorrected drift (sec) since last calibration */ unc_drift = (double)(nowtime - hclocktime) + sync_delay - exp_drift; /* Days since last calibration (in hardware clock time) */ cal_days = ((double)(adjtime_p->last_adj_time - adjtime_p->last_calib_time) + adjtime_p->not_adjusted) / (sec_per_day * atime_per_htime) + adj_days; /* Amount to add to previous drift factor */ factor_adjust = unc_drift / cal_days; if (debug) printf(_("Clock drifted %.1f seconds in the past " "%d seconds in spite of a drift factor of " "%f seconds/day.\n" "Adjusting drift factor by %f seconds/day\n"), unc_drift, (int) (nowtime - adjtime_p->last_calib_time), adjtime_p->drift_factor, factor_adjust); adjtime_p->drift_factor += factor_adjust; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -