#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_filegen.h"
#include "ntp_if.h"
#include "ntp_stdlib.h"
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_IEEEFP_H
# include <ieeefp.h>
#endif
#ifdef HAVE_MATH_H
# include <math.h>
#endif
#ifdef DOSYNCTODR
# if !defined(VMS)
# include <sys/resource.h>
# endif
#endif
#if defined(VMS)
# include <descrip.h>
#endif
#include <vproc.h>
#include <sys/mman.h>
#include <signal.h>
static char *key_file_name;
static char *stats_drift_file;
static int drift_exists;
static char *stats_temp_file;
int stats_write_period = 3600;
double stats_write_tolerance = 0;
static double prev_drift_comp = 99999.;
#ifndef NTP_VAR
# ifndef SYS_WINNT
# define NTP_VAR "/var/NTP/"
# else
# define NTP_VAR "c:\\var\\ntp\\"
# endif
#endif
#ifndef MAXPATHLEN
# define MAXPATHLEN 256
#endif
static char statsdir[MAXPATHLEN] = NTP_VAR;
static FILEGEN peerstats;
static FILEGEN loopstats;
static FILEGEN clockstats;
static FILEGEN rawstats;
static FILEGEN sysstats;
#ifdef DEBUG_TIMING
static FILEGEN timingstats;
#endif
#ifdef OPENSSL
static FILEGEN cryptostats;
#endif
#include <mach/mach_port.h>
#include <mach/mach_interface.h>
#include <mach/mach_init.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/pwr_mgt/IOPM.h>
static io_connect_t getFB(void) {
static io_connect_t fb;
if (!fb) {
fb = IOPMFindPowerManagement(kIOMasterPortDefault);
}
return fb;
}
static unsigned long energy_saving(void) {
IOReturn err;
unsigned long min = 99;
io_connect_t fb = getFB();
if (fb) {
err = IOPMGetAggressiveness( fb, kPMMinutesToSleep, &min);
if (err == kIOReturnSuccess) {
if (min > 0) {
return min;
} else {
err = IOPMGetAggressiveness( fb, kPMMinutesToSpinDown, &min);
if (err == kIOReturnSuccess) {
return min;
}
}
}
}
return 0;
}
int stats_control;
double old_drift;
void
init_util(void)
{
stats_drift_file = 0;
stats_temp_file = 0;
key_file_name = 0;
filegen_register(&statsdir[0], "peerstats", &peerstats);
filegen_register(&statsdir[0], "loopstats", &loopstats);
filegen_register(&statsdir[0], "clockstats", &clockstats);
filegen_register(&statsdir[0], "rawstats", &rawstats);
filegen_register(&statsdir[0], "sysstats", &sysstats);
#ifdef OPENSSL
filegen_register(&statsdir[0], "cryptostats", &cryptostats);
#endif
#ifdef DEBUG_TIMING
filegen_register(&statsdir[0], "timingstats", &timingstats);
#endif
}
int
save_drift_file(
)
{
FILE *fp;
int rc = true;
vproc_transaction_t vt;
static off_t stats_size = 0;
sigset_t sigterm, oset;
sigemptyset(&sigterm);
sigaddset(&sigterm, SIGTERM);
sigprocmask(SIG_BLOCK, &sigterm, &oset);
vt = vproc_transaction_begin(NULL);
if (stats_drift_file != 0 && (!drift_exists || !energy_saving())) {
if ((fp = fopen(stats_temp_file, "w")) == NULL) {
msyslog(LOG_ERR, "can't open %s: %m",
stats_temp_file);
rc = false;
goto done;
}
stats_size = fprintf(fp, "%.3f\n", drift_comp * 1e6);
(void)fclose(fp);
#ifdef SYS_WINNT
(void) _unlink(stats_drift_file);
#endif
#ifndef NO_RENAME
(void) rename(stats_temp_file, stats_drift_file);
#else
if ((fp = fopen(stats_drift_file, "w")) == NULL) {
msyslog(LOG_ERR, "can't open %s: %m",
stats_drift_file);
rc = false;
}
#endif
drift_exists++;
#if defined(VMS)
{
$DESCRIPTOR(oldvers,";-1");
struct dsc$descriptor driftdsc = {
strlen(stats_drift_file),0,0,stats_drift_file };
while(lib$delete_file(&oldvers,&driftdsc) & 1) ;
}
#endif
} else {
static void *mmap_addr;
if (mmap_addr == 0) {
int fd = open(stats_drift_file, O_RDWR);
if (fd >= 0) {
mmap_addr = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
if (mmap_addr == MAP_FAILED) {
msyslog(LOG_ERR, "can't mmap %s: %m", stats_drift_file);
mmap_addr = 0;
rc = false;
}
close(fd);
} else {
msyslog(LOG_ERR, "can't open %s: %m", stats_drift_file);
rc = false;
}
} else {
off_t n = snprintf(mmap_addr, getpagesize(), "%.3f\n", drift_comp * 1e6);
if (n != stats_size) {
truncate(stats_drift_file, n);
stats_size = n;
}
}
}
done:
vproc_transaction_end(NULL, vt);
sigprocmask(SIG_SETMASK, &oset, NULL);
return rc;
}
void
write_stats(void)
{
#ifdef DOSYNCTODR
struct timeval tv;
#if !defined(VMS)
int prio_set;
#endif
#ifdef HAVE_GETCLOCK
struct timespec ts;
#endif
int o_prio;
#if !defined(VMS)
errno = 0;
prio_set = 0;
o_prio = getpriority(PRIO_PROCESS,0);
if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
prio_set = 1;
#endif
#ifdef HAVE_GETCLOCK
(void) getclock(TIMEOFDAY, &ts);
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
#else
GETTIMEOFDAY(&tv,(struct timezone *)NULL);
#endif
if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) {
msyslog(LOG_ERR, "can't sync battery time: %m");
}
#if !defined(VMS)
if (prio_set)
setpriority(PRIO_PROCESS, 0, o_prio);
#endif
#endif
NLOG(NLOG_SYSSTATIST)
msyslog(LOG_INFO,
"offset %.6f sec freq %.3f ppm error %.6f poll %d",
last_offset, drift_comp * 1e6, sys_jitter,
sys_poll);
record_sys_stats();
if ((u_long)(fabs(prev_drift_comp - drift_comp) * 1e9) <=
(u_long)(fabs(stats_write_tolerance * drift_comp) * 1e9)) {
return;
}
if (save_drift_file())
prev_drift_comp = drift_comp;
}
void
stats_config(
int item,
const char *invalue
)
{
FILE *fp;
const char *value;
int len;
#ifdef SYS_WINNT
char newvalue[MAX_PATH], parameter[MAX_PATH];
if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
switch(item) {
case STATS_FREQ_FILE:
strcpy(parameter,"STATS_FREQ_FILE");
break;
case STATS_STATSDIR:
strcpy(parameter,"STATS_STATSDIR");
break;
case STATS_PID_FILE:
strcpy(parameter,"STATS_PID_FILE");
break;
default:
strcpy(parameter,"UNKNOWN");
break;
}
value = invalue;
msyslog(LOG_ERR,
"ExpandEnvironmentStrings(%s) failed: %m\n", parameter);
} else {
value = newvalue;
}
#else
value = invalue;
#endif
switch(item) {
case STATS_FREQ_FILE:
if (stats_drift_file != 0) {
(void) free(stats_drift_file);
(void) free(stats_temp_file);
stats_drift_file = 0;
stats_temp_file = 0;
}
if (value == 0 || (len = strlen(value)) == 0)
break;
stats_drift_file = (char*)emalloc((u_int)(len + 1));
#if !defined(VMS)
stats_temp_file = (char*)emalloc((u_int)(len +
sizeof(".TEMP")));
#else
stats_temp_file = (char*)emalloc((u_int)(len +
sizeof("-TEMP")));
#endif
memmove(stats_drift_file, value, (unsigned)(len+1));
memmove(stats_temp_file, value, (unsigned)len);
#if !defined(VMS)
memmove(stats_temp_file + len, ".TEMP",
sizeof(".TEMP"));
#else
memmove(stats_temp_file + len, "-TEMP",
sizeof("-TEMP"));
#endif
if ((fp = fopen(stats_drift_file, "r")) == NULL) {
old_drift = 1e9;
break;
}
if (fscanf(fp, "%lf", &old_drift) != 1) {
msyslog(LOG_ERR, "Frequency format error in %s",
stats_drift_file);
old_drift = 1e9;
fclose(fp);
break;
}
fclose(fp);
drift_exists++;
prev_drift_comp = old_drift / 1e6;
msyslog(LOG_INFO,
"frequency initialized %.3f PPM from %s",
old_drift, stats_drift_file);
break;
case STATS_STATSDIR:
if (strlen(value) >= sizeof(statsdir)) {
msyslog(LOG_ERR,
"value for statsdir too long (>%d, sigh)",
(int)sizeof(statsdir)-1);
} else {
l_fp now;
get_systime(&now);
strcpy(statsdir,value);
if(peerstats.prefix == &statsdir[0] &&
peerstats.fp != NULL) {
fclose(peerstats.fp);
peerstats.fp = NULL;
filegen_setup(&peerstats, now.l_ui);
}
if(loopstats.prefix == &statsdir[0] &&
loopstats.fp != NULL) {
fclose(loopstats.fp);
loopstats.fp = NULL;
filegen_setup(&loopstats, now.l_ui);
}
if(clockstats.prefix == &statsdir[0] &&
clockstats.fp != NULL) {
fclose(clockstats.fp);
clockstats.fp = NULL;
filegen_setup(&clockstats, now.l_ui);
}
if(rawstats.prefix == &statsdir[0] &&
rawstats.fp != NULL) {
fclose(rawstats.fp);
rawstats.fp = NULL;
filegen_setup(&rawstats, now.l_ui);
}
if(sysstats.prefix == &statsdir[0] &&
sysstats.fp != NULL) {
fclose(sysstats.fp);
sysstats.fp = NULL;
filegen_setup(&sysstats, now.l_ui);
}
#ifdef OPENSSL
if(cryptostats.prefix == &statsdir[0] &&
cryptostats.fp != NULL) {
fclose(cryptostats.fp);
cryptostats.fp = NULL;
filegen_setup(&cryptostats, now.l_ui);
}
#endif
}
break;
case STATS_PID_FILE:
if ((fp = fopen(value, "w")) == NULL) {
msyslog(LOG_ERR, "Can't open %s: %m", value);
break;
}
fprintf(fp, "%d", (int) getpid());
fclose(fp);;
break;
default:
break;
}
}
void
record_peer_stats(
struct sockaddr_storage *addr,
int status,
double offset,
double delay,
double dispersion,
double skew
)
{
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&peerstats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (peerstats.fp != NULL) {
fprintf(peerstats.fp,
"%lu %s %s %x %.9f %.9f %.9f %.9f\n",
day, ulfptoa(&now, 3), stoa(addr), status, offset,
delay, dispersion, skew);
fflush(peerstats.fp);
}
}
void
record_loop_stats(
double offset,
double freq,
double jitter,
double stability,
int spoll
)
{
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&loopstats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (loopstats.fp != NULL) {
fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
stability * 1e6, spoll);
fflush(loopstats.fp);
}
}
void
record_clock_stats(
struct sockaddr_storage *addr,
const char *text
)
{
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&clockstats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (clockstats.fp != NULL) {
fprintf(clockstats.fp, "%lu %s %s %s\n",
day, ulfptoa(&now, 3), stoa(addr), text);
fflush(clockstats.fp);
}
}
void
record_raw_stats(
struct sockaddr_storage *srcadr,
struct sockaddr_storage *dstadr,
l_fp *t1,
l_fp *t2,
l_fp *t3,
l_fp *t4
)
{
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&rawstats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (rawstats.fp != NULL) {
fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n",
day, ulfptoa(&now, 3), stoa(srcadr), dstadr ? stoa(dstadr) : "-",
ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9),
ulfptoa(t4, 9));
fflush(rawstats.fp);
}
}
void
record_sys_stats(void)
{
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&sysstats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (sysstats.fp != NULL) {
fprintf(sysstats.fp,
"%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
day, ulfptoa(&now, 3), sys_stattime / 3600,
sys_received, sys_processed, sys_newversionpkt,
sys_oldversionpkt, sys_unknownversion,
sys_restricted, sys_badlength, sys_badauth,
sys_limitrejected);
fflush(sysstats.fp);
proto_clr_stats();
}
}
#ifdef OPENSSL
void
record_crypto_stats(
struct sockaddr_storage *addr,
const char *text
)
{
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&cryptostats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (cryptostats.fp != NULL) {
if (addr == NULL)
fprintf(cryptostats.fp, "%lu %s %s\n",
day, ulfptoa(&now, 3), text);
else
fprintf(cryptostats.fp, "%lu %s %s %s\n",
day, ulfptoa(&now, 3), stoa(addr), text);
fflush(cryptostats.fp);
}
}
#endif
#ifdef DEBUG_TIMING
void
record_timing_stats(
const char *text
)
{
static unsigned int flshcnt;
l_fp now;
u_long day;
if (!stats_control)
return;
get_systime(&now);
filegen_setup(&timingstats, now.l_ui);
day = now.l_ui / 86400 + MJD_1900;
now.l_ui %= 86400;
if (timingstats.fp != NULL) {
fprintf(timingstats.fp, "%lu %s %s\n",
day, lfptoa(&now, 3), text);
if (++flshcnt % 100 == 0)
fflush(timingstats.fp);
}
}
#endif
void
getauthkeys(
const char *keyfile
)
{
int len;
len = strlen(keyfile);
if (len == 0)
return;
if (key_file_name != 0) {
if (len > (int)strlen(key_file_name)) {
(void) free(key_file_name);
key_file_name = 0;
}
}
if (key_file_name == 0) {
#ifndef SYS_WINNT
key_file_name = (char*)emalloc((u_int) (len + 1));
#else
key_file_name = (char*)emalloc((u_int) (MAXPATHLEN));
#endif
}
#ifndef SYS_WINNT
memmove(key_file_name, keyfile, (unsigned)(len+1));
#else
if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN))
{
msyslog(LOG_ERR,
"ExpandEnvironmentStrings(KEY_FILE) failed: %m\n");
}
#endif
authreadkeys(key_file_name);
}
void
rereadkeys(void)
{
if (key_file_name != 0)
authreadkeys(key_file_name);
}
int
sock_hash(
struct sockaddr_storage *addr
)
{
int hashVal;
int i;
int len;
char *ch;
hashVal = 0;
len = 0;
ch = (char *)&addr->ss_family;
hashVal = 37 * hashVal + (int)*ch;
if (sizeof(addr->ss_family) > 1) {
ch++;
hashVal = 37 * hashVal + (int)*ch;
}
switch(addr->ss_family) {
case AF_INET:
ch = (char *)&((struct sockaddr_in *)addr)->sin_addr;
len = sizeof(struct in_addr);
break;
case AF_INET6:
ch = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr;
len = sizeof(struct in6_addr);
break;
}
for (i = 0; i < len ; i++)
hashVal = 37 * hashVal + (int)*(ch + i);
hashVal = hashVal % 128;
if (hashVal < 0)
hashVal += 128;
return hashVal;
}
#if notyet
void
ntp_exit(int retval)
{
msyslog(LOG_ERR, "EXITING with return code %d", retval);
exit(retval);
}
#endif
#define kMaxUUIDLen 37
int awoke(void)
{
static char sleepwakeuuid[kMaxUUIDLen];
int rc = 0;
io_service_t service = IORegistryEntryFromPath(kIOMasterPortDefault,
kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");
CFStringRef uuidString = IORegistryEntryCreateCFProperty(
service,
CFSTR(kIOPMSleepWakeUUIDKey),
kCFAllocatorDefault, 0);
if (uuidString) {
char newuuid[kMaxUUIDLen];
CFStringGetCString(uuidString, newuuid, sizeof(newuuid),
kCFStringEncodingUTF8);
rc = strncasecmp(newuuid, sleepwakeuuid, sizeof(newuuid));
if (rc) {
strlcpy(sleepwakeuuid, newuuid, sizeof(sleepwakeuuid));
}
CFRelease(uuidString);
}
IOObjectRelease(service);
return rc;
}