[24540] | 1 | /* $Id: VBoxServiceTimeSync.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
|
---|
[3655] | 2 | /** @file
|
---|
| 3 | * VBoxService - Guest Additions TimeSync Service.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[76553] | 7 | * Copyright (C) 2007-2019 Oracle Corporation
|
---|
[3655] | 8 | *
|
---|
| 9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
| 10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
| 11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
[5999] | 12 | * General Public License (GPL) as published by the Free Software
|
---|
| 13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
| 14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
| 15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
[3655] | 16 | */
|
---|
| 17 |
|
---|
| 18 |
|
---|
[58033] | 19 | /** @page pg_vgsvc_timesync VBoxService - The Time Sync Service
|
---|
[3655] | 20 | *
|
---|
[58052] | 21 | * The time sync subservice synchronizes the guest OS walltime with the host.
|
---|
| 22 | *
|
---|
[3725] | 23 | * The time sync service plays along with the Time Manager (TM) in the VMM
|
---|
[70072] | 24 | * to keep the guest time accurate using the host machine as a reference.
|
---|
[58052] | 25 | * Communication is facilitated by VMMDev. TM will try its best to make sure
|
---|
[70072] | 26 | * all timer ticks get delivered so that there isn't normally any need to
|
---|
[58052] | 27 | * adjust the guest time.
|
---|
[3725] | 28 | *
|
---|
[3655] | 29 | * There are three normal (= acceptable) cases:
|
---|
| 30 | * -# When the service starts up. This is because ticks and such might
|
---|
| 31 | * be lost during VM and OS startup. (Need to figure out exactly why!)
|
---|
[3725] | 32 | * -# When the TM is unable to deliver all the ticks and swallows a
|
---|
[3655] | 33 | * backlog of ticks. The threshold for this is configurable with
|
---|
| 34 | * a default of 60 seconds.
|
---|
[3725] | 35 | * -# The time is adjusted on the host. This can be caused manually by
|
---|
[3655] | 36 | * the user or by some time sync daemon (NTP, LAN server, etc.).
|
---|
[3725] | 37 | *
|
---|
| 38 | * There are a number of very odd case where adjusting is needed. Here
|
---|
[3655] | 39 | * are some of them:
|
---|
[33540] | 40 | * -# Timer device emulation inaccuracies (like rounding).
|
---|
| 41 | * -# Inaccuracies in time source VirtualBox uses.
|
---|
[3655] | 42 | * -# The Guest and/or Host OS doesn't perform proper time keeping. This
|
---|
[70072] | 43 | * can come about as a result of OS and/or hardware issues.
|
---|
[3725] | 44 | *
|
---|
| 45 | * The TM is our source for the host time and will make adjustments for
|
---|
| 46 | * current timer delivery lag. The simplistic approach taken by TM is to
|
---|
[3655] | 47 | * adjust the host time by the current guest timer delivery lag, meaning that
|
---|
[70072] | 48 | * if the guest is behind 1 second with PIT/RTC/++ ticks, this should be
|
---|
| 49 | * reflected in the guest wall time as well.
|
---|
[3725] | 50 | *
|
---|
[3655] | 51 | * Now, there is any amount of trouble we can cause by changing the time.
|
---|
[70072] | 52 | * Most applications probably use the wall time when they need to measure
|
---|
[3655] | 53 | * things. A walltime that is being juggled about every so often, even if just
|
---|
[33540] | 54 | * a little bit, could occasionally upset these measurements by for instance
|
---|
[3655] | 55 | * yielding negative results.
|
---|
[3725] | 56 | *
|
---|
| 57 | * This bottom line here is that the time sync service isn't really supposed
|
---|
[3655] | 58 | * to do anything and will try avoid having to do anything when possible.
|
---|
[3725] | 59 | *
|
---|
[3655] | 60 | * The implementation uses the latency it takes to query host time as the
|
---|
[3725] | 61 | * absolute maximum precision to avoid messing up under timer tick catchup
|
---|
[70072] | 62 | * and/or heavy host/guest load. (Rationale is that a *lot* of stuff may
|
---|
| 63 | * happen on our way back from ring-3 and TM/VMMDev since we're taking the
|
---|
| 64 | * route thru the inner EM loop with its force flag processing.)
|
---|
[3725] | 65 | *
|
---|
| 66 | * But this latency has to be measured from our perspective, which means it
|
---|
[70072] | 67 | * could just as easily come out as 0. (OS/2 and Windows guests only update
|
---|
[3655] | 68 | * the current time when the timer ticks for instance.) The good thing is
|
---|
[3725] | 69 | * that this isn't really a problem since we won't ever do anything unless
|
---|
[33540] | 70 | * the drift is noticeable.
|
---|
[3725] | 71 | *
|
---|
| 72 | * It now boils down to these three (configuration) factors:
|
---|
[64300] | 73 | * -# g_cMsTimeSyncMinAdjust - The minimum drift we will ever bother with.
|
---|
[58033] | 74 | * -# g_TimeSyncLatencyFactor - The factor we multiply the latency by to
|
---|
[3655] | 75 | * calculate the dynamic minimum adjust factor.
|
---|
[64300] | 76 | * -# g_cMsTimeSyncMaxLatency - When to start discarding the data as utterly
|
---|
[3655] | 77 | * useless and take a rest (someone is too busy to give us good data).
|
---|
[21162] | 78 | * -# g_TimeSyncSetThreshold - The threshold at which we will just set the time
|
---|
| 79 | * instead of trying to adjust it (milliseconds).
|
---|
[3655] | 80 | */
|
---|
| 81 |
|
---|
[57358] | 82 |
|
---|
| 83 | /*********************************************************************************************************************************
|
---|
| 84 | * Header Files *
|
---|
| 85 | *********************************************************************************************************************************/
|
---|
[6029] | 86 | #ifdef RT_OS_WINDOWS
|
---|
[62679] | 87 | # include <iprt/win/windows.h>
|
---|
[6029] | 88 | #else
|
---|
| 89 | # include <unistd.h>
|
---|
| 90 | # include <errno.h>
|
---|
| 91 | # include <time.h>
|
---|
| 92 | # include <sys/time.h>
|
---|
| 93 | #endif
|
---|
[3655] | 94 |
|
---|
[76419] | 95 | #include <iprt/assert.h>
|
---|
[3655] | 96 | #include <iprt/string.h>
|
---|
| 97 | #include <iprt/semaphore.h>
|
---|
| 98 | #include <iprt/time.h>
|
---|
[76419] | 99 | #include <iprt/thread.h>
|
---|
| 100 | #include <VBox/err.h>
|
---|
[21218] | 101 | #include <VBox/VBoxGuestLib.h>
|
---|
[3655] | 102 | #include "VBoxServiceInternal.h"
|
---|
[25390] | 103 | #include "VBoxServiceUtils.h"
|
---|
[3655] | 104 |
|
---|
| 105 |
|
---|
[57358] | 106 | /*********************************************************************************************************************************
|
---|
| 107 | * Global Variables *
|
---|
| 108 | *********************************************************************************************************************************/
|
---|
[33540] | 109 | /** The timesync interval (milliseconds). */
|
---|
[58029] | 110 | static uint32_t g_TimeSyncInterval = 0;
|
---|
[3655] | 111 | /**
|
---|
[64300] | 112 | * @see pg_vgsvc_timesync
|
---|
[3725] | 113 | *
|
---|
| 114 | * @remark OS/2: There is either a 1 second resolution on the DosSetDateTime
|
---|
[58029] | 115 | * API or a bug in my settimeofday implementation. Thus, don't
|
---|
[3655] | 116 | * bother unless there is at least a 1 second drift.
|
---|
| 117 | */
|
---|
[3725] | 118 | #ifdef RT_OS_OS2
|
---|
[64300] | 119 | static uint32_t g_cMsTimeSyncMinAdjust = 1000;
|
---|
[3655] | 120 | #else
|
---|
[64300] | 121 | static uint32_t g_cMsTimeSyncMinAdjust = 100;
|
---|
[3725] | 122 | #endif
|
---|
[64300] | 123 | /** @see pg_vgsvc_timesync */
|
---|
[58029] | 124 | static uint32_t g_TimeSyncLatencyFactor = 8;
|
---|
[64300] | 125 | /** @see pg_vgsvc_timesync */
|
---|
| 126 | static uint32_t g_cMsTimeSyncMaxLatency = 250;
|
---|
| 127 | /** @see pg_vgsvc_timesync */
|
---|
[58029] | 128 | static uint32_t g_TimeSyncSetThreshold = 20*60*1000;
|
---|
[21160] | 129 | /** Whether the next adjustment should just set the time instead of trying to
|
---|
[75559] | 130 | * adjust it. This is used to implement --timesync-set-start.
|
---|
| 131 | * For purposes of setting the kernel timezone, OS/2 always starts with this. */
|
---|
| 132 | #ifdef RT_OS_OS2
|
---|
| 133 | static bool volatile g_fTimeSyncSetOnStart = true;
|
---|
| 134 | #else
|
---|
| 135 | static bool volatile g_fTimeSyncSetOnStart = false;
|
---|
| 136 | #endif
|
---|
[32643] | 137 | /** Whether to set the time when the VM was restored. */
|
---|
[58029] | 138 | static bool g_fTimeSyncSetOnRestore = true;
|
---|
[69993] | 139 | /** The logging verbosity level.
|
---|
[69987] | 140 | * This uses the global verbosity level by default. */
|
---|
[69993] | 141 | static uint32_t g_cTimeSyncVerbosity = 0;
|
---|
[3655] | 142 |
|
---|
[70072] | 143 | /** Current error count. Used to decide when to bitch and when not to. */
|
---|
[58029] | 144 | static uint32_t g_cTimeSyncErrors = 0;
|
---|
[21160] | 145 |
|
---|
[3655] | 146 | /** The semaphore we're blocking on. */
|
---|
[58029] | 147 | static RTSEMEVENTMULTI g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
|
---|
[3655] | 148 |
|
---|
[32643] | 149 | /** The VM session ID. Changes whenever the VM is restored or reset. */
|
---|
[58029] | 150 | static uint64_t g_idTimeSyncSession;
|
---|
[32643] | 151 |
|
---|
[18712] | 152 | #ifdef RT_OS_WINDOWS
|
---|
| 153 | /** Process token. */
|
---|
[58029] | 154 | static HANDLE g_hTokenProcess = NULL;
|
---|
[18712] | 155 | /** Old token privileges. */
|
---|
[19084] | 156 | static TOKEN_PRIVILEGES g_TkOldPrivileges;
|
---|
| 157 | /** Backup values for time adjustment. */
|
---|
[58029] | 158 | static DWORD g_dwWinTimeAdjustment;
|
---|
| 159 | static DWORD g_dwWinTimeIncrement;
|
---|
| 160 | static BOOL g_bWinTimeAdjustmentDisabled;
|
---|
[18712] | 161 | #endif
|
---|
| 162 |
|
---|
| 163 |
|
---|
[58029] | 164 | /**
|
---|
| 165 | * @interface_method_impl{VBOXSERVICE,pfnPreInit}
|
---|
| 166 | */
|
---|
| 167 | static DECLCALLBACK(int) vgsvcTimeSyncPreInit(void)
|
---|
[3655] | 168 | {
|
---|
[69987] | 169 | /* Use global verbosity as default. */
|
---|
[69993] | 170 | g_cTimeSyncVerbosity = g_cVerbosity;
|
---|
[69987] | 171 |
|
---|
[25390] | 172 | #ifdef VBOX_WITH_GUEST_PROPS
|
---|
[58029] | 173 | /** @todo Merge this function with vgsvcTimeSyncOption() to generalize
|
---|
[25407] | 174 | * the "command line args override guest property values" behavior. */
|
---|
[25390] | 175 |
|
---|
| 176 | /*
|
---|
| 177 | * Read the service options from the VM's guest properties.
|
---|
[33540] | 178 | * Note that these options can be overridden by the command line options later.
|
---|
[25390] | 179 | */
|
---|
| 180 | uint32_t uGuestPropSvcClientID;
|
---|
| 181 | int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
|
---|
| 182 | if (RT_FAILURE(rc))
|
---|
| 183 | {
|
---|
[36580] | 184 | if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
|
---|
| 185 | {
|
---|
[58029] | 186 | VGSvcVerbose(0, "VMInfo: Guest property service is not available, skipping\n");
|
---|
[36580] | 187 | rc = VINF_SUCCESS;
|
---|
| 188 | }
|
---|
| 189 | else
|
---|
[58029] | 190 | VGSvcError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
|
---|
[25390] | 191 | }
|
---|
| 192 | else
|
---|
| 193 | {
|
---|
[58029] | 194 | rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-interval",
|
---|
| 195 | &g_TimeSyncInterval, 50, UINT32_MAX - 1);
|
---|
[25390] | 196 | if ( RT_SUCCESS(rc)
|
---|
| 197 | || rc == VERR_NOT_FOUND)
|
---|
[58029] | 198 | rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-min-adjust",
|
---|
[64300] | 199 | &g_cMsTimeSyncMinAdjust, 0, 3600000);
|
---|
[25390] | 200 | if ( RT_SUCCESS(rc)
|
---|
| 201 | || rc == VERR_NOT_FOUND)
|
---|
[58029] | 202 | rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-latency-factor",
|
---|
| 203 | &g_TimeSyncLatencyFactor, 1, 1024);
|
---|
[25390] | 204 | if ( RT_SUCCESS(rc)
|
---|
| 205 | || rc == VERR_NOT_FOUND)
|
---|
[58029] | 206 | rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-max-latency",
|
---|
[64300] | 207 | &g_cMsTimeSyncMaxLatency, 1, 3600000);
|
---|
[25390] | 208 | if ( RT_SUCCESS(rc)
|
---|
| 209 | || rc == VERR_NOT_FOUND)
|
---|
[58029] | 210 | rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold",
|
---|
| 211 | &g_TimeSyncSetThreshold, 0, 7*24*60*60*1000 /* a week */);
|
---|
[25390] | 212 | if ( RT_SUCCESS(rc)
|
---|
| 213 | || rc == VERR_NOT_FOUND)
|
---|
| 214 | {
|
---|
[69991] | 215 | rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-start");
|
---|
[25390] | 216 | if (RT_SUCCESS(rc))
|
---|
[75559] | 217 | g_fTimeSyncSetOnStart = true;
|
---|
[25390] | 218 | }
|
---|
[32643] | 219 | if ( RT_SUCCESS(rc)
|
---|
| 220 | || rc == VERR_NOT_FOUND)
|
---|
| 221 | {
|
---|
[75559] | 222 | rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-no-set-start");
|
---|
| 223 | if (RT_SUCCESS(rc))
|
---|
| 224 | g_fTimeSyncSetOnStart = false;
|
---|
| 225 | }
|
---|
| 226 | if ( RT_SUCCESS(rc)
|
---|
| 227 | || rc == VERR_NOT_FOUND)
|
---|
| 228 | {
|
---|
[69991] | 229 | rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-on-restore");
|
---|
[32643] | 230 | if (RT_SUCCESS(rc))
|
---|
[69991] | 231 | g_fTimeSyncSetOnRestore = true;
|
---|
[32643] | 232 | }
|
---|
[69987] | 233 | if ( RT_SUCCESS(rc)
|
---|
| 234 | || rc == VERR_NOT_FOUND)
|
---|
| 235 | {
|
---|
[69991] | 236 | rc = VGSvcCheckPropExist(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-no-set-on-restore");
|
---|
| 237 | if (RT_SUCCESS(rc))
|
---|
| 238 | g_fTimeSyncSetOnRestore = false;
|
---|
| 239 | }
|
---|
| 240 | if ( RT_SUCCESS(rc)
|
---|
| 241 | || rc == VERR_NOT_FOUND)
|
---|
| 242 | {
|
---|
[69990] | 243 | uint32_t uValue;
|
---|
[69987] | 244 | rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-verbosity",
|
---|
[69990] | 245 | &uValue, 0 /*uMin*/, 255 /*uMax*/);
|
---|
[69987] | 246 | if (RT_SUCCESS(rc))
|
---|
[69993] | 247 | g_cTimeSyncVerbosity = uValue;
|
---|
[69987] | 248 | }
|
---|
[25390] | 249 | VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 | if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
|
---|
| 253 | rc = VINF_SUCCESS;
|
---|
| 254 | return rc;
|
---|
| 255 | #else
|
---|
| 256 | /* Nothing to do here yet. */
|
---|
[3655] | 257 | return VINF_SUCCESS;
|
---|
[25390] | 258 | #endif
|
---|
[3655] | 259 | }
|
---|
| 260 |
|
---|
| 261 |
|
---|
[58029] | 262 | /**
|
---|
[69987] | 263 | * Displays a verbose message based on the currently
|
---|
| 264 | * set timesync verbosity level.
|
---|
| 265 | *
|
---|
| 266 | * @param iLevel Minimum log level required to display this message.
|
---|
| 267 | * @param pszFormat The message text.
|
---|
| 268 | * @param ... Format arguments.
|
---|
| 269 | */
|
---|
| 270 | static void vgsvcTimeSyncLog(unsigned iLevel, const char *pszFormat, ...)
|
---|
| 271 | {
|
---|
[69993] | 272 | if (iLevel <= g_cTimeSyncVerbosity)
|
---|
[69987] | 273 | {
|
---|
[69997] | 274 | va_list va;
|
---|
| 275 | va_start(va, pszFormat);
|
---|
| 276 | VGSvcLogV(pszFormat, va);
|
---|
| 277 | va_end(va);
|
---|
[69987] | 278 | }
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 |
|
---|
| 282 | /**
|
---|
[58029] | 283 | * @interface_method_impl{VBOXSERVICE,pfnOption}
|
---|
| 284 | */
|
---|
| 285 | static DECLCALLBACK(int) vgsvcTimeSyncOption(const char **ppszShort, int argc, char **argv, int *pi)
|
---|
[3655] | 286 | {
|
---|
[69991] | 287 | int rc = VINF_SUCCESS;
|
---|
[3655] | 288 | if (ppszShort)
|
---|
[69991] | 289 | rc = -1 ;/* no short options */
|
---|
[3655] | 290 | else if (!strcmp(argv[*pi], "--timesync-interval"))
|
---|
[58029] | 291 | rc = VGSvcArgUInt32(argc, argv, "", pi, &g_TimeSyncInterval, 50, UINT32_MAX - 1);
|
---|
[3655] | 292 | else if (!strcmp(argv[*pi], "--timesync-min-adjust"))
|
---|
[64300] | 293 | rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cMsTimeSyncMinAdjust, 0, 3600000);
|
---|
[3655] | 294 | else if (!strcmp(argv[*pi], "--timesync-latency-factor"))
|
---|
[58029] | 295 | rc = VGSvcArgUInt32(argc, argv, "", pi, &g_TimeSyncLatencyFactor, 1, 1024);
|
---|
[3655] | 296 | else if (!strcmp(argv[*pi], "--timesync-max-latency"))
|
---|
[64300] | 297 | rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cMsTimeSyncMaxLatency, 1, 3600000);
|
---|
[21160] | 298 | else if (!strcmp(argv[*pi], "--timesync-set-threshold"))
|
---|
[58029] | 299 | rc = VGSvcArgUInt32(argc, argv, "", pi, &g_TimeSyncSetThreshold, 0, 7*24*60*60*1000); /* a week */
|
---|
[21160] | 300 | else if (!strcmp(argv[*pi], "--timesync-set-start"))
|
---|
[75559] | 301 | g_fTimeSyncSetOnStart = true;
|
---|
| 302 | else if (!strcmp(argv[*pi], "--timesync-no-set-start"))
|
---|
| 303 | g_fTimeSyncSetOnStart = false;
|
---|
[32644] | 304 | else if (!strcmp(argv[*pi], "--timesync-set-on-restore"))
|
---|
[69991] | 305 | g_fTimeSyncSetOnRestore = true;
|
---|
| 306 | else if (!strcmp(argv[*pi], "--timesync-no-set-on-restore"))
|
---|
| 307 | g_fTimeSyncSetOnRestore = false;
|
---|
[69993] | 308 | else if (!strcmp(argv[*pi], "--timesync-verbosity"))
|
---|
| 309 | rc = VGSvcArgUInt32(argc, argv, "", pi, &g_cTimeSyncVerbosity, 0 /*uMin*/, 255 /*uMax*/);
|
---|
[69991] | 310 | else
|
---|
| 311 | rc = -1;
|
---|
[21160] | 312 |
|
---|
[3655] | 313 | return rc;
|
---|
| 314 | }
|
---|
| 315 |
|
---|
| 316 |
|
---|
[58029] | 317 | /**
|
---|
| 318 | * @interface_method_impl{VBOXSERVICE,pfnInit}
|
---|
| 319 | */
|
---|
| 320 | static DECLCALLBACK(int) vgsvcTimeSyncInit(void)
|
---|
[3655] | 321 | {
|
---|
[3725] | 322 | /*
|
---|
[3655] | 323 | * If not specified, find the right interval default.
|
---|
| 324 | * Then create the event sem to block on.
|
---|
| 325 | */
|
---|
| 326 | if (!g_TimeSyncInterval)
|
---|
| 327 | g_TimeSyncInterval = g_DefaultInterval * 1000;
|
---|
| 328 | if (!g_TimeSyncInterval)
|
---|
| 329 | g_TimeSyncInterval = 10 * 1000;
|
---|
| 330 |
|
---|
[32643] | 331 | VbglR3GetSessionId(&g_idTimeSyncSession);
|
---|
| 332 | /* The status code is ignored as this information is not available with VBox < 3.2.10. */
|
---|
| 333 |
|
---|
[18712] | 334 | int rc = RTSemEventMultiCreate(&g_TimeSyncEvent);
|
---|
[3655] | 335 | AssertRC(rc);
|
---|
[18712] | 336 | #ifdef RT_OS_WINDOWS
|
---|
| 337 | if (RT_SUCCESS(rc))
|
---|
[18673] | 338 | {
|
---|
[18712] | 339 | /*
|
---|
[33540] | 340 | * Adjust privileges of this process so we can make system time adjustments.
|
---|
[18712] | 341 | */
|
---|
| 342 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &g_hTokenProcess))
|
---|
[18673] | 343 | {
|
---|
[19031] | 344 | TOKEN_PRIVILEGES tkPriv;
|
---|
| 345 | RT_ZERO(tkPriv);
|
---|
| 346 | tkPriv.PrivilegeCount = 1;
|
---|
| 347 | tkPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
---|
| 348 | if (LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkPriv.Privileges[0].Luid))
|
---|
[18712] | 349 | {
|
---|
[19084] | 350 | DWORD cbRet = sizeof(g_TkOldPrivileges);
|
---|
[21157] | 351 | if (AdjustTokenPrivileges(g_hTokenProcess, FALSE, &tkPriv, sizeof(TOKEN_PRIVILEGES), &g_TkOldPrivileges, &cbRet))
|
---|
| 352 | rc = VINF_SUCCESS;
|
---|
| 353 | else
|
---|
[18712] | 354 | {
|
---|
| 355 | DWORD dwErr = GetLastError();
|
---|
| 356 | rc = RTErrConvertFromWin32(dwErr);
|
---|
[58029] | 357 | VGSvcError("vgsvcTimeSyncInit: Adjusting token privileges (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
|
---|
| 358 | dwErr, rc);
|
---|
[18712] | 359 | }
|
---|
| 360 | }
|
---|
| 361 | else
|
---|
| 362 | {
|
---|
| 363 | DWORD dwErr = GetLastError();
|
---|
| 364 | rc = RTErrConvertFromWin32(dwErr);
|
---|
[58029] | 365 | VGSvcError("vgsvcTimeSyncInit: Looking up token privileges (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
|
---|
| 366 | dwErr, rc);
|
---|
[18712] | 367 | }
|
---|
| 368 | if (RT_FAILURE(rc))
|
---|
| 369 | {
|
---|
| 370 | CloseHandle(g_hTokenProcess);
|
---|
| 371 | g_hTokenProcess = NULL;
|
---|
| 372 | }
|
---|
[18673] | 373 | }
|
---|
[18712] | 374 | else
|
---|
[18673] | 375 | {
|
---|
[18712] | 376 | DWORD dwErr = GetLastError();
|
---|
| 377 | rc = RTErrConvertFromWin32(dwErr);
|
---|
[58029] | 378 | VGSvcError("vgsvcTimeSyncInit: Opening process token (SE_SYSTEMTIME_NAME) failed with status code %u/%Rrc!\n",
|
---|
| 379 | dwErr, rc);
|
---|
[18712] | 380 | g_hTokenProcess = NULL;
|
---|
[18673] | 381 | }
|
---|
| 382 | }
|
---|
[19084] | 383 |
|
---|
[70346] | 384 | if (g_pfnGetSystemTimeAdjustment)
|
---|
[19084] | 385 | {
|
---|
[70346] | 386 | if (g_pfnGetSystemTimeAdjustment(&g_dwWinTimeAdjustment, &g_dwWinTimeIncrement, &g_bWinTimeAdjustmentDisabled))
|
---|
| 387 | vgsvcTimeSyncLog(0, "vgsvcTimeSyncInit: Initially %ld (100ns) units per %ld (100 ns) units interval, disabled=%d\n",
|
---|
| 388 | g_dwWinTimeAdjustment, g_dwWinTimeIncrement, g_bWinTimeAdjustmentDisabled ? 1 : 0);
|
---|
| 389 | else
|
---|
| 390 | {
|
---|
| 391 | DWORD dwErr = GetLastError();
|
---|
| 392 | rc = RTErrConvertFromWin32(dwErr);
|
---|
| 393 | VGSvcError("vgsvcTimeSyncInit: Could not get time adjustment values! Last error: %ld!\n", dwErr);
|
---|
| 394 | }
|
---|
[19084] | 395 | }
|
---|
[18712] | 396 | #endif /* RT_OS_WINDOWS */
|
---|
[18673] | 397 |
|
---|
[3655] | 398 | return rc;
|
---|
| 399 | }
|
---|
| 400 |
|
---|
| 401 |
|
---|
[21160] | 402 | /**
|
---|
[70072] | 403 | * Try adjusting the time using adjtime or similar.
|
---|
[21160] | 404 | *
|
---|
| 405 | * @returns true on success, false on failure.
|
---|
| 406 | *
|
---|
| 407 | * @param pDrift The time adjustment.
|
---|
| 408 | */
|
---|
[58029] | 409 | static bool vgsvcTimeSyncAdjust(PCRTTIMESPEC pDrift)
|
---|
[21160] | 410 | {
|
---|
| 411 | #ifdef RT_OS_WINDOWS
|
---|
[25563] | 412 | /** @todo r=bird: g_hTokenProcess cannot be NULL here.
|
---|
[58029] | 413 | * vgsvcTimeSyncInit will fail and the service will not be started with
|
---|
| 414 | * it being NULL. vgsvcTimeSyncInit OTOH will *NOT* be called until the
|
---|
| 415 | * service thread has terminated. If anything
|
---|
[25563] | 416 | * else is the case, there is buggy code somewhere.*/
|
---|
[25159] | 417 | if (g_hTokenProcess == NULL) /* Is the token already closed when shutting down? */
|
---|
| 418 | return false;
|
---|
| 419 |
|
---|
[70346] | 420 | /* The API appeared in NT 3.50. */
|
---|
| 421 | if ( !g_pfnSetSystemTimeAdjustment
|
---|
| 422 | || !g_pfnGetSystemTimeAdjustment)
|
---|
| 423 | return false;
|
---|
| 424 |
|
---|
[21160] | 425 | DWORD dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwWinTimeIncrement;
|
---|
| 426 | BOOL fWinTimeAdjustmentDisabled;
|
---|
[70346] | 427 | if (g_pfnGetSystemTimeAdjustment(&dwWinTimeAdjustment, &dwWinTimeIncrement, &fWinTimeAdjustmentDisabled))
|
---|
[21160] | 428 | {
|
---|
| 429 | DWORD dwDiffMax = g_dwWinTimeAdjustment * 0.50;
|
---|
| 430 | DWORD dwDiffNew = dwWinTimeAdjustment * 0.10;
|
---|
| 431 |
|
---|
| 432 | if (RTTimeSpecGetMilli(pDrift) > 0)
|
---|
| 433 | {
|
---|
| 434 | dwWinNewTimeAdjustment = dwWinTimeAdjustment + dwDiffNew;
|
---|
| 435 | if (dwWinNewTimeAdjustment > (g_dwWinTimeAdjustment + dwDiffMax))
|
---|
| 436 | {
|
---|
| 437 | dwWinNewTimeAdjustment = g_dwWinTimeAdjustment + dwDiffMax;
|
---|
| 438 | dwDiffNew = dwDiffMax;
|
---|
| 439 | }
|
---|
| 440 | }
|
---|
| 441 | else
|
---|
| 442 | {
|
---|
| 443 | dwWinNewTimeAdjustment = dwWinTimeAdjustment - dwDiffNew;
|
---|
| 444 | if (dwWinNewTimeAdjustment < (g_dwWinTimeAdjustment - dwDiffMax))
|
---|
| 445 | {
|
---|
| 446 | dwWinNewTimeAdjustment = g_dwWinTimeAdjustment - dwDiffMax;
|
---|
| 447 | dwDiffNew = dwDiffMax;
|
---|
| 448 | }
|
---|
| 449 | }
|
---|
| 450 |
|
---|
[69987] | 451 | vgsvcTimeSyncLog(3, "vgsvcTimeSyncAdjust: Drift=%lldms\n", RTTimeSpecGetMilli(pDrift));
|
---|
| 452 | vgsvcTimeSyncLog(3, "vgsvcTimeSyncAdjust: OrgTA=%ld, CurTA=%ld, NewTA=%ld, DiffNew=%ld, DiffMax=%ld\n",
|
---|
| 453 | g_dwWinTimeAdjustment, dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwDiffNew, dwDiffMax);
|
---|
[70346] | 454 | if (g_pfnSetSystemTimeAdjustment(dwWinNewTimeAdjustment, FALSE /* Periodic adjustments enabled. */))
|
---|
[21160] | 455 | {
|
---|
| 456 | g_cTimeSyncErrors = 0;
|
---|
| 457 | return true;
|
---|
| 458 | }
|
---|
| 459 |
|
---|
| 460 | if (g_cTimeSyncErrors++ < 10)
|
---|
[58029] | 461 | VGSvcError("vgsvcTimeSyncAdjust: SetSystemTimeAdjustment failed, error=%u\n", GetLastError());
|
---|
[21160] | 462 | }
|
---|
| 463 | else if (g_cTimeSyncErrors++ < 10)
|
---|
[58029] | 464 | VGSvcError("vgsvcTimeSyncAdjust: GetSystemTimeAdjustment failed, error=%ld\n", GetLastError());
|
---|
[21160] | 465 |
|
---|
[43363] | 466 | #elif defined(RT_OS_OS2) || defined(RT_OS_HAIKU)
|
---|
[21160] | 467 | /* No API for doing gradual time adjustments. */
|
---|
| 468 |
|
---|
| 469 | #else /* PORTME */
|
---|
| 470 | /*
|
---|
[70072] | 471 | * Try using adjtime(), most unix-like systems have this.
|
---|
[21160] | 472 | */
|
---|
| 473 | struct timeval tv;
|
---|
| 474 | RTTimeSpecGetTimeval(pDrift, &tv);
|
---|
| 475 | if (adjtime(&tv, NULL) == 0)
|
---|
| 476 | {
|
---|
[70008] | 477 | vgsvcTimeSyncLog(1, "vgsvcTimeSyncAdjust: adjtime by %RDtimespec\n", pDrift);
|
---|
[21160] | 478 | g_cTimeSyncErrors = 0;
|
---|
| 479 | return true;
|
---|
| 480 | }
|
---|
| 481 | #endif
|
---|
| 482 |
|
---|
| 483 | /* failed */
|
---|
| 484 | return false;
|
---|
| 485 | }
|
---|
| 486 |
|
---|
| 487 |
|
---|
| 488 | /**
|
---|
| 489 | * Cancels any pending time adjustment.
|
---|
| 490 | *
|
---|
[58029] | 491 | * Called when we've caught up and before calls to vgsvcTimeSyncSet.
|
---|
[21160] | 492 | */
|
---|
[58029] | 493 | static void vgsvcTimeSyncCancelAdjust(void)
|
---|
[21160] | 494 | {
|
---|
| 495 | #ifdef RT_OS_WINDOWS
|
---|
[25563] | 496 | /** @todo r=bird: g_hTokenProcess cannot be NULL here. See argumentation in
|
---|
[58029] | 497 | * vgsvcTimeSyncAdjust. */
|
---|
[25480] | 498 | if (g_hTokenProcess == NULL) /* No process token (anymore)? */
|
---|
| 499 | return;
|
---|
[70346] | 500 | if (!g_pfnSetSystemTimeAdjustment)
|
---|
| 501 | return;
|
---|
| 502 | if (g_pfnSetSystemTimeAdjustment(0, TRUE /* Periodic adjustments disabled. */))
|
---|
[70012] | 503 | vgsvcTimeSyncLog(5, "vgsvcTimeSyncCancelAdjust: Windows Time Adjustment is now disabled.\n");
|
---|
[21160] | 504 | else if (g_cTimeSyncErrors++ < 10)
|
---|
[58029] | 505 | VGSvcError("vgsvcTimeSyncCancelAdjust: SetSystemTimeAdjustment(,disable) failed, error=%u\n", GetLastError());
|
---|
[21160] | 506 | #endif /* !RT_OS_WINDOWS */
|
---|
| 507 | }
|
---|
| 508 |
|
---|
| 509 |
|
---|
| 510 | /**
|
---|
[70072] | 511 | * Set the wall clock to compensate for drift.
|
---|
[21160] | 512 | *
|
---|
| 513 | * @returns true on success, false on failure.
|
---|
| 514 | *
|
---|
| 515 | * @param pDrift The time adjustment.
|
---|
| 516 | */
|
---|
[58029] | 517 | static void vgsvcTimeSyncSet(PCRTTIMESPEC pDrift)
|
---|
[21160] | 518 | {
|
---|
| 519 | /*
|
---|
[26211] | 520 | * Query the current time, adjust it by adding the drift and set it.
|
---|
[21160] | 521 | */
|
---|
[26211] | 522 | RTTIMESPEC NewGuestTime;
|
---|
| 523 | int rc = RTTimeSet(RTTimeSpecAdd(RTTimeNow(&NewGuestTime), pDrift));
|
---|
| 524 | if (RT_SUCCESS(rc))
|
---|
[21160] | 525 | {
|
---|
[26211] | 526 | /* Succeeded - reset the error count and log the change. */
|
---|
| 527 | g_cTimeSyncErrors = 0;
|
---|
| 528 |
|
---|
[70008] | 529 | if (g_cTimeSyncVerbosity >= 1)
|
---|
[26113] | 530 | {
|
---|
[26211] | 531 | char sz[64];
|
---|
| 532 | RTTIME Time;
|
---|
[69987] | 533 | vgsvcTimeSyncLog(1, "time set to %s\n", RTTimeToString(RTTimeExplode(&Time, &NewGuestTime), sz, sizeof(sz)));
|
---|
[26211] | 534 | #ifdef DEBUG
|
---|
| 535 | RTTIMESPEC Tmp;
|
---|
[70008] | 536 | vgsvcTimeSyncLog(3, " now %s\n", RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&Tmp)), sz, sizeof(sz)));
|
---|
[26211] | 537 | #endif
|
---|
[21160] | 538 | }
|
---|
| 539 | }
|
---|
[26231] | 540 | else if (g_cTimeSyncErrors++ < 10)
|
---|
[58029] | 541 | VGSvcError("vgsvcTimeSyncSet: RTTimeSet(%RDtimespec) failed: %Rrc\n", &NewGuestTime, rc);
|
---|
[21160] | 542 | }
|
---|
| 543 |
|
---|
| 544 |
|
---|
[58029] | 545 | /**
|
---|
| 546 | * @interface_method_impl{VBOXSERVICE,pfnWorker}
|
---|
| 547 | */
|
---|
| 548 | DECLCALLBACK(int) vgsvcTimeSyncWorker(bool volatile *pfShutdown)
|
---|
[3655] | 549 | {
|
---|
| 550 | RTTIME Time;
|
---|
[6709] | 551 | int rc = VINF_SUCCESS;
|
---|
[3655] | 552 |
|
---|
[19321] | 553 | /*
|
---|
[21157] | 554 | * Tell the control thread that it can continue spawning services.
|
---|
[19321] | 555 | */
|
---|
| 556 | RTThreadUserSignal(RTThreadSelf());
|
---|
| 557 |
|
---|
[21157] | 558 | /*
|
---|
[69994] | 559 | * Initialize the last host and guest times to prevent log message.
|
---|
[70072] | 560 | * We also track whether we set the time in the previous loop.
|
---|
[69953] | 561 | */
|
---|
| 562 | RTTIMESPEC HostLast;
|
---|
| 563 | if (RT_FAILURE(VbglR3GetHostTime(&HostLast)))
|
---|
| 564 | RTTimeSpecSetNano(&HostLast, 0);
|
---|
[69994] | 565 | RTTIMESPEC GuestLast;
|
---|
| 566 | RTTimeNow(&GuestLast);
|
---|
| 567 | bool fSetTimeLastLoop = false;
|
---|
[69953] | 568 |
|
---|
| 569 | /*
|
---|
[21157] | 570 | * The Work Loop.
|
---|
| 571 | */
|
---|
[3655] | 572 | for (;;)
|
---|
| 573 | {
|
---|
| 574 | /*
|
---|
[70072] | 575 | * Try to get a reliable time reading.
|
---|
[3655] | 576 | */
|
---|
| 577 | int cTries = 3;
|
---|
| 578 | do
|
---|
| 579 | {
|
---|
[69953] | 580 | /*
|
---|
| 581 | * Query the session id (first to keep lantency low) and the time.
|
---|
| 582 | */
|
---|
| 583 | uint64_t idNewSession = g_idTimeSyncSession;
|
---|
| 584 | if (g_fTimeSyncSetOnRestore)
|
---|
| 585 | VbglR3GetSessionId(&idNewSession);
|
---|
| 586 |
|
---|
| 587 | RTTIMESPEC GuestNow0;
|
---|
[3655] | 588 | RTTimeNow(&GuestNow0);
|
---|
[69953] | 589 |
|
---|
| 590 | RTTIMESPEC HostNow;
|
---|
[3655] | 591 | int rc2 = VbglR3GetHostTime(&HostNow);
|
---|
| 592 | if (RT_FAILURE(rc2))
|
---|
| 593 | {
|
---|
[21160] | 594 | if (g_cTimeSyncErrors++ < 10)
|
---|
[58029] | 595 | VGSvcError("vgsvcTimeSyncWorker: VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
|
---|
[3655] | 596 | break;
|
---|
| 597 | }
|
---|
[69953] | 598 |
|
---|
| 599 | RTTIMESPEC GuestNow;
|
---|
[3655] | 600 | RTTimeNow(&GuestNow);
|
---|
| 601 |
|
---|
[69953] | 602 | /*
|
---|
| 603 | * Calc latency and check if it's ok.
|
---|
| 604 | */
|
---|
[3655] | 605 | RTTIMESPEC GuestElapsed = GuestNow;
|
---|
| 606 | RTTimeSpecSub(&GuestElapsed, &GuestNow0);
|
---|
[64300] | 607 | if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_cMsTimeSyncMaxLatency)
|
---|
[3655] | 608 | {
|
---|
| 609 | /*
|
---|
[69953] | 610 | * If we were just restored, set the adjustment threshold to zero to force a resync.
|
---|
[32643] | 611 | */
|
---|
| 612 | uint32_t TimeSyncSetThreshold = g_TimeSyncSetThreshold;
|
---|
[69953] | 613 | if ( g_fTimeSyncSetOnRestore
|
---|
| 614 | && idNewSession != g_idTimeSyncSession)
|
---|
[32643] | 615 | {
|
---|
[70008] | 616 | vgsvcTimeSyncLog(2, "vgsvcTimeSyncWorker: The VM session ID changed, forcing resync.\n");
|
---|
[69953] | 617 | g_idTimeSyncSession = idNewSession;
|
---|
| 618 | TimeSyncSetThreshold = 0;
|
---|
[32643] | 619 | }
|
---|
| 620 |
|
---|
| 621 | /*
|
---|
[3655] | 622 | * Calculate the adjustment threshold and the current drift.
|
---|
| 623 | */
|
---|
| 624 | uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
|
---|
[64300] | 625 | if (MinAdjust < g_cMsTimeSyncMinAdjust)
|
---|
| 626 | MinAdjust = g_cMsTimeSyncMinAdjust;
|
---|
[3655] | 627 |
|
---|
| 628 | RTTIMESPEC Drift = HostNow;
|
---|
| 629 | RTTimeSpecSub(&Drift, &GuestNow);
|
---|
| 630 | if (RTTimeSpecGetMilli(&Drift) < 0)
|
---|
[64300] | 631 | MinAdjust += g_cMsTimeSyncMinAdjust; /* extra buffer against moving time backwards. */
|
---|
[3655] | 632 |
|
---|
| 633 | RTTIMESPEC AbsDrift = Drift;
|
---|
| 634 | RTTimeSpecAbsolute(&AbsDrift);
|
---|
[19321] | 635 |
|
---|
[70012] | 636 | if (g_cTimeSyncVerbosity >= 4)
|
---|
| 637 | {
|
---|
[70032] | 638 | char sz1[64];
|
---|
| 639 | char sz2[64];
|
---|
[70025] | 640 | vgsvcTimeSyncLog(4, "vgsvcTimeSyncWorker: Host: %s (MinAdjust: %RU32 ms), Guest: %s => %RDtimespec drift\n",
|
---|
[70032] | 641 | RTTimeToString(RTTimeExplode(&Time, &HostNow), sz1, sizeof(sz1)), MinAdjust,
|
---|
| 642 | RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz2, sizeof(sz2)), &Drift);
|
---|
[70012] | 643 | }
|
---|
[70008] | 644 |
|
---|
[69994] | 645 | bool fSetTimeInThisLoop = false;
|
---|
[69995] | 646 | uint64_t AbsDriftMilli = RTTimeSpecGetMilli(&AbsDrift);
|
---|
[75559] | 647 | if ( AbsDriftMilli > MinAdjust
|
---|
| 648 | || g_fTimeSyncSetOnStart)
|
---|
[3655] | 649 | {
|
---|
| 650 | /*
|
---|
[21160] | 651 | * Ok, the drift is above the threshold.
|
---|
| 652 | *
|
---|
| 653 | * Try a gradual adjustment first, if that fails or the drift is
|
---|
| 654 | * too big, fall back on just setting the time.
|
---|
[3655] | 655 | */
|
---|
[69953] | 656 | if ( AbsDriftMilli > TimeSyncSetThreshold
|
---|
[75559] | 657 | || g_fTimeSyncSetOnStart
|
---|
[69953] | 658 | || !vgsvcTimeSyncAdjust(&Drift))
|
---|
[3655] | 659 | {
|
---|
[58029] | 660 | vgsvcTimeSyncCancelAdjust();
|
---|
| 661 | vgsvcTimeSyncSet(&Drift);
|
---|
[69994] | 662 | fSetTimeInThisLoop = true;
|
---|
[3655] | 663 | }
|
---|
[69953] | 664 |
|
---|
| 665 | /*
|
---|
| 666 | * Log radical host time changes.
|
---|
| 667 | */
|
---|
| 668 | int64_t cNsHostDelta = RTTimeSpecGetNano(&HostNow) - RTTimeSpecGetNano(&HostLast);
|
---|
| 669 | if ((uint64_t)RT_ABS(cNsHostDelta) > RT_NS_1HOUR / 2)
|
---|
[69987] | 670 | vgsvcTimeSyncLog(0, "vgsvcTimeSyncWorker: Radical host time change: %'RI64ns (HostNow=%RDtimespec HostLast=%RDtimespec)\n",
|
---|
| 671 | cNsHostDelta, &HostNow, &HostLast);
|
---|
[3655] | 672 | }
|
---|
[21160] | 673 | else
|
---|
[58029] | 674 | vgsvcTimeSyncCancelAdjust();
|
---|
[69953] | 675 | HostLast = HostNow;
|
---|
[69994] | 676 |
|
---|
| 677 | /*
|
---|
| 678 | * Log radical guest time changes (we could be the cause of these, mind).
|
---|
| 679 | * Note! Right now we don't care about an extra log line after we called
|
---|
| 680 | * vgsvcTimeSyncSet. fSetTimeLastLoop helps show it though.
|
---|
| 681 | */
|
---|
| 682 | int64_t cNsGuestDelta = RTTimeSpecGetNano(&GuestNow) - RTTimeSpecGetNano(&GuestLast);
|
---|
| 683 | if ((uint64_t)RT_ABS(cNsGuestDelta) > RT_NS_1HOUR / 2)
|
---|
| 684 | vgsvcTimeSyncLog(0, "vgsvcTimeSyncWorker: Radical guest time change: %'RI64ns (GuestNow=%RDtimespec GuestLast=%RDtimespec fSetTimeLastLoop=%RTbool)\n",
|
---|
| 685 | cNsGuestDelta, &GuestNow, &GuestLast, fSetTimeLastLoop);
|
---|
| 686 | GuestLast = GuestNow;
|
---|
| 687 | fSetTimeLastLoop = fSetTimeInThisLoop;
|
---|
[3655] | 688 | break;
|
---|
| 689 | }
|
---|
[69987] | 690 | vgsvcTimeSyncLog(3, "vgsvcTimeSyncWorker: %RDtimespec: latency too high (%RDtimespec, max %ums) sleeping 1s\n",
|
---|
| 691 | &GuestNow, &GuestElapsed, g_cMsTimeSyncMaxLatency);
|
---|
[3655] | 692 | RTThreadSleep(1000);
|
---|
| 693 | } while (--cTries > 0);
|
---|
| 694 |
|
---|
[21160] | 695 | /* Clear the set-next/set-start flag. */
|
---|
[75559] | 696 | g_fTimeSyncSetOnStart = false;
|
---|
[21160] | 697 |
|
---|
[3655] | 698 | /*
|
---|
| 699 | * Block for a while.
|
---|
| 700 | *
|
---|
| 701 | * The event semaphore takes care of ignoring interruptions and it
|
---|
| 702 | * allows us to implement service wakeup later.
|
---|
| 703 | */
|
---|
| 704 | if (*pfShutdown)
|
---|
| 705 | break;
|
---|
| 706 | int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval);
|
---|
| 707 | if (*pfShutdown)
|
---|
| 708 | break;
|
---|
| 709 | if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
|
---|
| 710 | {
|
---|
[58029] | 711 | VGSvcError("vgsvcTimeSyncWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
|
---|
[3655] | 712 | rc = rc2;
|
---|
| 713 | break;
|
---|
| 714 | }
|
---|
| 715 | }
|
---|
| 716 |
|
---|
[58029] | 717 | vgsvcTimeSyncCancelAdjust();
|
---|
[3655] | 718 | return rc;
|
---|
| 719 | }
|
---|
| 720 |
|
---|
| 721 |
|
---|
[58029] | 722 | /**
|
---|
| 723 | * @interface_method_impl{VBOXSERVICE,pfnStop}
|
---|
| 724 | */
|
---|
| 725 | static DECLCALLBACK(void) vgsvcTimeSyncStop(void)
|
---|
[3655] | 726 | {
|
---|
[68557] | 727 | if (g_TimeSyncEvent != NIL_RTSEMEVENTMULTI)
|
---|
| 728 | RTSemEventMultiSignal(g_TimeSyncEvent);
|
---|
[3655] | 729 | }
|
---|
| 730 |
|
---|
| 731 |
|
---|
[58029] | 732 | /**
|
---|
| 733 | * @interface_method_impl{VBOXSERVICE,pfnTerm}
|
---|
| 734 | */
|
---|
| 735 | static DECLCALLBACK(void) vgsvcTimeSyncTerm(void)
|
---|
[3655] | 736 | {
|
---|
[18712] | 737 | #ifdef RT_OS_WINDOWS
|
---|
| 738 | /*
|
---|
| 739 | * Restore the SE_SYSTEMTIME_NAME token privileges (if init succeeded).
|
---|
| 740 | */
|
---|
| 741 | if (g_hTokenProcess)
|
---|
| 742 | {
|
---|
[19084] | 743 | if (!AdjustTokenPrivileges(g_hTokenProcess, FALSE, &g_TkOldPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
|
---|
[18712] | 744 | {
|
---|
| 745 | DWORD dwErr = GetLastError();
|
---|
[58029] | 746 | VGSvcError("vgsvcTimeSyncTerm: Restoring token privileges (SE_SYSTEMTIME_NAME) failed with code %u!\n", dwErr);
|
---|
[18712] | 747 | }
|
---|
| 748 | CloseHandle(g_hTokenProcess);
|
---|
| 749 | g_hTokenProcess = NULL;
|
---|
| 750 | }
|
---|
[19321] | 751 | #endif /* !RT_OS_WINDOWS */
|
---|
[18673] | 752 |
|
---|
[18712] | 753 | if (g_TimeSyncEvent != NIL_RTSEMEVENTMULTI)
|
---|
| 754 | {
|
---|
| 755 | RTSemEventMultiDestroy(g_TimeSyncEvent);
|
---|
| 756 | g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
|
---|
| 757 | }
|
---|
[3655] | 758 | }
|
---|
| 759 |
|
---|
| 760 |
|
---|
| 761 | /**
|
---|
| 762 | * The 'timesync' service description.
|
---|
| 763 | */
|
---|
[3725] | 764 | VBOXSERVICE g_TimeSync =
|
---|
[3655] | 765 | {
|
---|
| 766 | /* pszName. */
|
---|
| 767 | "timesync",
|
---|
| 768 | /* pszDescription. */
|
---|
| 769 | "Time synchronization",
|
---|
| 770 | /* pszUsage. */
|
---|
[29594] | 771 | " [--timesync-interval <ms>] [--timesync-min-adjust <ms>]\n"
|
---|
| 772 | " [--timesync-latency-factor <x>] [--timesync-max-latency <ms>]\n"
|
---|
[75559] | 773 | " [--timesync-set-threshold <ms>]\n"
|
---|
| 774 | " [--timesync-set-start|--timesync-no-set-start]\n"
|
---|
[69993] | 775 | " [--timesync-set-on-restore|--timesync-no-set-on-restore]\n"
|
---|
| 776 | " [--timesync-verbosity <level>]"
|
---|
[3655] | 777 | ,
|
---|
| 778 | /* pszOptions. */
|
---|
[29594] | 779 | " --timesync-interval Specifies the interval at which to synchronize the\n"
|
---|
| 780 | " time with the host. The default is 10000 ms.\n"
|
---|
| 781 | " --timesync-min-adjust The minimum absolute drift value measured in\n"
|
---|
| 782 | " milliseconds to make adjustments for.\n"
|
---|
| 783 | " The default is 1000 ms on OS/2 and 100 ms elsewhere.\n"
|
---|
[21163] | 784 | " --timesync-latency-factor\n"
|
---|
[29594] | 785 | " The factor to multiply the time query latency with\n"
|
---|
| 786 | " to calculate the dynamic minimum adjust time.\n"
|
---|
| 787 | " The default is 8 times.\n"
|
---|
| 788 | " --timesync-max-latency The max host timer query latency to accept.\n"
|
---|
| 789 | " The default is 250 ms.\n"
|
---|
[21163] | 790 | " --timesync-set-threshold\n"
|
---|
[29594] | 791 | " The absolute drift threshold, given as milliseconds,\n"
|
---|
| 792 | " where to start setting the time instead of trying to\n"
|
---|
| 793 | " adjust it. The default is 20 min.\n"
|
---|
[75559] | 794 | " --timesync-set-start, --timesync-no-set-start \n"
|
---|
| 795 | " Set the time when starting the time sync service.\n"
|
---|
| 796 | #ifdef RT_OS_OS2
|
---|
| 797 | " Default: --timesync-set-start\n"
|
---|
| 798 | #else
|
---|
| 799 | " Default: --timesync-no-set-start\n"
|
---|
| 800 | #endif
|
---|
[69991] | 801 | " --timesync-set-on-restore, --timesync-no-set-on-restore\n"
|
---|
| 802 | " Whether to immediately set the time when the VM is\n"
|
---|
| 803 | " restored or not. Default: --timesync-set-on-restore\n"
|
---|
[69993] | 804 | " --timesync-verbosity Sets the verbosity level. Defaults to service wide\n"
|
---|
| 805 | " verbosity level.\n"
|
---|
[3655] | 806 | ,
|
---|
| 807 | /* methods */
|
---|
[58029] | 808 | vgsvcTimeSyncPreInit,
|
---|
| 809 | vgsvcTimeSyncOption,
|
---|
| 810 | vgsvcTimeSyncInit,
|
---|
| 811 | vgsvcTimeSyncWorker,
|
---|
| 812 | vgsvcTimeSyncStop,
|
---|
| 813 | vgsvcTimeSyncTerm
|
---|
[3655] | 814 | };
|
---|
| 815 |
|
---|