152
159
Debug("Daemon: Synchronizing time\n");
155
* Get the host OS time & the max lag limit. The GETTIME backdoor command
156
* suffers from a 136-year overflow problem that cannot be corrected
157
* without breaking backwards compatibility with older Tools. Instead, we
158
* introduced the newer BDOOR_CMD_GETTIMEFULL (which is overflow safe), and
159
* we'll try it before falling back on the unsafe BDOOR_CMD_GETTIME.
161
* Note that BDOOR_CMD_GETTIMEFULL will not touch EAX when it succeeds. So
162
* we check for errors by comparing EAX to BDOOR_MAGIC, which was set by the
163
* call to Backdoor() prior to touching the backdoor port.
162
* We need 3 things from the host, and there exist 3 different versions of
163
* the calls (described further below):
165
* 2) maximum time lag allowed (config option), which is a
166
* threshold that keeps the tools from being over eager about
167
* resetting the time when it is only a little bit off.
170
* First 2 versions of the call add interrupt lag to the maximum allowed
171
* time lag, where as in the last call it is returned separately.
173
* Three versions of the call:
175
* - BDOOR_CMD_GETTIME: suffers from a 136-year overflow problem that
176
* cannot be corrected without breaking backwards compatibility with
177
* older Tools. So, we have the newer BDOOR_CMD_GETTIMEFULL, which is
180
* - BDOOR_CMD_GETTIMEFULL: overcomes the problem above.
182
* - BDOOR_CMD_GETTIMEFULL_WITH_LAG: Both BDOOR_CMD_GETTIMEFULL and
183
* BDOOR_CMD_GETTIME returns max lag limit as interrupt lag + the maximum
184
* allowed time lag. BDOOR_CMD_GETTIMEFULL_WITH_LAG separates these two
185
* values. This is helpful when synchronizing time backwards by slewing
188
* We use BDOOR_CMD_GETTIMEFULL_WITH_LAG first and fall back to
189
* BDOOR_CMD_GETTIMEFULL or BDOOR_CMD_GETTIME.
191
* Note that BDOOR_CMD_GETTIMEFULL and BDOOR_CMD_GETTIMEFULL_WITH_LAG will
192
* not touch EAX when it succeeds. So we check for errors by comparing EAX to
193
* BDOOR_MAGIC, which was set by the call to Backdoor() prior to touching the
165
bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL;
196
bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL_WITH_LAG;
167
198
if (bp.out.ax.word == BDOOR_MAGIC) {
168
199
hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
200
interruptLag = bp.out.di.word;
202
Debug("Using BDOOR_CMD_GETTIMEFULL_WITH_LAG\n");
170
Debug("New get time command not supported by current host, attempting "
172
bp.in.cx.halfs.low = BDOOR_CMD_GETTIME;
204
Debug("BDOOR_CMD_GETTIMEFULL_WITH_LAG not supported by current host, attempting "
205
"BDOOR_CMD_GETTIMEFULL\n");
207
bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL;
174
hostSecs = bp.out.ax.word;
209
if (bp.out.ax.word == BDOOR_MAGIC) {
210
hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
212
Debug("BDOOR_CMD_GETTIMEFULL not supported by current host, attempting "
213
"BDOOR_CMD_GETTIME\n");
214
bp.in.cx.halfs.low = BDOOR_CMD_GETTIME;
216
hostSecs = bp.out.ax.word;
176
219
hostUsecs = bp.out.bx.word;
179
* maxTimeLag is computed by the VMX as the sum of two things:
180
* 1) A threshold that keeps the tools from being over eager about
181
* resetting the time when it is only a little bit off.
182
* 2) The current backlog of timer interrupts that still need to be
183
* delivered to the guest.
185
220
maxTimeLag = bp.out.cx.word;
187
222
if (hostSecs <= 0) {
211
244
Debug("Daemon: Guest clock lost %.6f secs; limit=%.2f; "
212
245
"%"FMT64"d secs since last update\n",
213
246
diff / 1000000.0, maxTimeLag / 1000000.0, hostSecs - lastHostSecs);
247
Debug("Daemon: %d, %d, %"FMT64"d, %"FMT64"d, %"FMT64"d.\n",
248
syncOnce, slewCorrection, diff, maxTimeLag, interruptLag);
214
249
lastHostSecs = hostSecs;
218
* Adjust the guest OS time to equal the host OS time if:
219
* 1) The guest OS is behind the host OS by more than maxTimeLag.
220
* 2) The guest OS is ahead of the host OS and syncBackward
221
* is specified. syncBackwards is set to TRUE when the tools
222
* daemon starts and when the timesync feature is toggled from
225
if (diff > maxTimeLag || syncBackward) {
226
if (!System_AddToCurrentTime(diffSecs, diffUsecs)) {
227
Warning("Unable to set the guest OS time: %s.\n\n",
256
* Perform a step correction if:
257
* 1) The guest OS is behind the host OS by more than maxTimeLag + interruptLag.
258
* 2) The guest OS is ahead of the host OS.
260
if (diff > maxTimeLag + interruptLag) {
261
System_DisableTimeSlew();
262
if (!System_AddToCurrentTime(diffSecs, diffUsecs)) {
263
Warning("Unable to set the guest OS time: %s.\n\n", Msg_ErrString());
272
* If guest is behind host by more than maxTimeLag + interruptLag
273
* perform a step correction to the guest clock and ask the monitor
274
* to drop its accumulated catchup (interruptLag).
276
* Otherwise, perform a slew correction. Adjust the guest's clock
277
* rate to be either faster or slower than nominal real time, such
278
* that we expect to correct correctionPercent percent of the error
279
* during this synchronization cycle.
282
if (diff > maxTimeLag + interruptLag) {
283
System_DisableTimeSlew();
284
if (!System_AddToCurrentTime(diffSecs, diffUsecs)) {
285
Warning("Unable to set the guest OS time: %s.\n\n", Msg_ErrString());
288
} else if (slewCorrection && timeLagCall) {
291
/* Don't consider interruptLag during clock slewing. */
292
slewDiff = diff - interruptLag;
294
/* Correct only data->slewPercentCorrection percent error. */
295
slewDiff = (data->slewPercentCorrection * slewDiff) / 100;
297
if (!System_EnableTimeSlew(slewDiff, data->timeSyncPeriod)) {
298
Warning("Unable to slew the guest OS time: %s.\n\n", Msg_ErrString());
302
System_DisableTimeSlew();
233
306
#ifdef VMX86_DEBUG
234
307
System_GetCurrentTime(&secs2, &usecs2);
237
310
secs1, usecs1, secs2, usecs2);
241
* We have just synchronized the guest time to the host time. Ask
242
* VMware to reset to normal the rate of timer interrupts it forwards
243
* from the host to the guest.
314
* If we have stepped the time, ask TimeTracker to reset to normal the rate
315
* of timer interrupts it forwards from the host to the guest.
317
if (!System_IsTimeSlewEnabled()) {
245
318
bp.in.cx.halfs.low = BDOOR_CMD_STOPCATCHUP;
335
408
ToolsDaemonTimeSyncLoop(void *clientData) // IN
337
410
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
342
414
/* The event has fired: it is no longer valid */
343
415
data->timeSyncEvent = NULL;
345
if (!ToolsDaemon_SyncTime(FALSE)) {
417
if (!data->timeSyncPeriod) {
418
data->timeSyncPeriod = TIME_SYNC_TIME;
420
if (!ToolsDaemon_SyncTime(data->slewCorrection, FALSE, clientData)) {
346
421
Warning("Unable to synchronize time.\n\n");
351
period = data->timeSyncPeriod ? data->timeSyncPeriod * 100 : TIME_SYNC_TIME;
352
data->timeSyncEvent = EventManager_Add(ToolsDaemonEventQueue, period,
425
data->timeSyncEvent = EventManager_Add(ToolsDaemonEventQueue, data->timeSyncPeriod,
353
426
ToolsDaemonTimeSyncLoop, data);
354
427
if (data->timeSyncEvent == NULL) {
355
428
Warning("Unable to run the \"time synchronization\" loop.\n\n");
386
459
DWORD timeIncrement;
388
461
BOOL timeAdjustmentDisabled;
389
Bool success = FALSE;
390
/* Below needed for privilege junk. */
393
HANDLE token = INVALID_HANDLE_VALUE;
462
BOOL success = FALSE;
396
465
* We need the SE_SYSTEMTIME_NAME privilege to make the change; get
397
* the privilege now (or bail it if we can't).
466
* the privilege now (or bail if we can't).
399
if (!OpenProcessToken(GetCurrentProcess(),
400
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
402
error = GetLastError();
403
Debug("OpenProcessToken failed: %d", error);
406
if (!LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &luid)) {
407
error = GetLastError();
408
Debug("LookupPrivilegeValue(SE_SYSTEMTIME_NAME) failed: %d", error);
412
memset(&tp, 0, sizeof tp);
413
tp.PrivilegeCount = 1;
414
tp.Privileges[0].Luid = luid;
415
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
416
if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof tp, NULL, NULL)) {
417
error = GetLastError();
418
Debug("AdjustTokenPrivileges failed: %d", error);
468
success = System_SetProcessPrivilege(SE_SYSTEMTIME_NAME, TRUE);
422
473
/* Actually try to stop the time daemon. */
864
914
if (strcmp(name, stateChangeCmdTable[i].tcloCmd) == 0) {
865
915
const char *script;
868
data->stateChgInProgress = (GuestOsState) stateChangeCmdTable[i].id;
917
unsigned int stateId;
919
stateId = stateChangeCmdTable[i].id;
920
data->stateChgInProgress = (GuestOsState)stateId;
922
/* Check for the toolScripts option. */
923
if (!data->toolScriptOption[stateId]) {
924
ToolsDaemonStateChangeDone(TRUE, data);
925
Debug("Script for %s not configured to run\n", stateChangeCmdTable[i].tcloCmd);
926
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
870
929
script = GuestApp_GetDictEntry(*data->pConfDict,
871
stateChgConfNames[stateChangeCmdTable[i].id]);
930
stateChgConfNames[stateId]);
873
932
if (strlen(script) == 0) {
874
933
ToolsDaemonStateChangeDone(TRUE, data);
1139
1201
size_t argsSize, // Ignored
1140
1202
void *clientData) // Ignored
1142
Bool syncBackward = strcmp(args, "1")==0;
1204
Bool slewCorrection = !strcmp(args, "1");
1144
if (!ToolsDaemon_SyncTime(syncBackward)) {
1206
if (!ToolsDaemon_SyncTime(slewCorrection, TRUE, clientData)) {
1145
1207
return RpcIn_SetRetVals(result, resultLen,
1146
1208
"Unable to sync time", FALSE);
1201
1263
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1202
1264
goto invalid_value;
1266
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_SLEWCORRECTION) == 0) {
1267
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1270
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERCENTCORRECTION) == 0) {
1272
if (!StrUtil_StrToInt(&percent, value) || percent == 0 || percent > 100) {
1275
Debug("Daemon: update the slew correction percent.\n");
1204
1276
} else if (strcmp(option, TOOLSOPTION_COPYPASTE) == 0) {
1205
1277
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1206
1278
goto invalid_value;
1235
1307
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1236
1308
goto invalid_value;
1310
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWERON) == 0) {
1311
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1314
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWEROFF) == 0) {
1315
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1318
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_SUSPEND) == 0) {
1319
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1322
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_RESUME) == 0) {
1323
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1326
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_REBOOT) == 0) {
1327
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1239
1331
goto invalid_option;
1250
1342
* Try the one-shot time sync if time sync transitions from
1251
1343
* 'off' to 'on'.
1253
if (oldTimeSyncValue == 0 && start) {
1254
const char *backwardsAllowed;
1256
backwardsAllowed = GuestApp_GetDictEntry(data->optionsDict,
1257
TOOLSOPTION_SYNCTIME_ENABLE);
1259
* Preserve backwards compatibility: VMX may not ever send us this
1260
* option, so value may be NULL.
1262
ToolsDaemon_SyncTime(backwardsAllowed &&
1263
strcmp(backwardsAllowed, "1") == 0);
1345
if (oldTimeSyncValue == 0 && start &&
1346
GuestApp_GetDictEntry(data->optionsDict, TOOLSOPTION_SYNCTIME_ENABLE)) {
1347
ToolsDaemon_SyncTime(data->slewCorrection, TRUE, clientData);
1265
1349
oldTimeSyncValue = start;
1271
1355
retVal = FALSE);
1358
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_SLEWCORRECTION) == 0) {
1359
data->slewCorrection = strcmp(value, "0");
1360
Debug("Daemon: Setting slewCorrection, %d.\n", data->slewCorrection);
1361
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERCENTCORRECTION) == 0) {
1363
if (StrUtil_StrToInt(&percent, value)) {
1364
data->slewPercentCorrection = percent;
1274
1366
} else if (strcmp(option, TOOLSOPTION_BROADCASTIP) == 0 &&
1275
1367
strcmp(value, "1") == 0) {
1345
1437
retVal = FALSE);
1440
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWERON) == 0) {
1441
data->toolScriptOption[GUESTOS_STATECHANGE_POWERON] =
1442
strcmp(value, "0") ? TRUE : FALSE;
1443
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWEROFF) == 0) {
1444
data->toolScriptOption[GUESTOS_STATECHANGE_HALT] =
1445
strcmp(value, "0") ? TRUE : FALSE;
1446
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_SUSPEND) == 0) {
1447
data->toolScriptOption[GUESTOS_STATECHANGE_SUSPEND] =
1448
strcmp(value, "0") ? TRUE : FALSE;
1449
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_RESUME) == 0) {
1450
data->toolScriptOption[GUESTOS_STATECHANGE_RESUME] =
1451
strcmp(value, "0") ? TRUE : FALSE;
1452
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_REBOOT) == 0) {
1453
data->toolScriptOption[GUESTOS_STATECHANGE_REBOOT] =
1454
strcmp(value, "0") ? TRUE : FALSE;