diff options
-rw-r--r-- | sbin/adjkerntz/adjkerntz.8 | 51 | ||||
-rw-r--r-- | sbin/adjkerntz/adjkerntz.c | 259 |
2 files changed, 149 insertions, 161 deletions
diff --git a/sbin/adjkerntz/adjkerntz.8 b/sbin/adjkerntz/adjkerntz.8 index 9678bf2..1ddd72b 100644 --- a/sbin/adjkerntz/adjkerntz.8 +++ b/sbin/adjkerntz/adjkerntz.8 @@ -30,28 +30,21 @@ .Nd "adjusts the kernel time if the machine runs wall CMOS clock" .Sh SYNOPSIS .Nm adjkerntz -.Fl i -.Op Fl v -.Nm adjkerntz -.Fl a -.Op Fl v .Sh DESCRIPTION .Nm Adjkerntz +daemon fixes kernel time (makes it UTC) using the current wall CMOS clock value, the current time zone rule and the kernel timezone value. The adjustment is enabled only if the file .Pa /etc/wall_cmos_clock exists, in other cases it is assumed that the machine runs UTC CMOS clock and .Nm adjkerntz -does nothing. +does nothing and exits immediately. .Pp -The adjustment is needed at boot stage and when a time zone -change occurs, so -.Nm adjkerntz -can be called in two forms: -.Bl -tag -width 4n -.It Cm Fl i -initialization call from +The adjustment is usually needed at boot stage and when a time zone +change occurs. +.Nm Adjkerntz +started in .Pa /etc/rc (before any daemons are started). .Nm Adjkerntz @@ -60,27 +53,16 @@ makes the first adjustment and the initial time zone offset is stored into kernel variable for following subsequent .Nm adjkerntz -calls. -.It Cm Fl a -This form is needed, when time zone changes occur. +time zone change spying. +When time zone changes occur, .Nm Adjkerntz uses the previously stored time zone offset and the changed time zone rule to -produce the new time zone offset, fix the kernel time and store the new +produce the new time zone offset, fix RTC clock and store the new offset into .Pa adjkerntz kernel variable too. -It is recommended to use this form in root's -.Xr crontab 5 -near 3am, -since this time matches most modern time zone changes. -.It Cm Fl v -This option is for diagnostic purposes. It causes -.Nm adjkerntz -to print differences between the old and new time zone offsets -to stdout. -.El .Pp .Nm Adjkerntz clears the kernel timezone structure and makes kernel always run at UTC @@ -92,12 +74,16 @@ Super-user privilege is required for all operations. Time zone change rule, see .Xr tzset 3 ; not needed when +.Xr /usr/sbin/tzsetup +or .Xr zic 8 is used. .Sh FILES .Bl -tag -width /etc/wall_cmos_clock -compact .It Pa /etc/localtime -Link to the current zone info file, see +current zoneinfo file, see +.Xr /usr/sbin/tzsetup +and .Xr zic 8 . .It Pa /etc/wall_cmos_clock Empty file. @@ -107,10 +93,13 @@ absence indicates UTC CMOS clock. .Xr tzset 3 , .Xr zic 8 , .Xr rc 8 , -.Xr crontab 5 +.Xr sysctl 8 , +.Xr /usr/sbin/tzsetup .Sh DIAGNOSTICS -No diagnostics, unless \-v option is specified. -If any error occurs, an error message printed to stderr and +No diagnostics. +If any error occurs, an error message printed via +.Xr syslog 3 +and .Nm adjkerntz exits with return code greater than zero. .Sh AUTHOR diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c index 0e19bad..8c50cce 100644 --- a/sbin/adjkerntz/adjkerntz.c +++ b/sbin/adjkerntz/adjkerntz.c @@ -43,6 +43,7 @@ char copyright[] = #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <syslog.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/param.h> @@ -51,6 +52,8 @@ char copyright[] = #include "pathnames.h" +#define REPORT_PERIOD (30*60) + int main(argc, argv) int argc; char **argv; @@ -64,180 +67,176 @@ int main(argc, argv) /* Avoid time_t here, can be unsigned long or worse */ long offset, utcsec, localsec, diff; time_t initial_sec, final_sec; - int ch, init = -1, verbose = 0; + int init = 1, nonex1 = 0, nonex2 = 0; int disrtcset, need_restore = 0; - while ((ch = getopt(argc, argv, "aiv")) != EOF) - switch((char)ch) { - case 'i': /* initial call, save offset */ - if (init != -1) - goto usage; - init = 1; - break; - case 'a': /* adjustment call, use saved offset */ - if (init != -1) - goto usage; - init = 0; - break; - case 'v': /* verbose */ - verbose = 1; - break; - default: - usage: - fprintf(stderr, "Usage:\n\ -\tadjkerntz -i [-v]\t(initial call from /etc/rc)\n\ -\tadjkerntz -a [-v]\t(adjustment call from crontab)\n"); - return 2; - } - if (init == -1) - goto usage; + if (argc != 1) { + fprintf(stderr, "Usage:\tadjkerntz\n"); + return 2; + } if (access(_PATH_CLOCK, F_OK)) return 0; - /* Restore saved offset */ - - mib[0] = CTL_MACHDEP; - mib[1] = CPU_ADJKERNTZ; - len = sizeof(kern_offset); - if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) { - perror("sysctl(get_offset)"); - return 1; - } - -/****** Critical section, do all things as fast as possible ******/ - - /* get local CMOS clock and possible kernel offset */ - if (gettimeofday(&tv, &tz)) { - perror("gettimeofday"); - return 1; - } - - /* get the actual local timezone difference */ - initial_sec = tv.tv_sec; - local = *localtime(&initial_sec); - utc = *gmtime(&initial_sec); - utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ - /* because it assumed local time */ - - /* calculate local CMOS diff from GMT */ - - utcsec = mktime(&utc); - localsec = mktime(&local); - if (utcsec == -1 || localsec == -1) { - /* - * XXX user can only control local time, and it is - * unacceptable to fail here for -i. 2:30 am in the - * middle of the nonexistent hour means 3:30 am. - */ - fprintf(stderr, - "Nonexistent local time - try again in an hour\n"); + if (daemon(0, 0)) { + perror("daemon"); return 1; } - offset = utcsec - localsec; - /* correct the kerneltime for this diffs */ - /* subtract kernel offset, if present, old offset too */ + openlog("adjkerntz", LOG_PID, LOG_DAEMON); - diff = offset - tz.tz_minuteswest * 60 - kern_offset; + /* Spy on timezone changes with 1sec accurancy */ + for (;;sleep(1)) { - if (diff != 0) { - - /* Yet one step for final time */ +/****** Critical section, do all things as fast as possible ******/ - final_sec = tv.tv_sec + diff; + /* get local CMOS clock and possible kernel offset */ + if (gettimeofday(&tv, &tz)) { + syslog(LOG_ERR, "gettimeofday: %m"); + return 1; + } /* get the actual local timezone difference */ - local = *localtime(&final_sec); - utc = *gmtime(&final_sec); + initial_sec = tv.tv_sec; + local = *localtime(&initial_sec); + utc = *gmtime(&initial_sec); utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ /* because it assumed local time */ + /* calculate local CMOS diff from GMT */ + utcsec = mktime(&utc); localsec = mktime(&local); if (utcsec == -1 || localsec == -1) { /* - * XXX as above. The user has even less control, - * but perhaps we never get here. + * XXX user can only control local time, and it is + * unacceptable to fail here for init. 2:30 am in the + * middle of the nonexistent hour means 3:30 am. */ - fprintf(stderr, - "Nonexistent (final) local time - try again in an hour\n"); - return 1; + if (!(nonex1++ % REPORT_PERIOD)) { + if (nonex1 > 1) + nonex1 = 0; + syslog(LOG_WARNING, + "Nonexistent local time -- retry each second"); + } + continue; } offset = utcsec - localsec; + mib[0] = CTL_MACHDEP; + mib[1] = CPU_ADJKERNTZ; + len = sizeof(kern_offset); + if (sysctl(mib, 2, &kern_offset, &len, NULL, 0) == -1) { + syslog(LOG_ERR, "sysctl(get_offset): %m"); + return 1; + } + /* correct the kerneltime for this diffs */ /* subtract kernel offset, if present, old offset too */ diff = offset - tz.tz_minuteswest * 60 - kern_offset; if (diff != 0) { - tv.tv_sec += diff; - tv.tv_usec = 0; /* we are restarting here... */ - stv = &tv; + + /* Yet one step for final time */ + + final_sec = tv.tv_sec + diff; + + /* get the actual local timezone difference */ + local = *localtime(&final_sec); + utc = *gmtime(&final_sec); + utc.tm_isdst = local.tm_isdst; /* Use current timezone for mktime(), */ + /* because it assumed local time */ + + utcsec = mktime(&utc); + localsec = mktime(&local); + if (utcsec == -1 || localsec == -1) { + /* + * XXX as above. The user has even less control, + * but perhaps we never get here. + */ + if (!(nonex2++ % REPORT_PERIOD)) { + if (nonex2 > 1) + nonex2 = 0; + syslog(LOG_WARNING, + "Nonexistent (final) local time -- retry each second"); + } + continue; + } + offset = utcsec - localsec; + + /* correct the kerneltime for this diffs */ + /* subtract kernel offset, if present, old offset too */ + + diff = offset - tz.tz_minuteswest * 60 - kern_offset; + + if (diff != 0) { + tv.tv_sec += diff; + tv.tv_usec = 0; /* we are restarting here... */ + stv = &tv; + } + else + stv = NULL; } else stv = NULL; - } - else - stv = NULL; - - if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { - tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ - stz = &tz; - } - else - stz = NULL; - if (stz != NULL || stv != NULL) { - if (init && stv != NULL) { - mib[0] = CTL_MACHDEP; - mib[1] = CPU_DISRTCSET; - len = sizeof(disrtcset); - if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) { - perror("sysctl(get_disrtcset)"); - return 1; - } - if (disrtcset == 0) { - disrtcset = 1; - need_restore = 1; - if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { - perror("sysctl(set_disrtcset)"); + if (tz.tz_dsttime != 0 || tz.tz_minuteswest != 0) { + tz.tz_dsttime = tz.tz_minuteswest = 0; /* zone info is garbage */ + stz = &tz; + } + else + stz = NULL; + + if (stz != NULL || stv != NULL) { + if (init && stv != NULL) { + mib[0] = CTL_MACHDEP; + mib[1] = CPU_DISRTCSET; + len = sizeof(disrtcset); + if (sysctl(mib, 2, &disrtcset, &len, NULL, 0) == -1) { + syslog(LOG_ERR, "sysctl(get_disrtcset): %m"); return 1; } + if (disrtcset == 0) { + disrtcset = 1; + need_restore = 1; + if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { + syslog(LOG_ERR, "sysctl(set_disrtcset): %m"); + return 1; + } + } + } + /* stz means that kernel zone shifted */ + /* clock needs adjustment even if !init */ + if ((init || stz != NULL) && settimeofday(stv, stz)) { + syslog(LOG_ERR, "settimeofday: %m"); + return 1; } } - if ((init || stv == NULL) && settimeofday(stv, stz)) { - perror("settimeofday"); - return 1; - } - } - if (kern_offset != offset) { - kern_offset = offset; - mib[0] = CTL_MACHDEP; - mib[1] = CPU_ADJKERNTZ; - len = sizeof(kern_offset); - if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) { - perror("sysctl(update_offset)"); - return 1; + if (kern_offset != offset) { + kern_offset = offset; + mib[0] = CTL_MACHDEP; + mib[1] = CPU_ADJKERNTZ; + len = sizeof(kern_offset); + if (sysctl(mib, 2, NULL, NULL, &kern_offset, len) == -1) { + syslog(LOG_ERR, "sysctl(update_offset): %m"); + return 1; + } } - } - if (need_restore) { - disrtcset = 0; - if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { - perror("sysctl(restore_disrtcset)"); - return 1; + if (need_restore) { + need_restore = 0; + disrtcset = 0; + if (sysctl(mib, 2, NULL, NULL, &disrtcset, len) == -1) { + syslog(LOG_ERR, "sysctl(restore_disrtcset): %m"); + return 1; + } } - } /****** End of critical section ******/ - if (verbose) - printf("Calculated zone offset difference: %ld seconds\n", - diff); - - return 0; + init = 0; + } + return 1; } - |