From: Dave Hart Date: Fri, 3 Apr 2009 03:49:05 +0000 (+0000) Subject: new Windows interpolation knocks out 1ms jitter in previous X-Git-Tag: NTP_4_2_5P162~3^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f201508bf575988e74a6181563f70bdebda0121a;p=thirdparty%2Fntp.git new Windows interpolation knocks out 1ms jitter in previous remove dead code thin #ifdef SYS_WINNT forest 64-bit time_t truncation fixes, warnings silenced do not set hardware clock on exit unless system is shutting down bk: 49d587313HSb-bCQSZOdBzqjB2vsdA --- diff --git a/ChangeLog b/ChangeLog index 91466ac2c..18405829c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +* [Bug 216] New interpolation scheme for Windows eliminates 1ms jitter +* remove a bunch of #ifdef SYS_WINNT from portable code +* 64-bit time_t cleanup for building on newer Windows compilers +* Only set CMOS clock during ntpd exit on Windows if the computer is + shutting down or restarting. (4.2.5p161) 2009/03/31 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.5p160) 2009/03/30 Released by Harlan Stenn diff --git a/include/ntp_machine.h b/include/ntp_machine.h index 1a28cd7d1..cfe525838 100644 --- a/include/ntp_machine.h +++ b/include/ntp_machine.h @@ -227,53 +227,11 @@ typedef unsigned long u_long; * make them macros for everyone else */ #ifndef SYS_WINNT -# define SOCKET int +typedef int SOCKET; # define INVALID_SOCKET -1 # define SOCKET_ERROR -1 -# define closesocket close +# define closesocket(fd) close(fd) #endif -/* - * Windows NT - */ -#if defined(SYS_WINNT) -# if !defined(HAVE_CONFIG_H) || !defined(__config) -# include -# endif /* HAVE_CONFIG_H) */ -# include -# include -# include - -# define ifreq _INTERFACE_INFO -# define ifr_flags iiFlags -# define ifr_addr iiAddress.AddressIn -# define ifr_broadaddr iiBroadcastAddress.AddressIn -# define ifr_mask iiNetmask.AddressIn -# define zz_family sin_family - -# define S_IFREG _S_IFREG -# define stat _stat -# define isascii __isascii -# define isatty _isatty -# define mktemp _mktemp -# define unlink _unlink -# define fileno _fileno -# define write _write -#ifndef close -# define close _close -#endif -# undef interface -# include -#define getpid _getpid -/* - * Defining registers are not a good idea on Windows - * This gets rid of the usage - */ -#ifndef register -# define register -#endif - typedef char *caddr_t; -# define vsnprintf _vsnprintf -#endif /* SYS_WINNT */ int ntp_set_tod (struct timeval *tvp, void *tzp); @@ -503,8 +461,7 @@ extern char *strdup(const char *); #if !defined(HAVE_ATT_NICE) \ && !defined(HAVE_BSD_NICE) \ - && !defined(HAVE_NO_NICE) \ - && !defined(SYS_WINNT) + && !defined(HAVE_NO_NICE) #include "ERROR: You must define one of the HAVE_xx_NICE defines!" #endif diff --git a/include/ntp_stdlib.h b/include/ntp_stdlib.h index 0c8834427..bfff4597a 100644 --- a/include/ntp_stdlib.h +++ b/include/ntp_stdlib.h @@ -65,7 +65,6 @@ extern u_int32 addr2refid (struct sockaddr_storage *); extern int atoint (const char *, long *); extern int atouint (const char *, u_long *); extern int hextoint (const char *, u_long *); -extern char * humandate (u_long); extern char * humanlogtime (void); extern char * inttoa (long); extern char * mfptoa (u_long, u_long, short); diff --git a/include/ntp_types.h b/include/ntp_types.h index 0f96027fe..580cf23aa 100644 --- a/include/ntp_types.h +++ b/include/ntp_types.h @@ -22,6 +22,13 @@ #undef mac #endif +/* + * used to quiet compiler warnings + */ +#ifndef UNUSED_ARG +#define UNUSED_ARG(arg) ((void)(arg)) +#endif + /* * VMS DECC (v4.1), {u_char,u_short,u_long} are only in SOCKET.H, * and u_int isn't defined anywhere @@ -65,5 +72,16 @@ typedef unsigned short associd_t; /* association ID */ typedef u_int32 keyid_t; /* cryptographic key ID */ typedef u_int32 tstamp_t; /* NTP seconds timestamp */ +/* + * On Unix struct sock_timeval is equivalent to struct timeval. + * On Windows built with 64-bit time_t, sock_timeval.tv_sec is a long + * as required by Windows' socket() interface timeout argument, while + * timeval.tv_sec is time_t for the more common use as a UTC time + * within NTP. + */ +#ifndef SYS_WINNT +#define sock_timeval timeval +#endif + #endif /* _NTP_TYPES_ */ diff --git a/include/ntp_unixtime.h b/include/ntp_unixtime.h index f5fe546d1..8c43f1cea 100644 --- a/include/ntp_unixtime.h +++ b/include/ntp_unixtime.h @@ -136,6 +136,3 @@ extern u_long msutotsfhi[]; #define MSUTOTSF(msu, tsf) \ (tsf) = msutotsfhi[((msu) >> 5) & 0x1f] + msutotsflo[(msu) & 0x1f] - -extern char * tvtoa (const struct timeval *); -extern char * utvtoa (const struct timeval *); diff --git a/libntp/Makefile.am b/libntp/Makefile.am index 9b266214b..5da1af1fd 100644 --- a/libntp/Makefile.am +++ b/libntp/Makefile.am @@ -13,8 +13,8 @@ libntp_a_SRCS = a_md5encrypt.c adjtime.c atoint.c atolfp.c atouint.c \ ntp_rfc2553.c numtoa.c numtohost.c octtoint.c prettydate.c \ ntp_random.c recvbuff.c refnumtoa.c snprintf.c socktoa.c socktohost.c \ statestr.c strdup.c strerror.c strstr.c \ - syssignal.c tsftomsu.c tstotv.c tvtoa.c tvtots.c \ - uglydate.c uinttoa.c utvtoa.c ymd2yd.c \ + syssignal.c tsftomsu.c tstotv.c tvtots.c \ + uglydate.c uinttoa.c ymd2yd.c \ $(srcdir)/../libisc/assertions.c $(srcdir)/../libisc/error.c \ $(srcdir)/../libisc/interfaceiter.c $(srcdir)/../libisc/lib.c \ $(srcdir)/../libisc/log.c $(srcdir)/../libisc/md5.c \ diff --git a/libntp/buftvtots.c b/libntp/buftvtots.c index bfceb35da..d6d9300f1 100644 --- a/libntp/buftvtots.c +++ b/libntp/buftvtots.c @@ -11,6 +11,7 @@ #include "ntp_string.h" #include "ntp_unixtime.h" +#ifndef SYS_WINNT int buftvtots( const char *bufp, @@ -33,3 +34,20 @@ buftvtots( TVUTOTSF(tv.tv_usec, ts->l_uf); return 1; } +#else /* SYS_WINNT */ +/* + * Windows doesn't have the tty_clock line discipline, so + * don't look for a timestamp where there is none. + */ +int +buftvtots( + const char *bufp, + l_fp *ts + ) +{ + UNUSED_ARG(bufp); + UNUSED_ARG(ts); + + return 0; +} +#endif /* SYS_WINNT */ diff --git a/libntp/humandate.c b/libntp/humandate.c index 630c87133..e3614cf20 100644 --- a/libntp/humandate.c +++ b/libntp/humandate.c @@ -7,36 +7,7 @@ #include "lib_strbuf.h" #include "ntp_stdlib.h" -static const char *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; -static const char *days[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - -char * -humandate( - u_long ntptime - ) -{ - char *bp; - struct tm *tm; - - tm = ntp2unix_tm(ntptime, 1); - - if (!tm) - return "--- --- -- ---- --:--:--"; - - LIB_GETBUF(bp); - - (void) sprintf(bp, "%s, %s %2d %4d %2d:%02d:%02d", - days[tm->tm_wday], months[tm->tm_mon], tm->tm_mday, - 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); - - return bp; -} - +extern const char *months[]; /* prettydate.c */ /* This is used in msyslog.c; we don't want to clutter up the log with the year and day of the week, etc.; just the minimal date and time. */ diff --git a/libntp/prettydate.c b/libntp/prettydate.c index 8ab1dbb04..77458f00b 100644 --- a/libntp/prettydate.c +++ b/libntp/prettydate.c @@ -9,7 +9,7 @@ #include "ntp_stdlib.h" #include "ntp_assert.h" -static const char *months[] = { +const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; @@ -150,14 +150,16 @@ ntp2unix_tm( return tm; } + char * -prettydate( - l_fp *ts +common_prettydate( + l_fp *ts, + int local ) { char *bp; struct tm *tm; - time_t sec; + u_long sec; u_long msec; LIB_GETBUF(bp); @@ -165,7 +167,7 @@ prettydate( sec = ts->l_ui; msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ - tm = ntp2unix_tm(sec, 1); + tm = ntp2unix_tm(sec, local); if (!tm) { (void) sprintf(bp, "%08lx.%08lx --- --- -- ---- --:--:--", (u_long)ts->l_ui, (u_long)ts->l_uf); @@ -180,32 +182,20 @@ prettydate( return bp; } + char * -gmprettydate( +prettydate( l_fp *ts ) { - char *bp; - struct tm *tm; - time_t sec; - u_long msec; + return common_prettydate(ts, 1); +} - LIB_GETBUF(bp); - - sec = ts->l_ui; - msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */ - tm = ntp2unix_tm(sec, 0); - if (!tm) { - (void) sprintf(bp, "%08lx.%08lx --- --- -- ---- --:--:--", - (u_long)ts->l_ui, (u_long)ts->l_uf); - } - else { - (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu", - (u_long)ts->l_ui, (u_long)ts->l_uf, days[tm->tm_wday], - months[tm->tm_mon], tm->tm_mday, 1900 + tm->tm_year, - tm->tm_hour,tm->tm_min, tm->tm_sec, msec); - } - - return bp; +char * +gmprettydate( + l_fp *ts + ) +{ + return common_prettydate(ts, 0); } diff --git a/libntp/systime.c b/libntp/systime.c index c1ddd3d8c..0dfe3eeed 100644 --- a/libntp/systime.c +++ b/libntp/systime.c @@ -76,7 +76,7 @@ get_systime( # else getclock(TIMEOFDAY, &ts); # endif - now->l_i = ts.tv_sec + JAN_1970; + now->l_i = (int32)ts.tv_sec + JAN_1970; dtemp = ts.tv_nsec + (ntp_random() * 2. / FRAC) * sys_tick * 1e6; dtemp = dtemp / 1e9 + sys_residual; diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c index 5d448f2bc..105e6a034 100644 --- a/ntpd/ntp_config.c +++ b/ntpd/ntp_config.c @@ -953,7 +953,7 @@ config_other_modes(void) dequeue(my_config.manycastserver); memset((char *)&addr_sock, 0, sizeof(addr_sock)); - addr_sock.ss_family = addr_node->type; + addr_sock.ss_family = (u_short)addr_node->type; if (getnetnum(addr_node->address, &addr_sock, 1, t_UNK) == 1) proto_config(PROTO_MULTICAST_ADD, 0, 0., &addr_sock); @@ -971,7 +971,7 @@ config_other_modes(void) dequeue(my_config.multicastclient); memset((char *)&addr_sock, 0, sizeof(addr_sock)); - addr_sock.ss_family = addr_node->type; + addr_sock.ss_family = (u_short)addr_node->type; if (getnetnum(addr_node->address, &addr_sock, 1, t_UNK) == 1) proto_config(PROTO_MULTICAST_ADD, 0, 0., &addr_sock); @@ -1226,7 +1226,7 @@ config_access(void) /* Check if the user specified a default rule */ if (my_node->addr) { /* Resolve the specified address */ - addr_sock.ss_family = my_node->addr->type; + addr_sock.ss_family = (u_short)my_node->addr->type; if (getnetnum(my_node->addr->address, &addr_sock, 1,t_UNK) != 1) { @@ -1247,7 +1247,7 @@ config_access(void) /* Resolve the mask */ if (my_node->mask) { memset((char *)&addr_mask, 0, sizeof(addr_mask)); - addr_mask.ss_family = my_node->mask->type; + addr_mask.ss_family = (u_short)my_node->mask->type; if (getnetnum(my_node->mask->address, &addr_mask, 1, t_MSK) != 1) { /* Error in mask !!! * Free the node memory and move onto the next @@ -1476,7 +1476,7 @@ config_trap(void) /* Resolve the interface address */ memset((char *)&addr_sock, 0, sizeof(addr_sock)); - addr_sock.ss_family = addr_node->type; + addr_sock.ss_family = (u_short)addr_node->type; if (getnetnum(addr_node->address, &addr_sock, 1, t_UNK) != 1) { @@ -1817,7 +1817,7 @@ config_peers(void) /* Attempt to resolve the address */ memset((char *)&peeraddr, 0, sizeof(peeraddr)); - peeraddr.ss_family = curr_peer->addr->type; + peeraddr.ss_family = (u_short)curr_peer->addr->type; status = get_multiple_netnums(curr_peer->addr->address, &peeraddr, &res, 0, t_UNK); diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index 57f2572de..43211b331 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -1934,7 +1934,7 @@ asn2ntp ( tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; - return (timegm(&tm) + JAN_1970); + return ((u_long)timegm(&tm) + JAN_1970); } diff --git a/ntpd/ntp_intres.c b/ntpd/ntp_intres.c index 2bba8caaf..7d2e96df7 100644 --- a/ntpd/ntp_intres.c +++ b/ntpd/ntp_intres.c @@ -651,7 +651,7 @@ request( ) { fd_set fdset; - struct timeval tvout; + struct sock_timeval tvout; struct req_pkt reqpkt; l_fp ts; int n; diff --git a/ntpd/ntp_timer.c b/ntpd/ntp_timer.c index be3bfb051..a384746bb 100644 --- a/ntpd/ntp_timer.c +++ b/ntpd/ntp_timer.c @@ -150,11 +150,6 @@ reinit_timer(void) void init_timer(void) { -# if defined SYS_WINNT & !defined(SYS_CYGWIN32) - HANDLE hToken = INVALID_HANDLE_VALUE; - TOKEN_PRIVILEGES tkp; -# endif /* SYS_WINNT */ - /* * Initialize... */ @@ -209,28 +204,6 @@ init_timer(void) sys$setimr(0, &vmstimer, alarming, alarming, 0); # endif /* VMS */ #else /* SYS_WINNT */ - _tzset(); - - /* - * Get privileges needed for fiddling with the clock - */ - - /* get the current process token handle */ - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { - msyslog(LOG_ERR, "OpenProcessToken failed: %m"); - exit(1); - } - /* get the LUID for system-time privilege. */ - LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid); - tkp.PrivilegeCount = 1; /* one privilege to set */ - tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - /* get set-time privilege for this process. */ - AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0); - /* cannot test return value of AdjustTokenPrivileges. */ - if (GetLastError() != ERROR_SUCCESS) { - msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m"); - } - /* * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds * Under Windows/NT, diff --git a/ntpd/ntpd.c b/ntpd/ntpd.c index 9c08c3b02..d37448766 100644 --- a/ntpd/ntpd.c +++ b/ntpd/ntpd.c @@ -380,10 +380,6 @@ set_process_priority(void) priority_done); #endif /* DEBUG */ -#ifdef SYS_WINNT - priority_done += NT_set_process_priority(); -#endif - #if defined(HAVE_SCHED_SETSCHEDULER) if (!priority_done) { extern int config_priority_override, config_priority; diff --git a/ntpd/refclock_dumbclock.c b/ntpd/refclock_dumbclock.c index a8d5e8d9a..e0ab669ed 100644 --- a/ntpd/refclock_dumbclock.c +++ b/ntpd/refclock_dumbclock.c @@ -249,7 +249,7 @@ dumbclock_receive( struct tm asserted_tm; /* the struct tm of the same */ int adjyear; int adjmon; - int reality_delta; + time_t reality_delta; time_t now; diff --git a/ntpdate/ntpdate.c b/ntpdate/ntpdate.c index 734c06b0c..8307b13b8 100644 --- a/ntpdate/ntpdate.c +++ b/ntpdate/ntpdate.c @@ -65,7 +65,7 @@ struct timeval timeout = {0,0}; * Windows does not abort a select select call if SIGALRM goes off * so a 200 ms timeout is needed */ -struct timeval timeout = {0,1000000/TIMER_HZ}; +struct sock_timeval timeout = {0,1000000/TIMER_HZ}; #else struct timeval timeout = {60,0}; #endif @@ -239,7 +239,6 @@ static void printserver (struct server *, FILE *); int on = 1; WORD wVersionRequested; WSADATA wsaData; -HANDLE TimerThreadHandle = NULL; #endif /* SYS_WINNT */ #ifdef NO_MAIN_ALLOWED @@ -463,12 +462,7 @@ ntpdatemain ( if (debug || simple_query) { #ifdef HAVE_SETVBUF static char buf[BUFSIZ]; -#ifdef SYS_WINNT - /* Win32 does not implement line buffering */ - setvbuf(stdout, NULL, _IONBF, BUFSIZ); -#else setvbuf(stdout, buf, _IOLBF, BUFSIZ); -#endif /* SYS_WINNT */ #else setlinebuf(stdout); #endif @@ -561,13 +555,6 @@ ntpdatemain ( #if defined(HAVE_BSD_NICE) (void) setpriority(PRIO_PROCESS, 0, NTPDATE_PRIO); #endif -#ifdef SYS_WINNT - process_handle = GetCurrentProcess(); - if (!SetPriorityClass(process_handle, (DWORD) REALTIME_PRIORITY_CLASS)) { - msyslog(LOG_ERR, "SetPriorityClass failed: %m"); - } -#endif /* SYS_WINNT */ - initializing = 0; @@ -1530,12 +1517,12 @@ alarming( void CALLBACK alarming(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) { + UNUSED_ARG(uTimerID); UNUSED_ARG(uMsg); UNUSED_ARG(dwUser); + UNUSED_ARG(dw1); UNUSED_ARG(dw2); + alarm_flag++; } -#endif /* SYS_WINNT */ - -#ifdef SYS_WINNT static void callTimeEndPeriod(void) { @@ -1910,7 +1897,7 @@ input_handler(void) { register int n; register struct recvbuf *rb; - struct timeval tvzero; + struct sock_timeval tvzero; int fromlen; l_fp ts; int i; diff --git a/ntpdc/ntpdc.c b/ntpdc/ntpdc.c index 0e7b395ec..8034b1e40 100644 --- a/ntpdc/ntpdc.c +++ b/ntpdc/ntpdc.c @@ -177,8 +177,8 @@ static struct xcmd builtins[] = { /* * Some variables used and manipulated locally */ -static struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ -static struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */ +static struct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ +static struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */ static l_fp delay_time; /* delay time */ static char currenthost[LENHOSTNAME]; /* current host name */ int showhostnames = 1; /* show host names by default */ @@ -189,18 +189,6 @@ static SOCKET sockfd; /* fd socket is opened on */ static int havehost = 0; /* set to 1 when host open */ int s_port = 0; -#if defined (SYS_WINNT) || defined (SYS_VXWORKS) -char password[9]; -#endif /* SYS_WINNT || SYS_VXWORKS */ - -#ifdef SYS_WINNT -DWORD NumberOfBytesWritten; - -HANDLE TimerThreadHandle = NULL; /* 1998/06/03 - Used in ntplib/machines.c */ -void timer(void) { ; }; /* 1998/06/03 - Used in ntplib/machines.c */ - -#endif /* SYS_WINNT */ - /* * Holds data returned from queries. We allocate INITDATASIZE * octets to begin with, increasing this as we need to. @@ -684,7 +672,7 @@ getresponse( ) { struct resp_pkt rpkt; - struct timeval tvo; + struct sock_timeval tvo; int items; int i; int size; @@ -1015,7 +1003,7 @@ doquery( int res; char junk[512]; fd_set fds; - struct timeval tvzero; + struct sock_timeval tvzero; /* * Check to make sure host is open diff --git a/ntpq/ntpq.c b/ntpq/ntpq.c index 54d265f5e..7a62e8d29 100644 --- a/ntpq/ntpq.c +++ b/ntpq/ntpq.c @@ -383,8 +383,8 @@ struct xcmd builtins[] = { /* * Some variables used and manipulated locally */ -struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ -struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */ +struct sock_timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ +struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */ l_fp delay_time; /* delay time */ char currenthost[LENHOSTNAME]; /* current host name */ struct sockaddr_in hostaddr = { 0 }; /* host address */ @@ -846,7 +846,7 @@ getresponse( ) { struct ntp_control rpkt; - struct timeval tvo; + struct sock_timeval tvo; u_short offsets[MAXFRAGS+1]; u_short counts[MAXFRAGS+1]; u_short offset; @@ -2218,11 +2218,11 @@ timeout( int val; if (pcmd->nargs == 0) { - val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000; + val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000; (void) fprintf(fp, "primary timeout %d ms\n", val); } else { tvout.tv_sec = pcmd->argval[0].uval / 1000; - tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000)) + tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000)) * 1000; } } diff --git a/ports/winnt/include/clockstuff.h b/ports/winnt/include/clockstuff.h index d57866488..64f3a8088 100644 --- a/ports/winnt/include/clockstuff.h +++ b/ports/winnt/include/clockstuff.h @@ -7,14 +7,8 @@ #include "ntp_syslog.h" -/* Windows NT versions of gettimeofday and settimeofday - * - * ftime() has internal DayLightSavings related BUGS - * therefore switched to GetSystemTimeAsFileTime() - */ void init_winnt_time(void); -void shutdown_winnt_time(void); void reset_winnt_time(void); /* 100ns intervals between 1/1/1601 and 1/1/1970 as reported by @@ -22,7 +16,7 @@ void reset_winnt_time(void); */ #define FILETIME_1970 0x019db1ded53e8000 -#define HECTONANOSECONDS 10000000ui64 +#define HECTONANOSECONDS 10000000 /* * Multimedia Timer @@ -34,4 +28,35 @@ enum { MM_TIMER_LORES, MM_TIMER_HIRES }; + +/* + * Optional callback from libntp ntp_set_tod() to Windows ntpd code + * in nt_clockstuff that needs to know. Optional because other + * libntp clients like ntpdate don't use it. + */ + +typedef void (*time_stepped_callback)(void); +extern time_stepped_callback step_callback; + +/* + * get_sys_time_as_filetime is a function pointer to + * either GetSystemTimeAsFileTime provided by Windows + * or ntpd's interpolating replacement. + */ + +typedef void (WINAPI *PGSTAFT)(LPFILETIME pft); +extern PGSTAFT get_sys_time_as_filetime; + +void lock_thread_to_processor(HANDLE); + +#ifdef HAVE_PPSAPI +/* + * ntp_timestamp_from_counter provides an interface for + * serialpps.sys counterstamps to be converted to + * interpolated timestamps. + */ + +extern void ntp_timestamp_from_counter(l_fp *, ULONGLONG, ULONGLONG); +#endif + #endif diff --git a/ports/winnt/include/config.h b/ports/winnt/include/config.h index 749e0b0eb..69e7c4795 100644 --- a/ports/winnt/include/config.h +++ b/ports/winnt/include/config.h @@ -54,11 +54,25 @@ /* #define _WSPIAPI_H_ */ /* need these wrappers for ntpd.exe to load on w2k */ #endif +/* + * On Unix struct sock_timeval is equivalent to struct timeval. + * On Windows built with 64-bit time_t, sock_timeval.tv_sec is a long + * as required by Windows' socket() interface timeout argument, while + * timeval.tv_sec is time_t for the more common use as a UTC time + * within NTP. + * + * winsock.h unconditionally defines struct timeval with long tv_sec + * instead of time_t tv_sec. We redirect its declaration to struct + * sock_timeval instead of struct timeval with a #define. + */ +#define timeval sock_timeval + /* Include Windows headers */ #include #include #include + /* * Some definitions we are using are missing in the headers * shipping with VC6. However, if the SDK is installed then the @@ -81,6 +95,12 @@ * needed before including headers. */ +#undef timeval /* see sock_timeval #define and comment above */ +struct timeval { + time_t tv_sec; + long tv_usec; +}; + /* * IPv6 requirements */ @@ -148,8 +168,8 @@ typedef int socklen_t; */ #define FORCE_DNSRETRY 1 -#define OPEN_BCAST_SOCKET 1 /* for ntp_io.c */ -#define TYPEOF_IP_MULTICAST_LOOP BOOL +#define OPEN_BCAST_SOCKET 1 /* for ntp_io.c */ +#define TYPEOF_IP_MULTICAST_LOOP BOOL #define SETSOCKOPT_ARG_CAST (const char *) #define HAVE_RANDOM #define MAXHOSTNAMELEN 64 @@ -217,8 +237,6 @@ typedef __int32 int32_t; /* define a typedef for int32_t */ # define strerror NTstrerror char *NTstrerror(int errnum); -int NT_set_process_priority(void); /* Define this function */ - # define MCAST /* Enable Multicast Support */ # define MULTICAST_NONEWSOCKET /* Don't create a new socket for mcast address */ @@ -243,7 +261,7 @@ int NT_set_process_priority(void); /* Define this function */ # define NTP_POSIX_SOURCE # define SYSLOG_FILE /* from libntp.mak */ -# define SYSV_TIMEOFDAY /* for ntp_unixtime.h */ +# define HAVE_GETCLOCK # define SIZEOF_SIGNED_CHAR 1 # define SIZEOF_INT 4 /* for ntp_types.h */ @@ -270,6 +288,10 @@ int NT_set_process_priority(void); /* Define this function */ #define HAVE_STRDUP 1 #define HAVE_STRCHR 1 #define HAVE_FCNTL_H 1 +#define HAVE_SYS_RESOURCE_H +#define HAVE_BSD_NICE /* emulate BSD setpriority() */ + +typedef char *caddr_t; #ifndef _INTPTR_T_DEFINED typedef long intptr_t; diff --git a/ports/winnt/include/ntservice.h b/ports/winnt/include/ntservice.h index 550300cc6..c5e5f42c3 100644 --- a/ports/winnt/include/ntservice.h +++ b/ports/winnt/include/ntservice.h @@ -27,9 +27,10 @@ void ntservice_init(); void UpdateSCM(DWORD); -void ServiceControl(DWORD dwCtrlCode); +void WINAPI ServiceControl(DWORD dwCtrlCode); void ntservice_shutdown(); BOOL ntservice_isservice(); +BOOL ntservice_systemisshuttingdown(); BOOL WINAPI OnConsoleEvent(DWORD dwCtrlType); #endif \ No newline at end of file diff --git a/ports/winnt/include/sys/resource.h b/ports/winnt/include/sys/resource.h index 5e1599737..01f4c810f 100644 --- a/ports/winnt/include/sys/resource.h +++ b/ports/winnt/include/sys/resource.h @@ -1,4 +1,10 @@ -/************************************************************** - * Dummy Header for Unix to Windows NT portability - * Created for NTP package - **************************************************************/ +/* + * ports/winnt/include/sys/resource.h + * + * routines declared in Unix systems' sys/resource.h + */ + +#define PRIO_PROCESS 0 +#define NTP_PRIO (-12) + +int setpriority(int, int, int); /* winnt\libntp\setpriority.c */ diff --git a/ports/winnt/include/sys/time.h b/ports/winnt/include/sys/time.h index e27cce692..d8f734c76 100644 --- a/ports/winnt/include/sys/time.h +++ b/ports/winnt/include/sys/time.h @@ -1,7 +1,8 @@ -/************************************************************** - * Dummy Header for Unix to Windows NT portability - * Created for NTP package - **************************************************************/ +/* + * ports/winnt/include/sys/time.h + * + * routines declared in Unix systems' sys/time.h + */ #ifndef SYS_TIME_H #define SYS_TIME_H @@ -12,7 +13,14 @@ #include #include -extern int gettimeofday (struct timeval *); -extern int settimeofday (struct timeval *); +typedef struct timespec { + time_t tv_sec; + long tv_nsec; +} timespec_t; + +#define TIMEOFDAY 0 /* getclock() clktyp arg */ +extern int getclock(int, struct timespec *ts); +extern int gettimeofday(struct timeval *, int); +extern int settimeofday(struct timeval *); #endif /* SYS_TIME_H */ diff --git a/ports/winnt/libntp/SetSystemTime.c b/ports/winnt/libntp/SetSystemTime.c index 1465719ce..b4c2288bb 100644 --- a/ports/winnt/libntp/SetSystemTime.c +++ b/ports/winnt/libntp/SetSystemTime.c @@ -2,7 +2,9 @@ #include "clockstuff.h" #include "ntp_stdlib.h" -const char * set_tod_using = "SetSystemTime"; +const char *set_tod_using = "SetSystemTime"; + +time_stepped_callback step_callback = NULL; int ntp_set_tod( @@ -11,24 +13,24 @@ ntp_set_tod( ) { SYSTEMTIME st; - struct tm *gmtm; - const time_t x = tv->tv_sec; - long y = tv->tv_usec; - (void) tzp; - - gmtm = gmtime(&x); - st.wSecond = (WORD) gmtm->tm_sec; - st.wMinute = (WORD) gmtm->tm_min; - st.wHour = (WORD) gmtm->tm_hour; - st.wDay = (WORD) gmtm->tm_mday; - st.wMonth = (WORD) (gmtm->tm_mon + 1); - st.wYear = (WORD) (gmtm->tm_year + 1900); - st.wDayOfWeek = (WORD) gmtm->tm_wday; - st.wMilliseconds = (WORD) (y / 1000); - - if (!SetSystemTime(&st)) { + union { + FILETIME ft; + ULONGLONG ull; + } t; + + UNUSED_ARG(tzp); + + t.ull = FILETIME_1970 + + (ULONGLONG)tv->tv_sec * 10 * 1000 * 1000 + + (ULONGLONG)tv->tv_usec * 10; + + if (!FileTimeToSystemTime(&t.ft, &st) || !SetSystemTime(&st)) { msyslog(LOG_ERR, "SetSystemTime failed: %m\n"); return -1; } + + if (step_callback) + (*step_callback)(); + return 0; } diff --git a/ports/winnt/libntp/getclock.c b/ports/winnt/libntp/getclock.c new file mode 100644 index 000000000..a53a5c94e --- /dev/null +++ b/ports/winnt/libntp/getclock.c @@ -0,0 +1,49 @@ +/* + * getclock.c - Emulate Unix getclock(3) nanosecond interface for libntp/ntpd + */ +#include "config.h" +#include "clockstuff.h" +#include "ntp_stdlib.h" + +/* + * getclock() is in libntp. To use interpolation, + * ports/winnt/ntpd/nt_clockstuff.c overrides GetSystemTimeAsFileTime + * via the pointer get_sys_time_as_filetime. + */ +PGSTAFT get_sys_time_as_filetime; + +int +getclock( + int clktyp, + struct timespec *ts + ) +{ + union { + FILETIME ft; + ULONGLONG ull; + } uNow; + + if (clktyp != TIMEOFDAY) { +#ifdef DEBUG + if (debug) { + printf("getclock() supports only TIMEOFDAY clktyp\n"); + } +#endif + errno = EINVAL; + return -1; + } + + if (! get_sys_time_as_filetime) + get_sys_time_as_filetime = GetSystemTimeAsFileTime; + + (*get_sys_time_as_filetime)(&uNow.ft); + + /* + * Convert the hecto-nano second time to timespec format + */ + uNow.ull -= FILETIME_1970; + ts->tv_sec = (time_t)( uNow.ull / HECTONANOSECONDS); + ts->tv_nsec = (long)(( uNow.ull % HECTONANOSECONDS) * 100); + + return 0; +} diff --git a/ports/winnt/libntp/libntp.dsp b/ports/winnt/libntp/libntp.dsp index 78b2a6ca3..4e29403f6 100644 --- a/ports/winnt/libntp/libntp.dsp +++ b/ports/winnt/libntp/libntp.dsp @@ -86,6 +86,14 @@ LIB32=link.exe -lib # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File +SOURCE=.\getclock.c +# End Source File +# Begin Source File + +SOURCE=.\setpriority.c +# End Source File +# Begin Source File + SOURCE=..\..\..\libntp\a_md5encrypt.c # End Source File # Begin Source File diff --git a/ports/winnt/libntp/libntp.vcproj b/ports/winnt/libntp/libntp.vcproj index 322db117e..85f5983d1 100644 --- a/ports/winnt/libntp/libntp.vcproj +++ b/ports/winnt/libntp/libntp.vcproj @@ -711,6 +711,10 @@ /> + + @@ -780,24 +784,6 @@ - - - - - - + + @@ -1817,28 +1807,6 @@ /> - - - - - - - - @@ -1906,26 +1874,8 @@ - - - - - - + + diff --git a/ports/winnt/libntp/randfile.c b/ports/winnt/libntp/randfile.c index 80689174d..75a184b05 100644 --- a/ports/winnt/libntp/randfile.c +++ b/ports/winnt/libntp/randfile.c @@ -44,8 +44,9 @@ init_randfile() * a .rnd file is in there. */ homedir = getenv("HOME"); - if (homedir != NULL) { - strcpy(tmp, homedir); + if (homedir != NULL && + (strlen(homedir) + 5 /* \.rnd */) < sizeof(tmp)) { + strncpy(tmp, homedir, sizeof(tmp)); strcat(tmp, "\\.rnd"); rf = fopen(tmp, "rb"); if (rf != NULL) { diff --git a/ports/winnt/libntp/setpriority.c b/ports/winnt/libntp/setpriority.c new file mode 100644 index 000000000..31cfc8a68 --- /dev/null +++ b/ports/winnt/libntp/setpriority.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include /* our private version */ +#include "ntp_machine.h" +#include "ntp_stdlib.h" +#include "ntp_syslog.h" +#include "ntp_debug.h" +#include "ntp_fp.h" +#include "ntp.h" +#include "clockstuff.h" + + +/* + * setpriority + * + * to reduce the #ifdef forest in the portable code, + * we emulate the BSD setpriority interface: + * + * if (-1 == setpriority(PRIO_PROCESS, 0, NTP_PRIO)) + * msyslog(LOG_ERR, "setpriority() error: %m"); + * + * However, since the Windows port of ntpd has always raised its + * priority (to realtime if allowed, or silently downgraded to + * high by the system if not) with or without -N. Changing that + * now would endanger users who upgrade the binary without adding + * -N to its invocation. Instsrv assumes ntpd.exe is installed + * with no command-line arguments. + * + * This routine is used by utilities as well as ntpd itself, so + * it checks if the priority is already high or realtime and + * logs no complaints in that case, to avoid duplicating. ntpd + * will have raised the priority to one of those in + * init_winnt_time, while the utilities will rely on this + * code. + * + */ + +int setpriority( + int which, + int who, + int prio + ) +{ + BOOL success; + DWORD prio_class; + + if (PRIO_PROCESS != which || who || NTP_PRIO != prio) { + DPRINTF(1,("windows setpriority() clone needs work.\n")); + } + + prio_class = GetPriorityClass(GetCurrentProcess()); + + if (HIGH_PRIORITY_CLASS == prio_class || + REALTIME_PRIORITY_CLASS == prio_class) + return 0; + + success = SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); + + if (!success) { + msyslog(LOG_ERR, "Unable to raise priority: %m"); + errno = EPERM; + return -1; + } + + prio_class = GetPriorityClass(GetCurrentProcess()); + + if (REALTIME_PRIORITY_CLASS == prio_class) + msyslog(LOG_INFO, "Raised to realtime priority class"); + else if (HIGH_PRIORITY_CLASS == prio_class) + msyslog(LOG_ERR, "Raised to high priority class, realtime " + "requires Increase Scheduling Priority " + "privilege (enabled with secpol.msc)."); + else + msyslog(LOG_ERR, "Unexpected process priority class %d", + prio_class); + + return 0; +} diff --git a/ports/winnt/libntp/syslog.c b/ports/winnt/libntp/syslog.c index ad1ff93df..ad492324f 100644 --- a/ports/winnt/libntp/syslog.c +++ b/ports/winnt/libntp/syslog.c @@ -131,7 +131,8 @@ void openlog(const char *name, int flags, ...) { /* Get a handle to the Application event log */ hAppLog = RegisterEventSource(NULL, progname); - strcpy(progname, name); + strncpy(progname, name, sizeof(progname)); + progname[sizeof(progname) - 1] = 0; } /* diff --git a/ports/winnt/libntp/util_clockstuff.c b/ports/winnt/libntp/util_clockstuff.c index b6c593ae1..3b1dc39c8 100644 --- a/ports/winnt/libntp/util_clockstuff.c +++ b/ports/winnt/libntp/util_clockstuff.c @@ -2,32 +2,24 @@ #include "config.h" #endif +#include "ntp_syslog.h" +#include "ntp_stdlib.h" #include "clockstuff.h" -DWORD units_per_tick = 0; -DOUBLE ppm_per_adjust_unit = 0.0; /* to satisfy libntp */ - int gettimeofday( - struct timeval *tv + struct timeval *tv, + int ignored ) { - /* Use the system time (roughly synchronised to the tick, and - * extrapolated using the system performance counter. - */ + struct timespec ts; - FILETIME StartTime; - ULONGLONG Time; + UNUSED_ARG(ignored); - GetSystemTimeAsFileTime(&StartTime); - Time = (((ULONGLONG) StartTime.dwHighDateTime) << 32) + - (ULONGLONG) StartTime.dwLowDateTime; + getclock(TIMEOFDAY, &ts); - /* Convert the hecto-nano second time to tv format - */ - Time -= FILETIME_1970; - tv->tv_sec = (LONG) ( Time / 10000000ui64); - tv->tv_usec = (LONG) (( Time % 10000000ui64) / 10); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 10; return 0; } diff --git a/ports/winnt/ntp-keygen/ntp-keygen.vcproj b/ports/winnt/ntp-keygen/ntp-keygen.vcproj index 892d13a7c..6dc6147c4 100644 --- a/ports/winnt/ntp-keygen/ntp-keygen.vcproj +++ b/ports/winnt/ntp-keygen/ntp-keygen.vcproj @@ -332,28 +332,6 @@ /> - - - - - - - - diff --git a/ports/winnt/ntp-keygen/ntpkeygen.vcproj b/ports/winnt/ntp-keygen/ntpkeygen.vcproj deleted file mode 100644 index 892d13a7c..000000000 --- a/ports/winnt/ntp-keygen/ntpkeygen.vcproj +++ /dev/null @@ -1,419 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/winnt/ntpd/nt_clockstuff.c b/ports/winnt/ntpd/nt_clockstuff.c index 74a40ce1f..d7fc53700 100644 --- a/ports/winnt/ntpd/nt_clockstuff.c +++ b/ports/winnt/ntpd/nt_clockstuff.c @@ -1,43 +1,10 @@ /* Windows NT Clock Routines - * - * - * Revision History: - * $Log$ - * Revision 1.9 2000/11/19 09:02:12 dietrich - * From: Ron Thornton [rthornto@pictel.com] - * Sent: Thu 11/16/00 8:51 AM - * On Windows 2000 it requires a privilege on the current process token - * that is disabled by default on Windows 2000. - * - * I set the token by adding the following code at the beginning of the - * init_winnt_time() function in nt_clockstuff.c. - * - * Revision 1.8 2000/11/19 08:03:20 dietrich - * From: "Colin Dancer" - * To: - * Sent: 10 November 2000 12:59 - * Subject: NT bug in NTP 4.0.99j - * - * I've found a bug in (and produced a fix for) the NT clock interpolation - * code in NTP 4.0.99j. - * - * The symptoms of the problem are that gettimeofday() function on NT - * can be wrong by hundreds of seconds if, while a gettimeofday() call - * is being processed, an APC completes after the query of the performance - * counter but before the lock is grabbed. The most obvious fix is to move - * the lock to include the querying of the performance counter, but this - * could affect the predictability of the timestamp so I have instead - * tweaked the code to detect and sidestep the duff calculation. - * - * I've also found that on a loaded system the execution of the APC can be - * delayed, leading to errors of upto 10ms. There is no easy fix to this, - * but I do have code for an alternative interpolation scheme which avoids - * the problem on single processor systems. I'm currently integrating this - * along with code for deciding which algorithm to use based on whether - * the system is SP or MP. * * Created by Sven Dietrich sven@inter-yacht.com * + * New interpolation scheme by Dave Hart February 2009 + * overcomes 500us-1ms inherent jitter with the older scheme, first identified + * by Peter Rosin (nee Ekberg) in 2003 [Bug 216]. */ @@ -45,32 +12,40 @@ #include "config.h" #endif +#include /* our private version */ + +#if defined(_MSC_VER) && _MSC_VER >= 1400 /* VS 2005 */ +#include /* for __rdtsc() */ +#endif + +#ifdef HAVE_PPSAPI +#include +#endif + #include "ntp_stdlib.h" +#include "ntp_unixtime.h" +#include "ntp_timer.h" #include "clockstuff.h" #include "ntservice.h" -#include "ntp_timer.h" #include "ntpd.h" +extern double sys_residual; /* residual from previous adjustment */ + /* * Include code to possibly modify the MM timer while the service is active. */ - /* - * Whether or not MM timer modifications takes place is still controlled - * by the variable below which is initialized by a default value but - * might be changed depending on a command line switch. - */ - int modify_mm_timer = MM_TIMER_LORES; - - #define MM_TIMER_INTV 1 /* the interval we'd want to set the MM timer to [ms] */ +/* + * Whether or not MM timer modifications takes place is still controlled + * by the variable below which is initialized by a default value but + * might be changed depending on a command line switch. + */ +static int modify_mm_timer = MM_TIMER_LORES; - static UINT wTimerRes; - static TIMECAPS tc; +#define MM_TIMER_INTV 1 /* the interval we'd want to set the MM timer to [ms] */ +static UINT wTimerRes; -extern double sys_residual; /* residual from previous adjustment */ - -char szMsgPath[255]; BOOL init_randfile(); static long last_Adj = 0; @@ -79,47 +54,256 @@ static long last_Adj = 0; #define LS_CORR_INTV ( (LONGLONG) HECTONANOSECONDS * LS_CORR_INTV_SECS ) #define LS_CORR_LIMIT ( (LONGLONG) HECTONANOSECONDS / 2 ) // half a second -typedef union -{ +typedef union ft_ull { FILETIME ft; ULONGLONG ull; + LONGLONG ll; + LARGE_INTEGER li; } FT_ULL; +/* leap second stuff */ static FT_ULL ls_ft; static DWORD ls_time_adjustment; -static LARGE_INTEGER ls_ref_perf_cnt; +static ULONGLONG ls_ref_perf_cnt; static LONGLONG ls_elapsed; +static BOOL winnt_time_initialized = FALSE; +static BOOL winnt_use_interpolation = FALSE; +static ULONGLONG last_interp_time; +static unsigned clock_thread_id; + + +void WINAPI GetInterpTimeAsFileTime(LPFILETIME pft); static void StartClockThread(void); -static void StopClockThread(void); -void lock_thread_to_processor(HANDLE); +static void tune_ctr_freq(LONGLONG, LONGLONG); +void StopClockThread(void); +void atexit_revert_mm_timer(void); +void time_stepped(void); +static HANDLE clock_thread = NULL; +static HANDLE TimerThreadExitRequest = NULL; -static CRITICAL_SECTION TimerCritialSection; /* lock for LastTimerCount & LastTimerTime */ +/* + * interp_time estimates time in 100ns units + * based on a performance counter value given. + * The 2nd parameter indicates if this is + * part of a current time-of-day calculation. + */ +ULONGLONG interp_time(ULONGLONG, BOOL); -static ULONGLONG RollOverCount = 0; -static ULONGLONG LastTimerCount = 0; -static ULONGLONG LastTimerTime = 0; +/* + * add_counter_time_pair is called by the + * high priority clock thread with a new + * sample. + */ +void add_counter_time_pair(ULONGLONG, LONGLONG); -static HANDLE ClockThreadHandle = NULL; -static HANDLE TimerThreadExitRequest = NULL; +/* + * globals used by the above two functions to + * implement the counter/time history + */ +#define BASELINES_TOT 256 +#define BASELINES_USED 64 + +static volatile int newest_baseline = 0; +static volatile int newest_baseline_gen = 0; +static ULONGLONG baseline_counts[BASELINES_TOT] = {0}; +static LONGLONG baseline_times[BASELINES_TOT] = {0}; -static DWORD every = 0; -static DWORD initial_units_per_tick = 0; -static DWORD lastLowTimer = 0; +static int clock_backward_count; +static ULONGLONG clock_backward_max; -ULONGLONG PerfFrequency = 0; -static DWORD units_per_tick = 0; + +/* + * clockperiod is the period used for SetSystemTimeAdjustment + * slewing calculations but does not necessarily correspond + * to the precision of the OS clock. Prior to Windows Vista + * (6.0) the two were identical. In 100ns units. + */ +static DWORD clockperiod; + +/* + * os_clock_precision is the observed precision of the OS + * clock, meaning the increment between discrete values. This + * is currently calculated once at startup. 100ns units. + */ +static ULONGLONG os_clock_precision; + +/* + * NomPerfCtrFreq is from QueryPerformanceFrequency and is the + * number of performance counter beats per second. PerfCtrFreq + * starts from NomPerfCtrFreq but is maintained using a sliding + * window average based on actual performance counter behavior, + * to allow us to better tolerate powersaving measures that + * alter the effective frequency of the processor cycle counter + * (TSC) which sometimes underlies QueryPerformanceCounter. + * + * Note that the OS is unlikely to be so subtle in its internal + * scheduling of waitable timers, presumably done using the + * performance counter. Therefore our calculations for + * interpolated time should be based on PerfCtrFreq but our + * calculations for SetWaitableTimer should assume the OS will + * convert from FILETIME 100ns units to performance counter + * beats using the nominal frequency. + */ + +volatile ULONGLONG PerfCtrFreq = 0; + ULONGLONG NomPerfCtrFreq = 0; + +/* + * If we're using RDTSC beating at the same rate as + * QueryPerformanceCounter, there is a systemic + * offset we need to account for when using + * counterstamps from serialpps.sys, which are + * always from QPC (actually KeQueryPerformanceCounter). + */ +static LONGLONG QPC_offset = 0; + +/* + * use_pcc could be a local static in perf_ctr but + * lock_thread_to_processor cares about it. + */ +static int use_pcc = -1; + +/* + * ppm_per_adjust_unit is parts per million effect on the OS + * clock per slewing adjustment unit per second. Per haps. + */ static DOUBLE ppm_per_adjust_unit = 0.0; +/* + * performance counter frequency observations + */ +#define TUNE_CTR_DEPTH 3 /* running avg depth */ + +static HANDLE ctr_freq_timer = INVALID_HANDLE_VALUE; +static ULONGLONG tune_ctr_freq_max_interval; +static unsigned tune_ctr_period; +void start_ctr_freq_timer(ULONGLONG now_time); +void reset_ctr_freq_timer(ULONGLONG when, ULONGLONG now); +void reset_ctr_freq_timer_abs(ULONGLONG when); + +/* round a Windows time to the next bottom of the second */ + +#define ROUND_TO_NEXT_SEC_BOTTOM(t) \ +do { \ + (t) += 3 * HECTONANOSECONDS / 2 - 1; \ + (t) /= HECTONANOSECONDS; \ + (t) *= HECTONANOSECONDS; \ + (t) -= HECTONANOSECONDS / 2; \ +} while (0) + +/* there must be a better place for this */ +#define COUNTOF(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* + * NT native time format is 100's of nanoseconds since 1601-01-01. + * Helpers for converting between "hectonanoseconds" and the + * performance counter scale from which interpolated time is + * derived. + */ +#define HNS2PERF(hns) ((hns) * PerfCtrFreq / HECTONANOSECONDS) +#define PERF2HNS(ctr) ((ctr) * HECTONANOSECONDS / PerfCtrFreq) + + +#if defined(_MSC_VER) && _MSC_VER >= 1400 /* VS 2005 */ +#define get_pcc() __rdtsc() +#else +/* + * something like this can be used for a compiler without __rdtsc() + */ +ULONGLONG __forceinline +get_pcc(void) +{ + /* RDTSC returns in EDX:EAX, same as C compiler */ + __asm { + RDTSC + } +} +#endif + +/* + * perf_ctr() returns the current performance counter value, + * from QueryPerformanceCounter or RDTSC. + */ + +ULONGLONG WINAPI +perf_ctr(void) +{ + FT_ULL ft1; + + if (1 == use_pcc) { + return get_pcc(); + } else if (0 == use_pcc) { + QueryPerformanceCounter(&ft1.li); + return ft1.ull; + } else { + FT_ULL ft2; + FT_ULL ft3; + FT_ULL ft4; + FT_ULL ft5; + LONGLONG offset; + char *ntpd_pcc_freq_text = getenv("NTPD_PCC_FREQ"); + + /* one-time initialization */ +#ifdef DEBUG + if (!NomPerfCtrFreq) { + msyslog(LOG_ERR, "NomPerfCtrFreq not initialized before first perf_ctr() call"); + exit(-1); + } +#endif + + QueryPerformanceCounter(&ft1.li); + ft2.ull = get_pcc(); + Sleep(1); + QueryPerformanceCounter(&ft3.li); + Sleep(1); + ft4.ull = get_pcc(); + Sleep(1); + QueryPerformanceCounter(&ft5.li); + + offset = ft2.ull - ft1.ull; + ft3.ull += offset; + ft5.ull += offset; + + if (ntpd_pcc_freq_text || + (ft2.ull <= ft3.ull && + ft3.ull <= ft4.ull && + ft4.ull <= ft5.ull)) { + + use_pcc = 1; + ft1.ull = ft2.ull; + QPC_offset = offset; + + if (ntpd_pcc_freq_text) + sscanf(ntpd_pcc_freq_text, + "%I64u", + &NomPerfCtrFreq); + + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_INFO, + "using processor cycle counter " + "%.3f MHz", + NomPerfCtrFreq / 1e6); + } else { + use_pcc = 0; + } + + return ft1.ull; + } +} + /* * Request Multimedia Timer */ void -set_mm_timer(int timerres) +set_mm_timer( + int timerres + ) { modify_mm_timer = timerres; } + /* * adj_systime - called once every second to make system time adjustments. * Returns 1 if okay, 0 if trouble. @@ -132,7 +316,7 @@ adj_systime( double dtemp; u_char isneg = 0; int rc; - long dwTimeAdjustment; + long TimeAdjustment; /* * Add the residual from the previous adjustment to the new @@ -154,36 +338,40 @@ adj_systime( if (isneg) dtemp = -dtemp; - /* dtemp is in micro seconds. NT uses 100 ns units, - * so a unit change in dwTimeAdjustment corresponds - * to slewing 10 ppm on a 100 Hz system. - * Calculate the number of 100ns units to add, - * using OS tick frequency as per suggestion from Harry Pyle, - * and leave the remainder in dtemp */ - dwTimeAdjustment = (DWORD)( dtemp / ppm_per_adjust_unit + (isneg ? -0.5 : 0.5)) ; - dtemp += (double) -dwTimeAdjustment * ppm_per_adjust_unit; + /* + * dtemp is in micro seconds. NT uses 100 ns units, + * so a unit change in TimeAdjustment corresponds + * to slewing 10 ppm on a 100 Hz system. Calculate + * the number of 100ns units to add, using OS tick + * frequency as per suggestion from Harry Pyle, + * and leave the remainder in dtemp + */ + TimeAdjustment = (long) (dtemp / ppm_per_adjust_unit + (isneg ? -0.5 : 0.5)); + dtemp -= (double) TimeAdjustment * ppm_per_adjust_unit; - /* If a leap second is pending then determine the UTC time stamp - * of when the insertion must take place */ - if (leap_sec > 0) + /* + * If a leap second is pending then determine the UTC time stamp + * of when the insertion must take place + */ + if (leap_sec > 0) { if ( ls_ft.ull == 0 ) /* time stamp has not yet been computed */ { - FT_ULL ft; SYSTEMTIME st; - int itmp; - GetSystemTimeAsFileTime(&ft.ft); - FileTimeToSystemTime(&ft.ft, &st); + GetSystemTime(&st); - /* Accept leap announcement only 1 month in advance, + /* + * Accept leap announcement only 1 month in advance, * for end of March, June, September, or December. */ if ( ( st.wMonth % 3 ) == 0 ) { - /* The comarison time stamp is computed according - * to 0:00h UTC of the following day */ + /* + * The comparison time stamp is computed according + * to 0:00h UTC of the following day + */ if ( ++st.wMonth > 12 ) { st.wMonth -= 12; @@ -197,22 +385,25 @@ adj_systime( st.wMilliseconds = 0; SystemTimeToFileTime(&st, &ls_ft.ft); - msyslog(LOG_INFO, "Detected positive leap second announcement " - "for %04d-%02d-%02d %02d:%02d:%02d UTC", - st.wYear, st.wMonth, st.wDay, - st.wHour, st.wMinute, st.wSecond); + msyslog(LOG_NOTICE, + "Detected positive leap second announcement " + "for %04d-%02d-%02d %02d:%02d:%02d UTC", + st.wYear, st.wMonth, st.wDay, + st.wHour, st.wMinute, st.wSecond); } } - } + } - /* If the time stamp for the next leap second has been set - * then check if the leap second must be handled */ + /* + * If the time stamp for the next leap second has been set + * then check if the leap second must be handled + */ if ( ls_ft.ull ) { - LARGE_INTEGER this_perf_count; + ULONGLONG this_perf_count; - QueryPerformanceCounter( &this_perf_count ); + this_perf_count = perf_ctr(); if ( ls_time_adjustment == 0 ) /* has not yet been scheduled */ { @@ -221,16 +412,16 @@ adj_systime( GetSystemTimeAsFileTime(&curr_ft.ft); if ( curr_ft.ull >= ls_ft.ull ) { - ls_time_adjustment = every / LS_CORR_INTV_SECS; + ls_time_adjustment = clockperiod / LS_CORR_INTV_SECS; ls_ref_perf_cnt = this_perf_count; ls_elapsed = 0; - msyslog(LOG_INFO, "Inserting positive leap second."); + msyslog(LOG_NOTICE, "Inserting positive leap second."); } } else /* leap sec adjustment has been scheduled previously */ { - ls_elapsed = ( this_perf_count.QuadPart - ls_ref_perf_cnt.QuadPart ) - * HECTONANOSECONDS / PerfFrequency; + ls_elapsed = ( this_perf_count - ls_ref_perf_cnt ) + * HECTONANOSECONDS / PerfCtrFreq; } if ( ls_time_adjustment ) /* leap second adjustment is currently active */ @@ -241,24 +432,21 @@ adj_systime( ls_ft.ull = 0; } - /* NOTE: While the system time is slewed during the leap second - * the interpolation function which is based on the performance - * counter does not account for the slew. - */ - dwTimeAdjustment -= ls_time_adjustment; + /* + * NOTE: While the system time is slewed during the leap second + * the interpolation function which is based on the performance + * counter does not account for the slew. + */ + TimeAdjustment -= ls_time_adjustment; } } /* only adjust the clock if adjustment changes */ - if (last_Adj != dwTimeAdjustment) { - last_Adj = dwTimeAdjustment; -# ifdef DEBUG - if (debug > 1) - printf("SetSystemTimeAdjustment( %ld) + (%ld)\n", dwTimeAdjustment, units_per_tick); -# endif - dwTimeAdjustment += units_per_tick; - rc = !SetSystemTimeAdjustment(dwTimeAdjustment, FALSE); + if (last_Adj != TimeAdjustment) { + last_Adj = TimeAdjustment; + DPRINTF(1, ("SetSystemTimeAdjustment(%+ld)\n", TimeAdjustment)); + rc = !SetSystemTimeAdjustment(clockperiod + TimeAdjustment, FALSE); } else rc = 0; if (rc) @@ -267,22 +455,31 @@ adj_systime( return 0; } else { - sys_residual = dtemp / 1000000.0; + sys_residual = dtemp / 1e6; } -#ifdef DEBUG - if (debug > 6) - printf("adj_systime: adj %.9f -> remaining residual %.9f\n", now, sys_residual); -#endif + DPRINTF(6, ("adj_systime: adj %.9f -> remaining residual %.9f\n", + now, sys_residual)); + return 1; } -void init_winnt_time(void) +void +init_winnt_time(void) { - BOOL noslew; + char szMsgPath[MAX_PATH+1]; HANDLE hToken = INVALID_HANDLE_VALUE; TOKEN_PRIVILEGES tkp; + TIMECAPS tc; + BOOL noslew; + DWORD adjclockperiod; + LARGE_INTEGER Freq; + FT_ULL initial_hectonanosecs; + FT_ULL next_hectonanosecs; + + if (winnt_time_initialized) + return; /* * Make sure the service is initialized @@ -290,6 +487,12 @@ void init_winnt_time(void) */ ntservice_init(); + /* Set up the Console Handler */ + if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE)) + { + msyslog(LOG_ERR, "Can't set console control handler: %m"); + } + /* Set the Event-ID message-file name. */ if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath))) { @@ -305,14 +508,14 @@ void init_winnt_time(void) * Get privileges needed for fiddling with the clock */ - /* get the current process token handle */ + /* get the current process token handle */ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { msyslog(LOG_ERR, "OpenProcessToken failed: %m"); exit(-1); } - /* get the LUID for system-time privilege. */ + /* get the LUID for system-time privilege. */ LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid); tkp.PrivilegeCount = 1; /* one privilege to set */ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; @@ -329,130 +532,202 @@ void init_winnt_time(void) /* later set time call will probably fail */ } - /* Reset the Clock to a reasonable increment */ - if (!GetSystemTimeAdjustment(&initial_units_per_tick, &every,&noslew)) + CloseHandle(hToken); + hToken = INVALID_HANDLE_VALUE; + + /* + * ntpd on Windows has always raised its priority, without + * requiring -N as on Unix. Since Windows ntpd doesn't share + * the history of unix ntpd of once having no -N and therefore + * needing to be invoked under nice, there is no reason to + * bring it in line with the Unix version in this regard. + * Instsrv assumes ntpd is invoked with no arguments, and + * upgrading users would be negatively surprised by the + * poor timekeeping if they failed to add -N as part of + * upgrading were we to correct this platform difference. + */ + if (-1 == setpriority(PRIO_PROCESS, 0, NTP_PRIO)) + exit(-1); + + /* + * register with libntp ntp_set_tod() to call us back + * when time is stepped. + */ + step_callback = time_stepped; + + /* + * before we start looking at clock period, do any multimedia + * timer manipulation requested via -M option. + */ + if (modify_mm_timer) { + + if (timeGetDevCaps(&tc, sizeof(tc)) == TIMERR_NOERROR) { + + wTimerRes = min(max(tc.wPeriodMin, MM_TIMER_INTV), tc.wPeriodMax); + timeBeginPeriod(wTimerRes); + atexit(atexit_revert_mm_timer); + + msyslog(LOG_INFO, "MM timer resolution: %u..%u msec, set to %u msec", + tc.wPeriodMin, tc.wPeriodMax, wTimerRes ); + } else + msyslog(LOG_ERR, "Multimedia timer unavailable"); + } + + /* get the performance counter ticks per second */ + if (!QueryPerformanceFrequency(&Freq) || !Freq.QuadPart) { - msyslog(LOG_ERR, "GetSystemTimeAdjustment failed: %m\n"); + msyslog(LOG_ERR, "QueryPerformanceFrequency failed: %m\n"); exit(-1); } - units_per_tick = initial_units_per_tick; + NomPerfCtrFreq = PerfCtrFreq = Freq.QuadPart; + msyslog(LOG_INFO, + "Performance counter frequency %.3f MHz", + PerfCtrFreq / 1e6); - /* Calculate the time adjustment resulting from incrementing - * units per tick by 1 unit for 1 second */ - ppm_per_adjust_unit = 1000000.0 / (double) every; + /* Determine the existing system time slewing */ + if (!GetSystemTimeAdjustment(&adjclockperiod, &clockperiod, &noslew)) + { + msyslog(LOG_ERR, "GetSystemTimeAdjustment failed: %m\n"); + exit(-1); + } -#ifdef DEBUG - msyslog(LOG_INFO, "Initial Clock increment %7.1f us", - (float) (units_per_tick / 10)); - msyslog(LOG_INFO, "Adjustment rate %5.3f ppm/s", ppm_per_adjust_unit); -#endif + /* + * If there is no slewing before ntpd, adjclockperiod and clockperiod + * will be equal. Any difference is carried into adj_systime's first + * pass as the previous adjustment. + */ + last_Adj = adjclockperiod - clockperiod; + + if (last_Adj) + msyslog(LOG_INFO, + "Clock interrupt period %.3f msec " + "(startup slew %.1f usec/period)", + clockperiod / 1e4, + last_Adj / 10.); + else + msyslog(LOG_INFO, + "Clock interrupt period %.3f msec", + clockperiod / 1e4); - StartClockThread(); + /* + * Calculate the time adjustment resulting from incrementing + * units per tick by 1 unit for 1 second + */ + ppm_per_adjust_unit = 1e6 / clockperiod; - /* Set up the Console Handler */ - if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE)) - { - msyslog(LOG_ERR, "Can't set console control handler: %m"); + /* + * Spin on GetSystemTimeAsFileTime to determine its + * granularity. Prior to Windows Vista this is + * typically the same as the clock period. + */ + GetSystemTimeAsFileTime(&initial_hectonanosecs.ft); + do { + GetSystemTimeAsFileTime(&next_hectonanosecs.ft); + } while (initial_hectonanosecs.ull == next_hectonanosecs.ull); + + os_clock_precision = next_hectonanosecs.ull - + initial_hectonanosecs.ull; + + msyslog(LOG_INFO, + "Windows clock precision %.3f msec, min. slew %.3f ppm/s", + os_clock_precision / 1e4, + ppm_per_adjust_unit); + + winnt_time_initialized = TRUE; + + if (os_clock_precision < 4 * 10000 && !getenv("NTPD_USE_INTERP_DANGEROUS")) { + msyslog(LOG_INFO, "using Windows clock directly"); + } else { + winnt_use_interpolation = TRUE; + get_sys_time_as_filetime = GetInterpTimeAsFileTime; + StartClockThread(); } } -void reset_winnt_time(void) +void +atexit_revert_mm_timer(void) { - /* restore the clock frequency back to its original value */ - if (!SetSystemTimeAdjustment(0, TRUE)) - { - msyslog(LOG_ERR, "Failed to reset clock state, SetSystemTimeAdjustment(): %m"); - } - /* read the current system time, and write it back to - force CMOS update: */ - /************ Added back in 2003-01-26 *****************/ - { - SYSTEMTIME st; + timeEndPeriod(wTimerRes); + DPRINTF(1, ("MM timer resolution reset\n")); +} + + +void +reset_winnt_time(void) +{ + SYSTEMTIME st; + + /* + * If we're in the 2-second slew right after a leap second, + * we don't want to continue that extreme slew, in that case + * disable our slewing and return clock discipline to the + * kernel. Similarly if we are not yet synchronized, + * our current slew may not be a good ongoing trim. + * Otherwise, our leave in place the last SetSystemTimeAdjustment + * as an ongoing frequency correction, better than nothing. + * TODO: + * Verify this will not call SetSystemTimeAdjustment if + * ntpd is running in ntpdate mode. + */ + if (sys_leap == LEAP_NOTINSYNC || ls_time_adjustment) + SetSystemTimeAdjustment(0, TRUE); + + /* + * Read the current system time, and write it back to + * force CMOS update, only if we are exiting because + * the computer is shutting down and we are already + * synchronized. + */ + if (ntservice_systemisshuttingdown() && sys_leap != LEAP_NOTINSYNC) { GetSystemTime(&st); SetSystemTime(&st); + NLOG(NLOG_SYSEVENT | NLOG_CLOCKINFO) + msyslog(LOG_NOTICE, "system is shutting down, CMOS time reset."); } } +/* + * GetSystemTimeAsFileTime() interface clone is used by getclock() in ntpd. + */ -int -gettimeofday( - struct timeval *tv +void WINAPI +GetInterpTimeAsFileTime( + LPFILETIME pft ) { - /* Use the system time (roughly synchronised to the tick, and - * extrapolated using the system performance counter. - */ - - ULONGLONG Count; - LARGE_INTEGER LargeIntNowCount; - ULONGLONG Time; - ULONGLONG NowCount; - ULONGLONG PreCount; /*FIX*/ - LONGLONG TicksElapsed; - LONG time_adjustment; + FT_ULL now_time; + FT_ULL now_count; /* Mark a mark ASAP. The latency to here should * be reasonably deterministic - */ + */ - PreCount = LastTimerCount; /*FIX*/ - - if (!QueryPerformanceCounter(&LargeIntNowCount)) { - msyslog(LOG_ERR, "QueryPeformanceCounter failed: %m"); - exit(1); - } + now_count.ull = perf_ctr(); + now_time.ull = interp_time(now_count.ull, TRUE); - NowCount = LargeIntNowCount.QuadPart; - - /* Get base time we are going to extrapolate from - */ - EnterCriticalSection(&TimerCritialSection); - Count = LastTimerCount; - Time = LastTimerTime; - LeaveCriticalSection(&TimerCritialSection); - - /* Calculate when now is. - * - * Result = LastTimerTime + (NowCount - LastTimerCount) / PerfFrequency - */ - - if (NowCount >= Count) - { - TicksElapsed = NowCount - Count; /* linear progression of ticks */ - } - else - { - /************************************************************************/ - /* Differentiate between real rollover and the case of taking a */ - /* perfcount then the APC coming in. */ - /************************************************************************/ - if (Count > PreCount) /*FIX*/ - { /*FIX*/ - TicksElapsed = 0; /*FIX*/ - } /*FIX*/ - else /*FIX*/ - { /*FIX*/ - TicksElapsed = NowCount + (RollOverCount - Count); /*FIX*/ - } /*FIX*/ - } - - /* Calculate the new time (in 100's of nano-seconds) - */ - time_adjustment = (long) ((TicksElapsed * HECTONANOSECONDS) / PerfFrequency); - Time += time_adjustment; + if (last_interp_time > now_time.ull) { - /* Convert the hecto-nano second time to tv format - */ - Time -= FILETIME_1970; - tv->tv_sec = (LONG) ( Time / 10000000ui64); - tv->tv_usec = (LONG) (( Time % 10000000ui64) / 10); + clock_backward_count++; + if (last_interp_time - now_time.ull > clock_backward_max) + clock_backward_max = last_interp_time - now_time.ull; + now_time.ull = last_interp_time; + } else + last_interp_time = now_time.ull; - return 0; + *pft = now_time.ft; + return; } + +/* + * TimerApcFunction is invoked on the high-priority clock + * thread to capture a new baseline system time and + * performance counter correlation every 43 msec (64Hz + * OS clock precision). + */ static void CALLBACK TimerApcFunction( LPVOID lpArgToCompletionRoutine, @@ -460,128 +735,195 @@ TimerApcFunction( DWORD dwTimerHighValue ) { - LARGE_INTEGER LargeIntNowCount; - (void) lpArgToCompletionRoutine; /* not used */ + static BOOL ctr_freq_timer_started = FALSE; + static ULONGLONG prev_count; + ULONGLONG now_time; + FT_ULL now_count; - if (dwTimerLowValue == lastLowTimer) return; - /* Grab the counter first of all */ - QueryPerformanceCounter(&LargeIntNowCount); + now_count.ull = perf_ctr(); - /* Save this for next time */ - lastLowTimer = dwTimerLowValue; + now_time = (((ULONGLONG)dwTimerHighValue << 32) | + dwTimerLowValue); - /* Check to see if the counter has rolled. This happens - more often on Multi-CPU systems */ + /* + * Save this correlation in the history. + */ + add_counter_time_pair(now_count.ull, now_time); - if ((ULONGLONG) LargeIntNowCount.QuadPart < LastTimerCount) { - /* Counter Rolled - try and estimate the rollover point using - the nominal counter frequency divided by an estimate of the - OS frequency */ - RollOverCount = LastTimerCount + PerfFrequency * every / HECTONANOSECONDS - - (ULONGLONG) LargeIntNowCount.QuadPart; -#ifdef DEBUG - msyslog(LOG_INFO, - "Performance Counter Rollover %I64u:\rLast Timer Count %I64u\rCurrent Count %I64u", - RollOverCount, LastTimerCount, LargeIntNowCount.QuadPart); -#endif - } + /* + * Once we're synchronized start the counter frequency + * tuning timer. + */ + if (INVALID_HANDLE_VALUE == ctr_freq_timer && + LEAP_NOTINSYNC != sys_leap) - /* Now we can hang out and wait for the critical section to free up; - we will get the CPU this timeslice. Meanwhile other tasks can use - the last value of LastTimerCount */ - - EnterCriticalSection(&TimerCritialSection); - LastTimerCount = (ULONGLONG) LargeIntNowCount.QuadPart; - LastTimerTime = ((ULONGLONG) dwTimerHighValue << 32) + - (ULONGLONG) dwTimerLowValue; - LeaveCriticalSection(&TimerCritialSection); + start_ctr_freq_timer(now_time); } - -DWORD WINAPI ClockThread(void *arg) +unsigned WINAPI +ClockThread( + void *arg + ) { + LARGE_INTEGER DueTime; + HANDLE timer; + double HZ; + double TimerHz; + DWORD timer_period_msec; + DWORD res; + char *ntpd_int_int_text; + + UNUSED_ARG(arg); + + timer = CreateWaitableTimer(NULL, FALSE, NULL); + + ntpd_int_int_text = getenv("NTPD_INT_INT"); + + HZ = (double)HECTONANOSECONDS / clockperiod; + + if (HZ > 63 && HZ < 65) { + timer_period_msec = 43; + } else if (HZ > 98 && HZ < 102) { + timer_period_msec = 27; + if (!ntpd_int_int_text) + msyslog(LOG_WARNING, + "%.3f Hz system clock may benefit from " + "custom NTPD_INT_INT env var timer interval " + "override between approx. 20 and 50 msecs.", + HZ); + } else { + timer_period_msec = (DWORD)(0.5 + (2.752 * clockperiod / 10000)); + if (!ntpd_int_int_text) + msyslog(LOG_WARNING, + "unfamiliar %.3f Hz system clock may benefit " + "from custom NTPD_INT_INT env var timer " + "interval override between approx. 20 and 50 " + "msecs.", + HZ); + } - LARGE_INTEGER DueTime; - HANDLE WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL); - - (void) arg; /* not used */ - - if (WaitableTimerHandle != NULL) { - DueTime.QuadPart = 0i64; - if (SetWaitableTimer(WaitableTimerHandle, &DueTime, 1L /* ms */, TimerApcFunction, &WaitableTimerHandle, FALSE) != NO_ERROR) { - for(;;) { - if (WaitForSingleObjectEx(TimerThreadExitRequest, INFINITE, TRUE) == WAIT_OBJECT_0) { - break; /* we've been asked to exit */ - } - } - } - CloseHandle(WaitableTimerHandle); - WaitableTimerHandle = NULL; + if (ntpd_int_int_text) { + timer_period_msec = atoi(ntpd_int_int_text); + timer_period_msec = max(9, timer_period_msec); + msyslog(LOG_NOTICE, + "using NTPD_INT_INT env var override %u", + timer_period_msec); } - return 0; -} + TimerHz = 1e3 / timer_period_msec; + msyslog(LOG_NOTICE, "HZ %.3f using %u msec timer %.3f Hz %d deep", + HZ, + timer_period_msec, + TimerHz, + BASELINES_USED); -static void StartClockThread(void) -{ - DWORD tid; - FILETIME StartTime; - LARGE_INTEGER Freq = { 0, 0 }; - - /* get the performance counter freq */ - if (!QueryPerformanceFrequency(&Freq)) - { - msyslog(LOG_ERR, "QueryPerformanceFrequency failed: %m\n"); - exit (-1); - } + /* negative DueTime means relative to now */ + DueTime.QuadPart = -(int)timer_period_msec; - PerfFrequency = Freq.QuadPart; + SetWaitableTimer( + timer, + &DueTime, /* first fire */ + timer_period_msec, /* period thereafter */ + TimerApcFunction, /* callback routine */ + &timer, /* context for callback */ + FALSE); /* do not interfere with power saving */ + /* + * The clock thread spends the rest of its life in the TimerApcFunction + * and ctr_freq_timer_fired timer APC callbacks, which can only occur + * while this thread is in an alertable wait. Note the Ex on + * WaitForSingleObjectEx and TRUE for fAlertable. The wait will return + * after each APC callback in which case we simply wait again. We will + * break out of the loop when StopClockThread signals our exit event. + */ + do res = WaitForSingleObjectEx( + TimerThreadExitRequest, + INFINITE, + TRUE); + while (WAIT_OBJECT_0 != res); - if ( modify_mm_timer != 0) - { - if (timeGetDevCaps( &tc, sizeof( tc ) ) == TIMERR_NOERROR ) - { - wTimerRes = min( max( tc.wPeriodMin, MM_TIMER_INTV ), tc.wPeriodMax ); + CloseHandle(timer); - timeBeginPeriod( wTimerRes ); -#ifdef DEBUG - msyslog( LOG_INFO, "MM timer resolution: %u..%u ms, set to %u ms\n", - tc.wPeriodMin, tc.wPeriodMax, wTimerRes ); -#endif - } - else - msyslog( LOG_ERR, "Failed to get MM timer caps\n" ); + if (ctr_freq_timer != INVALID_HANDLE_VALUE) { + CloseHandle(ctr_freq_timer); + ctr_freq_timer = INVALID_HANDLE_VALUE; } + return 0; +} - /* init variables with the time now */ - GetSystemTimeAsFileTime(&StartTime); - LastTimerTime = (((ULONGLONG) StartTime.dwHighDateTime) << 32) + - (ULONGLONG) StartTime.dwLowDateTime; - /* init sync objects */ - InitializeCriticalSection(&TimerCritialSection); - TimerThreadExitRequest = CreateEvent(NULL, FALSE, FALSE, "TimerThreadExitRequest"); +static void +StartClockThread(void) +{ + static BOOL done_once = FALSE; + FT_ULL StartTime; - ClockThreadHandle = CreateThread(NULL, 0, ClockThread, NULL, - CREATE_SUSPENDED, &tid); + /* init variables with the time now */ + GetSystemTimeAsFileTime(&StartTime.ft); + baseline_times[0] = StartTime.ull; + baseline_counts[0] = perf_ctr(); - if (ClockThreadHandle != NULL) { + /* init sync objects */ + TimerThreadExitRequest = CreateEvent(NULL, FALSE, FALSE, NULL); + + clock_thread = + (HANDLE)_beginthreadex( + NULL, + 0, + ClockThread, + NULL, + CREATE_SUSPENDED, + &clock_thread_id); + + if (clock_thread != NULL) { /* remember the thread priority is only within the process class */ - if (!SetThreadPriority(ClockThreadHandle, THREAD_PRIORITY_TIME_CRITICAL)) { + if (!SetThreadPriority(clock_thread, THREAD_PRIORITY_TIME_CRITICAL)) { DPRINTF(1, ("Error setting thread priority\n")); } - lock_thread_to_processor(ClockThreadHandle); - ResumeThread(ClockThreadHandle); + lock_thread_to_processor(clock_thread); + ResumeThread(clock_thread); + + if (FALSE == done_once) { + done_once = TRUE; + lock_thread_to_processor(GetCurrentThread()); + atexit( StopClockThread ); + } + + /* + * Give the clock thread time to fill its counter/time + * sample buffer. This will underfill the buffer a + * bit for sample periods over 43 msec. + */ + Sleep(BASELINES_USED * 43); + } +} + - lock_thread_to_processor(GetCurrentThread()); +void +StopClockThread(void) +{ + /* + * if the clock thread exit()s this routine + * will be called on the clock thread and + * we need not (and can't) use the normal + * TimerThreadExitRequest event. + */ + if (GetCurrentThreadId() != clock_thread_id) { - atexit( StopClockThread ); + if (!SetEvent(TimerThreadExitRequest) || + WaitForSingleObject(clock_thread, 2 * 1000) != + WAIT_OBJECT_0) { + msyslog(LOG_ERR, "Failed to stop clock thread."); + } } + CloseHandle(TimerThreadExitRequest); + TimerThreadExitRequest = NULL; + CloseHandle(clock_thread); + clock_thread = NULL; } @@ -594,7 +936,28 @@ lock_thread_to_processor(HANDLE thread) char *cputext; unsigned int cpu; + if ( ! winnt_time_initialized) { + DPRINTF(1, ("init_winnt_time() must be called before " + "lock_thread_to_processor(), exiting\n")); + exit(-1); + } + + if ( ! winnt_use_interpolation) + return; + + if (-1 == use_pcc) { + DPRINTF(1, ("perf_ctr is not called before affinity, " + "change lock_thread_to_processor to call it\n")); + exit(-1); + } else if (0 == use_pcc) + return; + + /* + * Calculate the ThreadAffinityMask we'll use once on the + * first invocation. + */ if ( ! ProcessAffinityMask) { + /* * Choose which processor to nail the main and clock threads to. * If we have more than one, we simply choose the 2nd. @@ -635,55 +998,540 @@ lock_thread_to_processor(HANDLE thread) ThreadAffinityMask = (0 == cpu) ? 0 : (1 << (cpu - 1)); if (ThreadAffinityMask && - !(ThreadAffinityMask & ProcessAffinityMask)) { + !(ThreadAffinityMask & ProcessAffinityMask)) DPRINTF(1, ("Selected CPU %u (mask %x) is outside " "process mask %x, using all CPUs.\n", cpu, ThreadAffinityMask, ProcessAffinityMask)); - } else { + else DPRINTF(1, ("Wiring to processor %u (0 means all) " "affinity mask %x\n", cpu, ThreadAffinityMask)); - } ThreadAffinityMask &= ProcessAffinityMask; } if (ThreadAffinityMask && - !SetThreadAffinityMask(thread, ThreadAffinityMask)) { - - DPRINTF(1, ("Unable to wire thread to mask %x: %s\n", - ThreadAffinityMask, strerror(GetLastError()))); + !SetThreadAffinityMask(thread, ThreadAffinityMask)) + + msyslog(LOG_ERR, + "Unable to wire thread to mask %x: %m\n", + ThreadAffinityMask); +} + + +#ifdef HAVE_PPSAPI +/* + * helper routine for serial PPS which returns QueryPerformanceCounter + * timestamp and needs to interpolate it to an NTP timestamp. + */ +void +pps_ntp_timestamp_from_counter( + ntp_fp_t *result, + ULONGLONG Timestamp, + ULONGLONG Counterstamp + ) +{ + /* + * convert between equivalent l_fp and PPSAPI ntp_fp_t + */ + ntp_timestamp_from_counter( + (l_fp *)result, + Timestamp, + Counterstamp); +} + + +void +ntp_timestamp_from_counter( + l_fp *result, + ULONGLONG Timestamp, + ULONGLONG Counterstamp + ) +{ +#ifdef DEBUG + FT_ULL Now; +#endif + ULONGLONG InterpTimestamp; + + if (winnt_use_interpolation) { + InterpTimestamp = interp_time(Counterstamp + QPC_offset, FALSE); + +#ifdef DEBUG + /* sanity check timestamp is within 1 minute of now */ + GetSystemTimeAsFileTime(&Now.ft); + Now.ll -= InterpTimestamp; + if (Now.ll > 60 * HECTONANOSECONDS || + Now.ll < -60 * (LONGLONG) HECTONANOSECONDS) { + DPRINTF(1, ("ntp_timestamp_from_counter interpolated " + "time %.6fs from current\n", + Now.ll / (double) HECTONANOSECONDS)); + DPRINTF(1, ("interpol time %llx from %llx\n", + InterpTimestamp, + Counterstamp)); + exit(-1); + } +#endif + } else { /* ! winnt_use_interpolation */ + /* have to simply use the driver's system time timestamp */ + InterpTimestamp = Timestamp; +#ifdef DEBUG + /* sanity check timestamp is within 1 minute of now */ + GetSystemTimeAsFileTime(&Now.ft); + Now.ll -= InterpTimestamp; + if (Now.ll > 60 * HECTONANOSECONDS || + Now.ll < -60 * (LONGLONG) HECTONANOSECONDS) { + DPRINTF(1, ("ntp_timestamp_from_counter serial driver system " + "time %.6fs from current\n", + Now.ll / (double) HECTONANOSECONDS)); + exit(-1); + } +#endif } + + /* convert from 100ns units to NTP fixed point format */ + + InterpTimestamp -= FILETIME_1970; + result->l_ui = JAN_1970 + (u_int32)(InterpTimestamp / HECTONANOSECONDS); + result->l_uf = (u_int32)((InterpTimestamp % HECTONANOSECONDS) * + (FRAC / HECTONANOSECONDS)); } +#endif /* HAVE_PPSAPI */ -static void StopClockThread(void) -{ - if ( wTimerRes ) /* if not 0 then the MM timer has been modified at startup */ - { - timeEndPeriod( wTimerRes ); - wTimerRes = 0; +void +time_stepped(void) +{ + /* + * called back by ntp_set_tod after the system + * time has been stepped (set). + * + * We normally prevent the reported time from going backwards + * but need to allow it in this case. + */ + if (FALSE == winnt_use_interpolation) + return; + + + /* + * Restart the clock thread to get a new baseline + * time/counter correlation. + */ + StopClockThread(); + + /* + * newest_baseline_gen is a generation counter + * incremented once each time newest_baseline + * is reset. + */ + newest_baseline_gen++; + + last_interp_time = + clock_backward_max = + clock_backward_count = + newest_baseline = 0; + + memset(baseline_counts, 0, sizeof(baseline_counts)); + memset(baseline_times, 0, sizeof(baseline_times)); + + StartClockThread(); +} + + +/* + * log2ull - log base 2 of a unsigned 64-bit number + */ +int +log2ull( + ULONGLONG n + ) +{ + const ULONGLONG one = 1; + int log = 0; + + if (n >= one<<32) { n >>= 32; log += 32; } + if (n >= one<<16) { n >>= 16; log += 16; } + if (n >= one<< 8) { n >>= 8; log += 8; } + if (n >= one<< 4) { n >>= 4; log += 4; } + if (n >= one<< 2) { n >>= 2; log += 2; } + if (n >= one<< 1) { log += 1; } + + return (n) ? log : (-1); +} + + +/* + * ctr_freq_timer_fired is called once a few seconds before + * tune_ctr_period seconds have elapsed, to reset the timer + * and hopefully minimize error due to the system using the + * nominal performance counter frequency to set the timer + * internally, which is typically dozens of PPM from the + * actual performance counter rate. A few seconds later + * it is called again to observe the counter and estimate the + * counter frequency. + */ +static void CALLBACK +ctr_freq_timer_fired( + LPVOID arg, + DWORD dwTimeLow, + DWORD dwTimeHigh + ) +{ + static FT_ULL begin_time = {0}; + static FT_ULL begin_count = {0}; + static ULONGLONG next_period_time = 0; + static ULONGLONG report_systemtime = 0; + FT_ULL now_time; + FT_ULL now_count; + + if (!begin_time.ull) { + begin_count.ull = perf_ctr(); + begin_time.ft.dwLowDateTime = dwTimeLow; + begin_time.ft.dwHighDateTime = dwTimeHigh; + + /* + * adapt perf ctr observation interval to the + * counter frequency + */ + tune_ctr_period = 22680 / log2ull(NomPerfCtrFreq); + + /* + * reset timer 2s before period ends to minimize + * error from OS timer routines using nominal + * performance frequency internally. + */ + tune_ctr_freq_max_interval = tune_ctr_period - 2; + + next_period_time = begin_time.ull + + (ULONGLONG)tune_ctr_period * HECTONANOSECONDS; + + ROUND_TO_NEXT_SEC_BOTTOM(next_period_time); + + reset_ctr_freq_timer(next_period_time, begin_time.ull); + + return; + } + + now_time.ft.dwLowDateTime = dwTimeLow; + now_time.ft.dwHighDateTime = dwTimeHigh; + + if (now_time.ull >= next_period_time) { + + now_count.ull = perf_ctr(); + tune_ctr_freq( + now_count.ull - begin_count.ull, + now_time.ull - begin_time.ull); + + next_period_time += (ULONGLONG)tune_ctr_period * HECTONANOSECONDS; + begin_count.ull = now_count.ull; + begin_time.ull = now_time.ull; + } + + /* + * Log clock backward events no more often than 5 minutes. + */ + if (!report_systemtime) + + report_systemtime = now_time.ull + + 5 * 60 * HECTONANOSECONDS; + + else if (report_systemtime <= now_time.ull) { + + report_systemtime += 5 * 60 * HECTONANOSECONDS; + + if (clock_backward_count) { + msyslog(LOG_WARNING, + "clock would have gone backward %d times, " + "max %.1f usec", + clock_backward_count, + clock_backward_max / 10.); + + clock_backward_max = clock_backward_count = 0; + } + } + + reset_ctr_freq_timer(next_period_time, now_time.ull); +} + + +void +reset_ctr_freq_timer_abs( + ULONGLONG when + ) +{ + FT_ULL fire_time; + + fire_time.ull = when; + + SetWaitableTimer( + ctr_freq_timer, + &fire_time.li, /* first fire */ + 0, /* not periodic */ + ctr_freq_timer_fired, /* callback routine */ + NULL, /* context for callback */ + FALSE); /* do not interfere with power saving */ +} + + +void +reset_ctr_freq_timer( + ULONGLONG when, + ULONGLONG now + ) +{ + if (when - now > + (tune_ctr_freq_max_interval * HECTONANOSECONDS + HECTONANOSECONDS)) + + when = now + tune_ctr_freq_max_interval * HECTONANOSECONDS; + + reset_ctr_freq_timer_abs(when); +} + + +void +start_ctr_freq_timer( + ULONGLONG now_time + ) +{ + ULONGLONG when; + + ctr_freq_timer = CreateWaitableTimer(NULL, FALSE, NULL); + + when = now_time; + ROUND_TO_NEXT_SEC_BOTTOM(when); + + reset_ctr_freq_timer_abs(when); +} + + +/* + * tune_ctr_freq is called once per tune_ctr_period seconds + * with a counter difference and time difference. + */ +void +tune_ctr_freq( + LONGLONG ctr_delta, + LONGLONG time_delta + ) +{ + static unsigned count = 0; + static unsigned dispcount = 0; + static unsigned report_at_count = 0; + static int disbelieved = 0; + static int i = 0; + static double nom_freq = 0; + static LONGLONG diffs[TUNE_CTR_DEPTH] = {0}; + static LONGLONG sum = 0; + + LONGLONG delta; + LONGLONG deltadiff; + ULONGLONG ObsPerfCtrFreq; + double obs_freq; + double this_freq; + int isneg; + + /* nom_freq is constant over the run */ + if (!report_at_count) { + report_at_count = 24 * 60 * 60 / tune_ctr_period; + nom_freq = NomPerfCtrFreq / 1e6; + } + + /* delta is the per-second observed frequency this time */ + delta = (LONGLONG)((double)ctr_delta * HECTONANOSECONDS / + time_delta); + + /* disbelieve any delta more than +/- 976 PPM from nominal */ + deltadiff = delta - NomPerfCtrFreq; + if (0 > deltadiff) { + isneg = 1; + deltadiff = -deltadiff; + } else + isneg = 0; + + if ((ULONGLONG)deltadiff > (NomPerfCtrFreq / 1024)) { + disbelieved++; + dispcount++; #ifdef DEBUG - msyslog( LOG_INFO, "MM timer set to default\n" ); + msyslog(LOG_DEBUG, "ctr delta %s%lld exceeds limit %llu", + (isneg) ? "-" : "", + deltadiff, + NomPerfCtrFreq / 1024); #endif + } else { + + /* + * collect average over TUNE_CTR_DEPTH samples + * for our PerfCtrFreq trimming. + */ + + if (isneg) + deltadiff = -deltadiff; + sum -= diffs[i]; + diffs[i] = deltadiff; + sum += deltadiff; + i = (i + 1) % COUNTOF(diffs); + count++; + dispcount++; } - if (SetEvent(TimerThreadExitRequest) && - WaitForSingleObject(ClockThreadHandle, 10000L) == 0) - { - CloseHandle(TimerThreadExitRequest); - TimerThreadExitRequest = NULL; + this_freq = delta / 1e6; + + ObsPerfCtrFreq = NomPerfCtrFreq + (sum / COUNTOF(diffs)); + +#if 1 /* #if 0 to disable changing freq used */ + /* get rid of ObsPerfCtrFreq when removing the #ifdef */ + PerfCtrFreq = ObsPerfCtrFreq; +#endif + obs_freq = ObsPerfCtrFreq / 1e6; - CloseHandle(ClockThreadHandle); - ClockThreadHandle = NULL; + /* + * report observed ctr freq each time the estimate used during + * startup moves toward the observed freq from the nominal, + * and once a day afterward. + */ - DeleteCriticalSection(&TimerCritialSection); + if (count > COUNTOF(diffs) && + /* (count % COUNTOF(diffs)) && */ /* enables reporting each */ + dispcount < report_at_count) /* TUNE_CTR_DEPTH samples */ + return; + + NLOG(NLOG_CLOCKINFO) + if (count <= COUNTOF(diffs)) + /* moving to observed freq. from nominal (startup) */ + msyslog(LOG_INFO, + (obs_freq > 100) + ? "ctr %.3f MHz %+6.2f PPM using " + "%.3f MHz %+6.2f PPM" + : "ctr %.6f MHz %+6.2f PPM using " + "%.6f MHz %+6.2f PPM", + this_freq, + 1e6 * (this_freq - nom_freq) / nom_freq, + obs_freq, + 1e6 * (obs_freq - nom_freq) / nom_freq); + else + /* steady state */ + msyslog(LOG_INFO, + (obs_freq > 100) + ? "ctr %.3f MHz %+.2f PPM" + : "ctr %.6f MHz %+.2f PPM", + obs_freq, + 1e6 * (obs_freq - nom_freq) / nom_freq); + + if (disbelieved) { + msyslog(LOG_ERR, + "%d ctr samples exceed +/- 976 PPM range gate", + disbelieved); + disbelieved = 0; } - else - { - msyslog(LOG_ERR, "Failed to stop clock thread."); - Sleep( 100 ); + + dispcount = 0; +} + + +/* + * add_counter_time_pair is called by the + * high priority clock thread with each new + * baseline counter/time correlation. + */ +void +add_counter_time_pair( + ULONGLONG ctr, + LONGLONG time + ) +{ + int i; + + i = (newest_baseline + 1) % BASELINES_TOT; + + baseline_counts[i] = ctr; + baseline_times[i] = time; + + newest_baseline = i; +} + + +/* + * interp_time estimates NT time in 100ns units + * based on a performance counter value given. + * This must tolerate recent historical counters + * as well as current. When current is FALSE + * we can't assume ctr is the latest/highest + * seen. + */ +ULONGLONG +interp_time( + ULONGLONG ctr, + BOOL current + ) +{ + static __declspec(thread) int last_newest = -1; + static __declspec(thread) int last_newest_gen; + static __declspec(thread) int best_index; + ULONGLONG this_ctr; + LONGLONG this_time; + LONGLONG latest_time; + LONGLONG ctr_diff; + int i; + int i_gen; + int c; + + /* + * Use the system time (roughly synchronised to the tick, and + * extrapolated using the system performance counter. + * + * Cache the results per thread and only repeat the + * calculation when new data has arrived. + */ + i = newest_baseline; + i_gen = newest_baseline_gen; + + if (last_newest == i && last_newest_gen == i_gen) { + this_time = baseline_times[best_index]; + ctr_diff = ctr - baseline_counts[best_index]; + this_time += (LONGLONG)PERF2HNS((double)ctr_diff); + return this_time; } + + last_newest = i; + last_newest_gen = i_gen; + + latest_time = 0; + + /* + * Run through the history calculating the interpolated + * time based on each counter/time correlation in turn, + * and believe the latest one. This is akin to the NTP + * protocol minimum delay clock filter. Errors due to + * counter/time correlations with stale time are all + * negative. + */ + for (c = 0; c < BASELINES_USED; c++) { + if (baseline_times[i]) { + this_time = baseline_times[i]; + this_ctr = baseline_counts[i]; + + ctr_diff = ctr - this_ctr; + + if (current && ctr_diff < 0) { + /* + * The performance counter apparently went + * backwards without rolling over. It might + * be nice to complain but we don't want + * to do it repeatedly. + */ + ctr_diff = 0; + } + + this_time += (LONGLONG)PERF2HNS((double)ctr_diff); + + if (this_time > latest_time) { + latest_time = this_time; + best_index = i; + } + } + i = i ? (i - 1) : (BASELINES_TOT - 1); + } + + return latest_time; } diff --git a/ports/winnt/ntpd/ntp_iocompletionport.c b/ports/winnt/ntpd/ntp_iocompletionport.c index 449dde113..7f7500e39 100644 --- a/ports/winnt/ntpd/ntp_iocompletionport.c +++ b/ports/winnt/ntpd/ntp_iocompletionport.c @@ -17,6 +17,7 @@ #include "ntp_iocompletionport.h" #include "transmitbuff.h" #include "ntp_request.h" +#include "clockstuff.h" #include "ntp_io.h" /* @@ -54,9 +55,6 @@ typedef struct IoCompletionInfo { typedef DWORD ULONG_PTR; #endif -/* in nt_clockstuff.c */ -extern void lock_thread_to_processor(HANDLE); - /* * local function definitions */ @@ -157,7 +155,7 @@ iocompletionthread(void *NotUsed) IoCompletionInfo * lpo = NULL; u_long time_next_ifscan_after_error = 0; - /* UNUSED_ARG(NotUsed); */ + UNUSED_ARG(NotUsed); /* * socket and refclock receive call gettimeofday() @@ -697,7 +695,7 @@ OnWriteComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus) transmitbuf_t *buff; struct interface *inter; - /* UNUSED_ARG(Bytes); */ + UNUSED_ARG(Bytes); buff = lpo->trans_buf; diff --git a/ports/winnt/ntpd/ntservice.c b/ports/winnt/ntpd/ntservice.c index a9afe1f68..4aaf8c09e 100644 --- a/ports/winnt/ntpd/ntservice.c +++ b/ports/winnt/ntpd/ntservice.c @@ -31,23 +31,23 @@ #include #endif + /* Handle to SCM for updating service status */ static SERVICE_STATUS_HANDLE hServiceStatus = 0; static BOOL foreground = FALSE; -static char ConsoleTitle[128]; +static BOOL computer_shutting_down = FALSE; static int glb_argc; static char **glb_argv; HANDLE hServDoneEvent = NULL; int accept_wildcard_if_for_winnt; extern volatile int debug; -extern char *progname; void uninit_io_completion_port(); int ntpdmain(int argc, char *argv[]); /* * Forward declarations */ -void ServiceControl(DWORD dwCtrlCode); +void WINAPI ServiceControl(DWORD dwCtrlCode); void ntservice_exit(void); void WINAPI service_main( DWORD argc, LPTSTR *argv ) @@ -99,27 +99,27 @@ int main( int argc, char *argv[] ) if (foreground) { /* run in console window */ - exit(ntpdmain(argc, argv)); + rc = ntpdmain(argc, argv); } else { /* Start up as service */ SERVICE_TABLE_ENTRY dispatchTable[] = { - { TEXT(NTP_DISPLAY_NAME), (LPSERVICE_MAIN_FUNCTION) service_main }, + { TEXT(NTP_DISPLAY_NAME), service_main }, { NULL, NULL } }; rc = StartServiceCtrlDispatcher(dispatchTable); - if (!rc) { - progname = argv[0]; + if (rc) + rc = 0; + else { rc = GetLastError(); #ifdef DEBUG - fprintf(stderr, "%s: unable to start as service, rc: %i\n\n", progname, rc); + fprintf(stderr, "%s: unable to start as service, rc: %i\n\n", argv[0], rc); #endif fprintf(stderr, "\nUse -d, -q, --help or -n to run from the command line.\n"); - exit(rc); } } - exit(0); + return rc; } /* @@ -127,14 +127,15 @@ int main( int argc, char *argv[] ) */ void ntservice_init() { + char ConsoleTitle[256]; + if (!foreground) { /* Register handler with the SCM */ hServiceStatus = RegisterServiceCtrlHandler(NTP_DISPLAY_NAME, - (LPHANDLER_FUNCTION)ServiceControl); + ServiceControl); if (!hServiceStatus) { NTReportError(NTP_SERVICE_NAME, "could not register service control handler"); - UpdateSCM(SERVICE_STOPPED); exit(1); } UpdateSCM(SERVICE_RUNNING); @@ -146,6 +147,7 @@ ntservice_init() { atexit( ntservice_exit ); } + /* * Routine to check if this is a service or a foreground program */ @@ -153,22 +155,19 @@ BOOL ntservice_isservice() { return(!foreground); } -/* service_ctrl - control handler for NTP service - * signals the service_main routine of start/stop requests - * from the control panel or other applications making - * win32API calls + +/* + * Routine to check if the service is stopping + * because the computer is shutting down */ +BOOL +ntservice_systemisshuttingdown() { + return computer_shutting_down; +} + void ntservice_exit( void ) { - - if (!foreground) { /* did not become a service, simply exit */ - /* service mode, need to have the service_main routine - * register with the service control manager that the - * service has stopped running, before exiting - */ - UpdateSCM(SERVICE_STOPPED); - } uninit_io_completion_port(); Sleep( 200 ); //##++ @@ -179,13 +178,21 @@ ntservice_exit( void ) # ifdef DEBUG _CrtDumpMemoryLeaks(); # endif + + if (!foreground) { + /* service mode, need to have the service_main routine + * register with the service control manager that the + * service has stopped running, before exiting + */ + UpdateSCM(SERVICE_STOPPED); + } } /* * ServiceControl(): Handles requests from the SCM and passes them on * to the service. */ -void +void WINAPI ServiceControl(DWORD dwCtrlCode) { /* Handle the requested control code */ HANDLE exitEvent = get_exit_event(); @@ -193,14 +200,17 @@ ServiceControl(DWORD dwCtrlCode) { switch(dwCtrlCode) { case SERVICE_CONTROL_SHUTDOWN: + computer_shutting_down = TRUE; + /* fall through to stop case */ + case SERVICE_CONTROL_STOP: - UpdateSCM(SERVICE_STOP_PENDING); if (exitEvent != NULL) { SetEvent(exitEvent); + UpdateSCM(SERVICE_STOP_PENDING); Sleep( 100 ); //##++ } return; - + case SERVICE_CONTROL_PAUSE: case SERVICE_CONTROL_CONTINUE: case SERVICE_CONTROL_INTERROGATE: @@ -231,10 +241,7 @@ void UpdateSCM(DWORD state) { ss.dwWin32ExitCode = NO_ERROR; ss.dwWaitHint = dwState == SERVICE_STOP_PENDING ? 5000 : 1000; - if (!SetServiceStatus(hServiceStatus, &ss)) { - ss.dwCurrentState = SERVICE_STOPPED; - SetServiceStatus(hServiceStatus, &ss); - } + SetServiceStatus(hServiceStatus, &ss); } } @@ -247,7 +254,7 @@ OnConsoleEvent( switch (dwCtrlType) { #ifdef DEBUG - case CTRL_BREAK_EVENT : + case CTRL_BREAK_EVENT: if (debug > 0) { debug <<= 1; } @@ -257,24 +264,28 @@ OnConsoleEvent( if (debug > 8) { debug = 0; } - printf("debug level %d\n", debug); - break ; + msyslog(LOG_DEBUG, "debug level %d", debug); + break; +#else + case CTRL_BREAK_EVENT: + break; #endif - case CTRL_C_EVENT : - case CTRL_CLOSE_EVENT : - case CTRL_SHUTDOWN_EVENT : + case CTRL_C_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_SHUTDOWN_EVENT: if (exitEvent != NULL) { SetEvent(exitEvent); Sleep( 100 ); //##++ } - break; + break; default : - return FALSE; - - + /* pass to next handler */ + return FALSE; } + + /* we've handled it, no more handlers should be called */ return TRUE;; } diff --git a/ports/winnt/ntpd/win32_io.c b/ports/winnt/ntpd/win32_io.c index 20aa6dc89..c8718464a 100644 --- a/ports/winnt/ntpd/win32_io.c +++ b/ports/winnt/ntpd/win32_io.c @@ -14,18 +14,6 @@ #define MAX_SERIAL 16 /* COM1-COM16 */ -int NT_set_process_priority(void) -{ - if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) - { - msyslog(LOG_ERR, "SetPriorityClass: %m"); - return 0; - } - else - return 1; -} - - /* * common_serial_open ensures duplicate opens of the same port * work by duplicating the handle for the 2nd open, allowing diff --git a/ports/winnt/ntpdate/ntpdate.vcproj b/ports/winnt/ntpdate/ntpdate.vcproj index 37e0c145a..3edd8e901 100644 --- a/ports/winnt/ntpdate/ntpdate.vcproj +++ b/ports/winnt/ntpdate/ntpdate.vcproj @@ -240,28 +240,6 @@ /> - - - - - - - - diff --git a/ports/winnt/ntpdc/ntpdc.vcproj b/ports/winnt/ntpdc/ntpdc.vcproj index 94b621129..cee1866b7 100644 --- a/ports/winnt/ntpdc/ntpdc.vcproj +++ b/ports/winnt/ntpdc/ntpdc.vcproj @@ -289,28 +289,6 @@ /> - - - - - - - - diff --git a/ports/winnt/ntpq/ntpq.vcproj b/ports/winnt/ntpq/ntpq.vcproj index bf2b57d48..7803454f1 100644 --- a/ports/winnt/ntpq/ntpq.vcproj +++ b/ports/winnt/ntpq/ntpq.vcproj @@ -283,28 +283,6 @@ /> - - - - - - - - diff --git a/ports/winnt/scripts/mkver.bat b/ports/winnt/scripts/mkver.bat index 71e6d8032..f548be43f 100755 --- a/ports/winnt/scripts/mkver.bat +++ b/ports/winnt/scripts/mkver.bat @@ -130,14 +130,14 @@ REM **************************************************************************** SET UTC_SIGN= REM *** Now get the timezone settings from the registry - regedit /e %TEMP%\TZ.TMP "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - IF NOT EXIST %TEMP%\TZ.TMP GOTO NOTZINFO + regedit /e %TEMP%\TZ-%GENERATED_PROGRAM%.TMP "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + IF NOT EXIST %TEMP%\TZ-%GENERATED_PROGRAM%.TMP GOTO NOTZINFO - for /f "Tokens=1* Delims==" %%a in ('type %TEMP%\TZ.TMP') do if %%a == "ActiveTimeBias" SET ACTIVEBIAS=%%b + for /f "Tokens=1* Delims==" %%a in ('type %TEMP%\TZ-%GENERATED_PROGRAM%.TMP') do if %%a == "ActiveTimeBias" SET ACTIVEBIAS=%%b for /f "Tokens=1* Delims=:" %%a in ('echo %ACTIVEBIAS%') do ( SET ACTIVEBIAS=%%b & SET PARTYP=%%a ) REM *** Clean up temporary file - IF EXIST %TEMP%\TZ.TMP DEL %TEMP%\TZ.TMP + IF EXIST %TEMP%\TZ-%GENERATED_PROGRAM%.TMP DEL %TEMP%\TZ-%GENERATED_PROGRAM%.TMP REM *** Check if we really got a dword value from the registry ... IF NOT "%PARTYP%"=="dword " goto NOTZINFO diff --git a/util/ntp-keygen.c b/util/ntp-keygen.c index f6aa55b89..787cf4d7c 100644 --- a/util/ntp-keygen.c +++ b/util/ntp-keygen.c @@ -279,11 +279,8 @@ main( */ gethostname(hostbuf, MAXHOSTNAME); hostname = hostbuf; -#ifndef SYS_WINNT gettimeofday(&tv, 0); -#else - gettimeofday(&tv); -#endif + epoch = tv.tv_sec; {