+* [Bug 2597] leap file processing -- loose ends
+ log daily warning when leap info less than 28 days to expiration or
+ already expired; nag hourly on last day before expiration; log when
+ leapfile name is invalid
(4.2.7p440) 2014/04/09 Released by Harlan Stenn <stenn@ntp.org>
* [Bug 2536] ntpd sandboxing support (libseccomp2) cleanup.
* [Bug 2570] cleanup: fix log format for successful leapfile load
extern int mprintf_clock_stats(sockaddr_u *, const char *, ...)
NTP_PRINTF(2, 3);
extern void record_raw_stats (sockaddr_u *srcadr, sockaddr_u *dstadr, l_fp *t1, l_fp *t2, l_fp *t3, l_fp *t4, int leap, int version, int mode, int stratum, int poll, int precision, double root_delay, double root_dispersion, u_int32 refid);
-extern int check_leap_file (void);
+extern void check_leap_file (int is_daily_check, u_int32 ntptime, const time_t * systime);
extern void record_crypto_stats (sockaddr_u *, const char *);
#ifdef DEBUG
extern void record_timing_stats (const char *);
#include <config.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <ctype.h>
#include "ntp_types.h"
#include "ntp_leapsec.h"
#include "ntp.h"
#include "vint64ops.h"
+#include "lib_strbuf.h"
#include "isc/sha1.h"
+static const char * const logPrefix = "leapsecond file";
+
/* ---------------------------------------------------------------------
* GCC is rather sticky with its 'const' attribute. We have to do it more
* explicit than with a cast if we want to get rid of a CONST qualifier.
static void reset_times(leap_table_t*);
static int leapsec_add(leap_table_t*, const vint64*, int);
static int leapsec_raw(leap_table_t*, const vint64 *, int, int);
+static char * lstostr(const vint64 * ts);
/* =====================================================================
* Get & Set the current leap table
* or this will fail.
*/
int/*BOOL*/
-leapsec_load_file(
+leapsec_load_stream(
FILE * ifp ,
const char * fname)
{
- static const char * const logPrefix = "leapsecond file";
-
- leap_table_t * pt;
- int rcheck;
+ leap_table_t *pt;
+ int rcheck;
if (NULL == fname)
fname = "<unknown>";
break;
case LSVALID_NOHASH:
- msyslog(LOG_INFO, "%s ('%s'): no hash signature",
+ msyslog(LOG_NOTICE, "%s ('%s'): no hash signature",
logPrefix, fname);
break;
case LSVALID_BADHASH:
}
return FALSE;
}
- msyslog(LOG_NOTICE, "%s ('%s'): loaded", logPrefix, fname);
+
+ if (pt->head.size)
+ msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d",
+ logPrefix, fname, lstostr(&pt->head.expire),
+ lstostr(&pt->info[0].ttime), pt->info[0].taiof);
+ else
+ msyslog(LOG_NOTICE,
+ "%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)",
+ logPrefix, fname, lstostr(&pt->head.expire),
+ pt->head.base_tai);
+
return leapsec_set_table(pt);
}
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_load_file(
+ const char * fname,
+ struct stat * sb_old,
+ int/*BOOL*/ force)
+{
+ FILE * fp;
+ struct stat sb_new;
+ int rc;
+
+ /* just do nothing if there is no leap file */
+ if ( ! (fname && *fname))
+ return FALSE;
+
+ /* try to stat the leapfile */
+ if (0 != stat(fname, &sb_new)) {
+ msyslog(LOG_ERR, "%s ('%s'): stat failed: %m",
+ logPrefix, fname);
+ return FALSE;
+ }
+
+ /* silently skip to postcheck if no new file found */
+ if (NULL != sb_old) {
+ if (!force &&
+ sb_old->st_mtime == sb_new.st_mtime &&
+ sb_old->st_ctime == sb_new.st_ctime )
+ return FALSE;
+ *sb_old = sb_new;
+ }
+
+ /* try to open the leap file, complain if that fails */
+ if ((fp = fopen(fname, "r")) == NULL) {
+ msyslog(LOG_ERR,
+ "%s ('%s'): open failed: %m",
+ logPrefix, fname);
+ return FALSE;
+ }
+
+ rc = leapsec_load_stream(fp, fname);
+ fclose(fp);
+ return rc;
+}
+
/* ------------------------------------------------------------------ */
void
leapsec_getsig(
return LSVALID_GOODHASH;
}
+/*
+ * lstostr - prettyprint NTP seconds
+ */
+static char * lstostr(
+ const vint64 * ts)
+{
+ char * buf;
+ struct calendar tm;
+
+ LIB_GETBUF(buf);
+ ntpcal_ntp64_to_date(&tm, ts);
+ snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02dZ",
+ tm.year, tm.month, tm.monthday,
+ tm.hour, tm.minute);
+ return buf;
+}
+
+
+
/* -*- that's all folks! -*- */
#ifndef NTP_LEAPSEC_H
#define NTP_LEAPSEC_H
+struct stat;
+
+
/* function pointer types. Note that 'fprintf' and 'getc' can be casted
* to the dumper resp. reader type, provided the auxiliary argument is a
* valid FILE pointer in hat case.
*/
extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg);
-/* Read a leap second file. This is a convenience wrapper around the
- * generic load function, 'leapsec_load()'.
+/* Read a leap second file from stream. This is a convenience wrapper
+ * around the generic load function, 'leapsec_load()'.
+ */
+extern int/*BOOL*/ leapsec_load_stream(FILE * fp, const char * fname);
+
+/* Read a leap second file from file. It checks that the file exists and
+ * (if 'force' is not applied) the ctime/mtime has changed since the
+ * last load. If the file has to be loaded, either due to 'force' or
+ * changed time stamps, the 'stat()' results of the file are stored in
+ * '*sb' for the next cycle. Returns TRUE on successful load, FALSE
+ * otherwise. Uses 'leapsec_load_stream()' internally.
*/
-extern int/*BOOL*/ leapsec_load_file(FILE * fp, const char * fname);
+extern int/*BOOL*/ leapsec_load_file(const char * fname, struct stat * sb,
+ int/*BOOL*/force);
/* Get the current leap data signature. This consists of the last
* ransition, the table expiration, and the total TAI difference at the
#define TC_ERR (-1)
#endif
-extern char *leapseconds_file; /* name of the leapseconds file */
-
static void check_leapsec(u_int32, const time_t*, int/*BOOL*/);
/*
if (stats_timer <= current_time) {
stats_timer += SECSPERHR;
write_stats();
- if (sys_tai != 0 && leapsec_expired(now.l_ui, &tnow)) {
- int clf = check_leap_file();
-
- /*
- ** check_leap_file() returns -1 on a problem,
- ** 0 on an expired leapsecond file, or the number
- ** of days until the leapsecond file expires.
- **
- ** We only want to log stuff once/day.
- */
- if (check_leapfile < current_time) {
- check_leapfile += SECSPERDAY;
- if (-1 == clf) {
- /* nothing to do */
- } else if (0 == clf) {
- report_event(EVNT_LEAPVAL, NULL, NULL);
- msyslog(LOG_WARNING,
- "timer: leapseconds data file <%s> has expired!",
- leapseconds_file);
- } else if (clf < 31) {
- msyslog(LOG_WARNING,
- "timer: leapseconds data file <%s> will expire in less than %d days' time.", leapseconds_file, clf);
- }
- }
+ if (check_leapfile < current_time) {
+ check_leapfile += SECSPERDAY;
+ check_leap_file(TRUE, now.l_ui, &tnow);
+ } else {
+ check_leap_file(FALSE, now.l_ui, &tnow);
}
}
}
* File names
*/
static char *key_file_name; /* keys file name */
-char *leapseconds_file; /* leapseconds file name */
-struct stat leapseconds_file_sb1; /* leapseconds file stat() buffer */
-struct stat leapseconds_file_sb2; /* leapseconds file stat() buffer */
+static char *leapfile_name; /* leapseconds file name */
+static struct stat leapfile_stat; /* leapseconds file stat() buffer */
+static int /*BOOL*/have_leapfile = FALSE;
char *stats_drift_file; /* frequency file name */
static char *stats_temp_file; /* temp frequency file name */
static double wander_resid; /* last frequency update */
break;
}
fprintf(fp, "%d", (int)getpid());
- fclose(fp);;
+ fclose(fp);
break;
/*
if (!value || (len = strlen(value)) == 0)
break;
- leapseconds_file = erealloc(leapseconds_file, len + 1);
- memcpy(leapseconds_file, value, len + 1);
+ leapfile_name = erealloc(leapfile_name, len + 1);
+ memcpy(leapfile_name, value, len + 1);
- if ((fp = fopen(leapseconds_file, "r")) == NULL) {
- msyslog(LOG_ERR, "leapseconds file %s: %m",
- leapseconds_file);
- break;
- }
-
- if (-1 == fstat(fileno(fp), &leapseconds_file_sb1)) {
- msyslog(LOG_ERR,
- "leapseconds: stat(%s) failed: %m",
- leapseconds_file);
- } else if (leapsec_load_file(fp, leapseconds_file)) {
+ if (leapsec_load_file(leapfile_name, &leapfile_stat, TRUE)) {
leap_signature_t lsig;
leapsec_getsig(&lsig);
? "expired"
: "expires",
fstostr(lsig.etime));
+ have_leapfile = TRUE;
}
- fclose(fp);
break;
default:
* without SHA1 signature is accepted, but if there is a signature line,
* the signature must be valid or the file is rejected.
*/
-int
+void
check_leap_file(
- void
+ int is_daily_check,
+ uint32_t ntptime ,
+ const time_t *systime
)
{
- FILE *fp;
- struct stat *sp1 = &leapseconds_file_sb1;
- struct stat *sp2 = &leapseconds_file_sb2;
- int rc = INT_MAX; /* assume not expired for long a time */
- l_fp now;
+ static const char * const logPrefix = "leapsecond file";
+ int rc;
+ /* just do nothing if there is no leap file */
+ if ( ! (leapfile_name && *leapfile_name))
+ return;
+
+ /* try to load leapfile, force it if no leapfile loaded yet */
+ if (leapsec_load_file(
+ leapfile_name, &leapfile_stat, !have_leapfile))
+ have_leapfile = TRUE;
+ else if (!have_leapfile)
+ return;
- if (leapseconds_file) {
- get_systime(&now);
- if ((fp = fopen(leapseconds_file, "r")) == NULL) {
- msyslog(LOG_ERR,
- "check_leap_file: fopen(%s): %m",
- leapseconds_file);
- rc = -1;
- } else if (fstat(fileno(fp), &leapseconds_file_sb2)) {
+ /* test the expiration of the leap data and log with proper
+ * level and frequency (once/hour or once/day, depending on the
+ * state.
+ */
+ rc = leapsec_daystolive(ntptime, systime);
+ if (rc == 0) {
+ msyslog(LOG_WARNING,
+ "%s ('%s'): will expire in less than one day",
+ logPrefix, leapfile_name);
+ } else if (is_daily_check && rc < 28) {
+ if (rc < 0)
msyslog(LOG_ERR,
- "check_leap_file: stat(%s): %m",
- leapseconds_file);
- rc = -1;
- } else if ( (sp1->st_mtime != sp2->st_mtime)
- || (sp1->st_ctime != sp2->st_ctime)) {
- leapseconds_file_sb1 = leapseconds_file_sb2;
- if (!leapsec_load_file(fp, leapseconds_file))
- rc = -1;
- }
- if (rc >= 0) {
- rc = leapsec_daystolive(now.l_ui, NULL);
- if (rc < 0)
- rc = 0;
- }
- if (fp != NULL)
- fclose(fp);
+ "%s ('%s'): expired (%d days)",
+ logPrefix, leapfile_name, -rc);
+ else
+ msyslog(LOG_WARNING,
+ "%s ('%s'): will expire in less than %d days",
+ logPrefix, leapfile_name, 1+rc);
}
-
- return rc;
}