1
/*********************************************************
2
* Copyright (C) 2001 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License as published
6
* by the Free Software Foundation version 2.1 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
11
* License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17
*********************************************************/
23
* Platform independent methods used by the tools daemon.
24
* The tools daemon does the following:
25
* -starts automatically with the guest
26
* -syncs the guest time to the host
27
* -executes scripts on state change requests from the VMX
28
* -listens for other TCLO cmds through the backdoor
43
# include "hgfsUsabilityLib.h"
45
# include "ServiceHelpers.h"
50
#include "toolsDaemon.h"
51
#include "vm_version.h"
54
#include "eventManager.h"
64
#include "backdoor_def.h"
67
#include "hgfsServerManager.h"
69
#include "foundryToolsDaemon.h"
72
#include "guestInfo.h"
75
#include "vm_atomic.h"
77
#include "guestInfoServer.h"
78
#include "syncDriver.h"
79
#endif // #ifndef N_PLAT_NLM
81
#if !defined(__FreeBSD__) && !defined(sun) && !defined(N_PLAT_NLM)
82
#include "deployPkg.h"
85
#ifdef TOOLSDAEMON_HAS_RESOLUTION
86
# include "resolution.h"
89
/* in 1/100 of a second */
90
#define RPCIN_POLL_TIME 10
91
/* sync the time once a minute */
92
#define TIME_SYNC_TIME 6000
93
/* only PERCENT_CORRECTION percent is corrected everytime */
94
#define PERCENT_CORRECTION 50
97
* Table mapping state changes to their conf file names.
100
* Bug 294328: Mac OS guests do not (yet) support the state change RPCs.
103
static const char *stateChgConfNames[] = {
105
CONFNAME_POWEROFFSCRIPT, /* HALT */
106
CONFNAME_POWEROFFSCRIPT, /* REBOOT */
107
CONFNAME_POWERONSCRIPT, /* POWERON */
108
CONFNAME_RESUMESCRIPT, /* RESUME */
109
CONFNAME_SUSPENDSCRIPT, /* SUSPEND */
113
DblLnkLst_Links *ToolsDaemonEventQueue = NULL; // main loop event queue
114
static char *guestTempDirectory = NULL;
116
void ToolsDaemon_InitializeForeignVM(ToolsDaemon_Data *toolsDaemonData);
117
void ToolsDaemon_ShutdownForeignVM(void);
121
*-----------------------------------------------------------------------------
123
* ToolsDaemon_SyncTime --
125
* Set the guest OS time to the host OS time
129
* FALSE on failure (detail is displayed)
134
*-----------------------------------------------------------------------------
138
ToolsDaemon_SyncTime(Bool slewCorrection, // IN: Is clock slewing enabled?
139
Bool syncOnce, // IN: Is this function called in a loop?
140
void *toolsData) // IN: Opaque data
152
ToolsDaemon_Data *data = (ToolsDaemon_Data *) toolsData;
153
Bool timeLagCall = FALSE;
155
static int64 lastHostSecs = 0;
159
System_GetCurrentTime(&secs1, &usecs1);
162
Debug("Daemon: Synchronizing time\n");
165
* We need 3 things from the host, and there exist 3 different versions of
166
* the calls (described further below):
168
* 2) maximum time lag allowed (config option), which is a
169
* threshold that keeps the tools from being over eager about
170
* resetting the time when it is only a little bit off.
173
* First 2 versions of the call add interrupt lag to the maximum allowed
174
* time lag, where as in the last call it is returned separately.
176
* Three versions of the call:
178
* - BDOOR_CMD_GETTIME: suffers from a 136-year overflow problem that
179
* cannot be corrected without breaking backwards compatibility with
180
* older Tools. So, we have the newer BDOOR_CMD_GETTIMEFULL, which is
183
* - BDOOR_CMD_GETTIMEFULL: overcomes the problem above.
185
* - BDOOR_CMD_GETTIMEFULL_WITH_LAG: Both BDOOR_CMD_GETTIMEFULL and
186
* BDOOR_CMD_GETTIME returns max lag limit as interrupt lag + the maximum
187
* allowed time lag. BDOOR_CMD_GETTIMEFULL_WITH_LAG separates these two
188
* values. This is helpful when synchronizing time backwards by slewing
191
* We use BDOOR_CMD_GETTIMEFULL_WITH_LAG first and fall back to
192
* BDOOR_CMD_GETTIMEFULL or BDOOR_CMD_GETTIME.
194
* Note that BDOOR_CMD_GETTIMEFULL and BDOOR_CMD_GETTIMEFULL_WITH_LAG will
195
* not touch EAX when it succeeds. So we check for errors by comparing EAX to
196
* BDOOR_MAGIC, which was set by the call to Backdoor() prior to touching the
199
bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL_WITH_LAG;
201
if (bp.out.ax.word == BDOOR_MAGIC) {
202
hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
203
interruptLag = bp.out.di.word;
205
Debug("Using BDOOR_CMD_GETTIMEFULL_WITH_LAG\n");
207
Debug("BDOOR_CMD_GETTIMEFULL_WITH_LAG not supported by current host, attempting "
208
"BDOOR_CMD_GETTIMEFULL\n");
210
bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL;
212
if (bp.out.ax.word == BDOOR_MAGIC) {
213
hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
215
Debug("BDOOR_CMD_GETTIMEFULL not supported by current host, attempting "
216
"BDOOR_CMD_GETTIME\n");
217
bp.in.cx.halfs.low = BDOOR_CMD_GETTIME;
219
hostSecs = bp.out.ax.word;
222
hostUsecs = bp.out.bx.word;
223
maxTimeLag = bp.out.cx.word;
226
Warning("Invalid host OS time: %"FMT64"d secs, %"FMT64"d usecs.\n\n",
227
hostSecs, hostUsecs);
232
/* Get the guest OS time */
233
if (!System_GetCurrentTime(&guestSecs, &guestUsecs)) {
234
Warning("Unable to retrieve the guest OS time: %s.\n\n", Msg_ErrString());
238
diffSecs = hostSecs - guestSecs;
239
diffUsecs = hostUsecs - guestUsecs;
242
diffUsecs += 1000000U;
244
diff = diffSecs * 1000000L + diffUsecs;
247
Debug("Daemon: Guest clock lost %.6f secs; limit=%.2f; "
248
"%"FMT64"d secs since last update\n",
249
diff / 1000000.0, maxTimeLag / 1000000.0, hostSecs - lastHostSecs);
250
Debug("Daemon: %d, %d, %"FMT64"d, %"FMT64"d, %"FMT64"d.\n",
251
syncOnce, slewCorrection, diff, maxTimeLag, interruptLag);
252
lastHostSecs = hostSecs;
259
* Perform a step correction if:
260
* 1) The guest OS is behind the host OS by more than maxTimeLag + interruptLag.
261
* 2) The guest OS is ahead of the host OS.
263
if (diff > maxTimeLag + interruptLag) {
264
System_DisableTimeSlew();
265
if (!System_AddToCurrentTime(diffSecs, diffUsecs)) {
266
Warning("Unable to set the guest OS time: %s.\n\n", Msg_ErrString());
275
* If guest is behind host by more than maxTimeLag + interruptLag
276
* perform a step correction to the guest clock and ask the monitor
277
* to drop its accumulated catchup (interruptLag).
279
* Otherwise, perform a slew correction. Adjust the guest's clock
280
* rate to be either faster or slower than nominal real time, such
281
* that we expect to correct correctionPercent percent of the error
282
* during this synchronization cycle.
285
if (diff > maxTimeLag + interruptLag) {
286
System_DisableTimeSlew();
287
if (!System_AddToCurrentTime(diffSecs, diffUsecs)) {
288
Warning("Unable to set the guest OS time: %s.\n\n", Msg_ErrString());
291
} else if (slewCorrection && timeLagCall) {
294
/* Don't consider interruptLag during clock slewing. */
295
slewDiff = diff - interruptLag;
297
/* Correct only data->slewPercentCorrection percent error. */
298
slewDiff = (data->slewPercentCorrection * slewDiff) / 100;
300
if (!System_EnableTimeSlew(slewDiff, data->timeSyncPeriod)) {
301
Warning("Unable to slew the guest OS time: %s.\n\n", Msg_ErrString());
305
System_DisableTimeSlew();
310
System_GetCurrentTime(&secs2, &usecs2);
312
Debug("Time changed from %"FMT64"d.%"FMT64"d -> %"FMT64"d.%"FMT64"d\n",
313
secs1, usecs1, secs2, usecs2);
317
* If we have stepped the time, ask TimeTracker to reset to normal the rate
318
* of timer interrupts it forwards from the host to the guest.
320
if (!System_IsTimeSlewEnabled()) {
321
bp.in.cx.halfs.low = BDOOR_CMD_STOPCATCHUP;
330
*-----------------------------------------------------------------------------
332
* ToolsDaemonConfFileLoop --
334
* Run the "conf file reload" loop
338
* FALSE on failure (detail is displayed)
343
*-----------------------------------------------------------------------------
347
ToolsDaemonConfFileLoop(void *clientData) // IN
349
GuestApp_Dict **pConfDict = (GuestApp_Dict **) clientData;
354
* With the addition of the Sync Driver we can get into a state
355
* where the system drive is frozen, preventing the completion of
356
* any disk-based I/O. The event that periodically reloads the conf
357
* file then gets blocked, which blocks the main daemon thread and
358
* prevents any further GuestRPC messages from getting
359
* processed. This effectively deadlocks the tools daemon and among
360
* other things makes it impossible to thaw disk I/O once it's been
363
* So, we keep track of when the disks are frozen and skip doing disk
364
* I/O during that time.
366
#if !defined(N_PLAT_NLM)
367
if (!SyncDriver_DrivesAreFrozen()) {
368
if (Conf_ReloadFile(pConfDict)) {
369
GuestInfoServer_DisableDiskInfoQuery(
370
GuestApp_GetDictEntryBool(*pConfDict, CONFNAME_DISABLEQUERYDISKINFO));
372
Debug_Set(GuestApp_GetDictEntryBool(*pConfDict, CONFNAME_LOG),
374
Debug_EnableToFile(GuestApp_GetDictEntry(*pConfDict, CONFNAME_LOGFILE),
379
if (Conf_ReloadFile(pConfDict)) {
380
Debug_Set(GuestApp_GetDictEntryBool(*pConfDict, CONFNAME_LOG),
382
Debug_EnableToFile(GuestApp_GetDictEntry(*pConfDict, CONFNAME_LOGFILE),
387
EventManager_Add(ToolsDaemonEventQueue, CONF_POLL_TIME, ToolsDaemonConfFileLoop,
394
*-----------------------------------------------------------------------------
396
* ToolsDaemonTimeSyncLoop --
398
* Run the "time synchronization" loop
402
* FALSE on failure (detail is displayed)
407
*-----------------------------------------------------------------------------
411
ToolsDaemonTimeSyncLoop(void *clientData) // IN
413
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
417
/* The event has fired: it is no longer valid */
418
data->timeSyncEvent = NULL;
420
if (!data->timeSyncPeriod) {
421
data->timeSyncPeriod = TIME_SYNC_TIME;
423
if (!ToolsDaemon_SyncTime(data->slewCorrection, FALSE, clientData)) {
424
Warning("Unable to synchronize time.\n\n");
428
data->timeSyncEvent = EventManager_Add(ToolsDaemonEventQueue, data->timeSyncPeriod,
429
ToolsDaemonTimeSyncLoop, data);
430
if (data->timeSyncEvent == NULL) {
431
Warning("Unable to run the \"time synchronization\" loop.\n\n");
441
*-----------------------------------------------------------------------------
443
* ToolsDaemonDisableWinTimeDaemon --
445
* Try to disable the Windows Time Daemon.
454
*-----------------------------------------------------------------------------
459
ToolsDaemonDisableWinTimeDaemon(void)
461
DWORD timeAdjustment;
464
BOOL timeAdjustmentDisabled;
465
BOOL success = FALSE;
468
* We need the SE_SYSTEMTIME_NAME privilege to make the change; get
469
* the privilege now (or bail if we can't).
471
success = System_SetProcessPrivilege(SE_SYSTEMTIME_NAME, TRUE);
476
/* Actually try to stop the time daemon. */
477
if (GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
478
&timeAdjustmentDisabled)) {
479
Debug("GetSystemTimeAdjustment() succeeded: timeAdjustment %d,"
480
"timeIncrement %d, timeAdjustmentDisabled %s\n",
481
timeAdjustment, timeIncrement,
482
timeAdjustmentDisabled ? "TRUE" : "FALSE");
484
* timeAdjustmentDisabled means the opposite of what you'd think;
485
* if it's TRUE, that means the system may be adjusting the time
486
* on its own using the time daemon. Read MSDN for the details,
487
* and see Bug 24173 for more discussion on this.
490
if (timeAdjustmentDisabled) {
492
* MSDN is a bit vague on the semantics of this function, but it
493
* would appear that the timeAdjustment value here is simply the
494
* total amount that the system will add to the clock on each
495
* timer tick, i.e. if you set it to zero the system clock will
496
* not progress at all (and indeed, attempting to set it to zero
497
* results in an ERROR_INVALID_PARAMETER). In order to have time
498
* proceed at the normal rate, this needs to be set to the value
499
* of timeIncrement retrieved from GetSystemTimeAdjustment().
501
if (!SetSystemTimeAdjustment(timeIncrement, FALSE)) {
502
error = GetLastError();
503
Debug("Daemon: SetSystemTimeAdjustment failed: %d\n", error);
508
error = GetLastError();
509
Debug("Daemon: GetSystemTimeAdjustment failed: %d\n", error);
516
Debug("Stopping time daemon %s.\n", success ? "succeeded" : "failed");
517
System_SetProcessPrivilege(SE_SYSTEMTIME_NAME, FALSE);
524
*-----------------------------------------------------------------------------
526
* ToolsDaemonStartStopTimeSyncLoop --
528
* Start or stop the "time synchronization" loop. Nothing will be
529
* done if start==TRUE & it's already running or start=FALSE & it's
534
* FALSE on failure (detail is displayed)
539
*-----------------------------------------------------------------------------
543
ToolsDaemonStartStopTimeSyncLoop(ToolsDaemon_Data *data, // IN
548
if (start && data->timeSyncEvent == NULL) {
549
Debug("Daemon: Starting time sync loop\n");
550
Debug("Daemon: New sync period is %d sec\n", data->timeSyncPeriod);
551
if (!ToolsDaemonTimeSyncLoop(data)) {
556
Debug("Daemon: Attempting to disable Windows Time daemon\n");
557
if (!ToolsDaemonDisableWinTimeDaemon()) {
558
Debug("Daemon: Failed to disable Windows Time daemon\n");
563
} else if (!start && data->timeSyncEvent != NULL) {
564
Debug("Daemon: Stopping time sync loop\n");
565
System_DisableTimeSlew();
566
EventManager_Remove(data->timeSyncEvent);
567
data->timeSyncEvent = NULL;
572
* No need to start time sync b/c it's already running or no
573
* need to stop it b/c it's not running.
581
*-----------------------------------------------------------------------------
583
* ToolsDaemonOldUpdateOptions --
585
* Get the latest value of the tools options from VMware, and update
586
* guestd's behavior according to this new value
587
* (Legacy from before the unified TCLO loop)
591
* FALSE on failure (detail is displayed)
596
*-----------------------------------------------------------------------------
600
ToolsDaemonOldUpdateOptions(ToolsDaemon_Data *data) // IN
609
toolsOptions = GuestApp_OldGetOptions();
611
syncTime = (toolsOptions & VMWARE_GUI_SYNC_TIME) != 0;
612
GuestApp_SetDictEntry(data->optionsDict, TOOLSOPTION_SYNCTIME,
613
syncTime ? "1" : "0");
614
copyPaste = (toolsOptions & VMWARE_GUI_EXCHANGE_SELECTIONS) != 0;
615
GuestApp_SetDictEntry(data->optionsDict, TOOLSOPTION_COPYPASTE,
616
copyPaste ? "1" : "0");
617
autoHide = (toolsOptions & VMWARE_GUI_WARP_CURSOR_ON_UNGRAB) != 0;
618
GuestApp_SetDictEntry(data->optionsDict, TOOLSOPTION_AUTOHIDE,
619
autoHide ? "1" : "0");
621
if (ToolsDaemonStartStopTimeSyncLoop(data, syncTime) == FALSE) {
630
*-----------------------------------------------------------------------------
632
* ToolsDaemonOldUpdateOptionsLoop --
634
* Run the "update options" loop
635
* (Legacy from before the unified TCLO loop)
639
* FALSE on failure (detail is displayed)
644
*-----------------------------------------------------------------------------
648
ToolsDaemonOldUpdateOptionsLoop(void *clientData) // IN
650
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
654
if (ToolsDaemonOldUpdateOptions(data) == FALSE) {
658
data->oldOptionsLoop = EventManager_Add(ToolsDaemonEventQueue, 100,
659
ToolsDaemonOldUpdateOptionsLoop, data);
660
if (data->oldOptionsLoop == NULL) {
661
Warning("Unable to run the \"update options\" loop.\n");
670
*-----------------------------------------------------------------------------
672
* ToolsDaemonStartStopOldUpdateOptionsLoop --
674
* Start or stop the old upate options loop depending
675
* on whether vmware is unified loop capable.
676
* It won't be started again it's already running & it won't be
677
* stopped if it's not running.
681
* FALSE if all attempts to get options have failed.
686
*-----------------------------------------------------------------------------
690
ToolsDaemonStartStopOldUpdateOptionsLoop(ToolsDaemon_Data *data) // IN
697
* Start the old options loop if it's not running & the unified loop is no
698
* supported; stop it if it is running & the unified loop is supported.
700
unifiedLoopCap = GuestApp_GetUnifiedLoopCap(TOOLS_DAEMON_NAME);
701
if (!unifiedLoopCap && data->oldOptionsLoop == NULL) {
702
Debug("Daemon: No unified loop cap; starting old poll loop.\n");
704
if (!ToolsDaemonOldUpdateOptionsLoop(data)) {
707
} else if (unifiedLoopCap && data->oldOptionsLoop != NULL) {
708
Debug("Daemon: Unified loop cap found; stopping old poll loop.\n");
710
EventManager_Remove(data->oldOptionsLoop);
711
data->oldOptionsLoop = NULL;
714
* No need to start the loop b/c it's already running or no
715
* need to stop it b/c it's not running.
724
*-----------------------------------------------------------------------------
726
* ToolsDaemonResetSent --
728
* Called after we've sent the reset TCLO completion to vmware.
735
* Set our version in vmware & start/stop the old options loop.
737
*-----------------------------------------------------------------------------
741
ToolsDaemonResetSent(void *clientData) // IN
743
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
747
#if !defined(N_PLAT_NLM)
748
GuestInfoServer_VMResumedNotify();
751
GuestApp_Log("Version: " BUILD_NUMBER "\n");
753
if (!ToolsDaemonStartStopOldUpdateOptionsLoop(data)) {
754
/* We aren't much use if we can't get the options */
755
Panic("Unable to get options from %s\n", PRODUCT_LINE_NAME);
759
data->resetCB(data->resetCBData);
767
*-----------------------------------------------------------------------------
769
* ToolsDaemonTcloReset --
771
* 'reset' tclo cmd handler. MUST be the first tclo message sent
772
* by VMware when it recognizes that a toolbox app has opened
776
* TRUE on success (*result is empty)
777
* FALSE on failure (*result contains the error)
780
* May start or stop the old update options loop.
782
*-----------------------------------------------------------------------------
786
ToolsDaemonTcloReset(RpcInData *data) // IN/OUT
789
* Mandatory reset RPC
792
Debug("----------Daemon: Received 'reset' from vmware\n");
795
* Schedule the post-reset actions to happen a little after one cycle of the
796
* RpcIn loop. This will give vmware a chance to receive the ATR &
797
* reinitialize the channel if appropriate. [greg]
799
EventManager_Add(ToolsDaemonEventQueue, (int) (RPCIN_POLL_TIME * 1.5),
800
ToolsDaemonResetSent, data->clientData);
802
return RPCIN_SETRETVALS(data, "ATR " TOOLS_DAEMON_NAME, TRUE);
807
*-----------------------------------------------------------------------------
809
* ToolsDaemonStateChangeDone --
811
* Called when a state change script is done running.
812
* Sends the state change status with the script exit value.
818
* May halt/reboot the VM. Also VMware may suspend the VM upon
819
* receipt of a positive status.
821
*-----------------------------------------------------------------------------
825
ToolsDaemonStateChangeDone(Bool status, // IN
828
ToolsDaemon_Data *data = (ToolsDaemon_Data *) cbData;
831
ASSERT(data->rebootCB);
832
ASSERT(data->haltCB);
834
Debug("Daemon: state change callback called\n");
837
* We execute the requested action if the script succeeded, or if the
838
* same action was tried before but didn't finish due to a script failure.
839
* See bug 168568 for discussion.
841
if (status || data->lastFailedStateChg == data->stateChgInProgress) {
844
if (data->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT || data->stateChgInProgress == GUESTOS_STATECHANGE_HALT) {
845
if (Hostinfo_GetOSType() >= OS_VISTA) {
850
if (data->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT) {
851
Debug("Initiating reboot\n");
852
status = data->rebootCB(data->rebootCBData);
853
} else if (data->stateChgInProgress == GUESTOS_STATECHANGE_HALT) {
854
Debug("Initiating halt\n");
855
status = data->haltCB(data->haltCBData);
857
data->lastFailedStateChg = GUESTOS_STATECHANGE_NONE;
861
data->lastFailedStateChg = data->stateChgInProgress;
864
if (!ToolsDaemon_SetOsPhase(status, data->stateChgInProgress)) {
865
Warning("Unable to send the status RPCI");
868
data->stateChgInProgress = GUESTOS_STATECHANGE_NONE;
870
/* Unless the process couldn't be spawned, we need to free it */
871
if (data->asyncProc) {
872
free(data->asyncProc);
873
data->asyncProc = NULL;
879
* Bug 294328: Mac OS guests do not (yet) support the state change RPCs.
883
*-----------------------------------------------------------------------------
885
* ToolsDaemonTcloStateChange --
887
* Tclo cmd handler for commands which invoke state change scripts.
890
* TRUE on success (*result is empty)
891
* FALSE on failure (*result contains the error)
894
* Scripts are invoked in the guest.
896
*-----------------------------------------------------------------------------
900
ToolsDaemonTcloStateChange(char const **result, // OUT
901
size_t *resultLen, // OUT
902
const char *name, // IN
903
const char *args, // IN
904
size_t argsSize, // Ignored
905
void *clientData) // IN
908
ProcMgr_ProcArgs procArgs;
909
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
912
ASSERT(data->pConfDict);
913
ASSERT(*data->pConfDict);
915
Debug("Got state change message\n");
917
if (data->asyncProc != NULL) {
918
Debug("State change already in progress\n");
919
return RpcIn_SetRetVals(result, resultLen,
920
"State change already in progress", FALSE);
923
for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
924
if (strcmp(name, stateChangeCmdTable[i].tcloCmd) == 0) {
927
unsigned int stateId;
929
stateId = stateChangeCmdTable[i].id;
930
data->stateChgInProgress = (GuestOsState)stateId;
932
/* Check for the toolScripts option. */
933
if (!data->toolScriptOption[stateId]) {
934
ToolsDaemonStateChangeDone(TRUE, data);
935
Debug("Script for %s not configured to run\n", stateChangeCmdTable[i].tcloCmd);
936
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
939
script = Util_SafeStrdup(GuestApp_GetDictEntry(*data->pConfDict,
940
stateChgConfNames[stateId]));
941
Debug("Script to execute is: %s\n", script);
942
if (strlen(script) == 0) {
943
ToolsDaemonStateChangeDone(TRUE, data);
944
Debug("No script to run\n");
946
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
947
} else if (!Util_IsAbsolutePath(script)) {
949
char *installDir = GuestApp_GetInstallPath();
950
ASSERT_MEM_ALLOC(installDir);
951
absScript = Str_Asprintf(NULL, "%s%c%s", installDir, DIRSEPC, script);
952
ASSERT_MEM_ALLOC(absScript);
958
scriptCmd = Str_Asprintf(NULL, "%s", script);
959
#elif !defined(_WIN32)
961
ASSERT(data->execLogPath);
962
scriptCmd = Str_Asprintf(NULL, "(%s) 2>&1 >> %s",
963
script, data->execLogPath);
966
* Pass the CREATE_NO_WINDOW flag to CreateProcess so that the
967
* cmd.exe window will not be visible to the user in the guest.
969
memset(&procArgs, 0, sizeof procArgs);
970
procArgs.bInheritHandles = TRUE;
971
procArgs.dwCreationFlags = CREATE_NO_WINDOW;
974
char systemDir[1024 * 3];
975
Win32U_GetSystemDirectory(systemDir, sizeof systemDir);
976
scriptCmd = Str_Asprintf(NULL, "%s\\cmd.exe /c \"%s\"", systemDir, script);
980
if (scriptCmd == NULL) {
981
Debug("Could not format the cmd to run scripts\n");
982
return RpcIn_SetRetVals(result, resultLen,
983
"Could not format cmd to run scritps",
986
data->asyncProc = ProcMgr_ExecAsync(scriptCmd, &procArgs);
988
if (data->asyncProc) {
989
data->asyncProcCb = ToolsDaemonStateChangeDone;
990
data->asyncProcCbData = data;
992
ToolsDaemonStateChangeDone(FALSE, data);
998
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1003
Debug("Error starting script\n");
1004
return RpcIn_SetRetVals(result, resultLen, "Error starting script",
1009
Debug("Invalid state change command\n");
1010
return RpcIn_SetRetVals(result, resultLen, "Invalid state change command",
1013
#endif // ifndef __APPLE__
1017
*-----------------------------------------------------------------------------
1019
* ToolsDaemonTcloCapReg --
1021
* Register our capabilities with the VMX & request
1030
*-----------------------------------------------------------------------------
1034
ToolsDaemonTcloCapReg(char const **result, // OUT
1035
size_t *resultLen, // OUT
1036
const char *name, // IN
1037
const char *args, // IN
1038
size_t argsSize, // Ignored
1039
void *clientData) // IN
1042
unsigned int minResolutionWidth;
1043
unsigned int minResolutionHeight;
1045
ToolsDaemon_Data *data;
1048
data = (ToolsDaemon_Data *)clientData;
1053
* Inform the VMX that we support setting the guest
1054
* resolution and display topology. Currently, this only
1055
* applies on windows.
1057
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.resolution_set 1")) {
1058
Debug("ToolsDaemonTcloCapReg: Unable to register resolution set capability\n");
1060
/* Tell the VMX to send resolution updates to the tools daemon */
1061
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.resolution_server %s 1",
1062
TOOLS_DAEMON_NAME)) {
1063
Debug("ToolsDaemonTcloCapReg: Unable to register resolution server capability\n");
1066
* Bug 149541: Windows 2000 does not currently support multimon.
1068
* In addition, NT will never support multimon. 9x guests have
1069
* frozen tools, and will report this capability set to 1, which
1070
* current UIs will treat as unsupported.
1072
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.display_topology_set %s",
1073
Hostinfo_GetOSType() >= OS_WINXP ? "2" : "0")) {
1074
Debug("ToolsDaemonTcloCapReg: Unable to register display topology set "
1077
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.display_global_offset 1")) {
1078
Debug("ToolsDaemonTcloCapReg: Unable to register display global offset "
1081
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.color_depth_set 1")) {
1082
Debug("ToolsDaemonTcloCapReg: Unable to register color depth set "
1087
* Report to the VMX any minimum guest resolution below which we
1088
* can't resize the guest. See bug 58681.
1090
ToolsDaemon_GetMinResolution(*data->pConfDict, &minResolutionWidth,
1091
&minResolutionHeight);
1093
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.resolution_min %u %u",
1094
minResolutionWidth, minResolutionHeight)) {
1095
Debug("ToolsDaemonTcloCapReg: Unable to register minimum resolution of %ux%u\n",
1096
minResolutionWidth, minResolutionHeight);
1100
#ifdef TOOLSDAEMON_HAS_RESOLUTION
1101
Resolution_RegisterCaps();
1105
* Bug 294328: Mac OS guests do not (yet) support the state change RPCs.
1108
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.statechange")) {
1109
Debug("ToolsDaemonTcloCapReg: VMware doesn't support tools.capability.statechange. "
1110
"Trying .haltreboot\n");
1111
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.haltreboot")) {
1112
return RpcIn_SetRetVals(result, resultLen,
1113
"Unable to register capabilities", FALSE);
1117
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.softpowerop_retry")) {
1118
Debug("ToolsDaemonTcloCapReg: VMX doesn't support "
1119
"tools.capability.softpowerop_retry.");
1121
#endif // ifndef __APPLE__
1124
* This is a _WIN32 || linux check, with the additional check since linux is
1125
* defined when you build the NetWare Tools.
1127
#if (defined(_WIN32) || defined(linux)) && !defined(N_PLAT_NLM)
1129
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.auto_upgrade 2")) {
1130
Debug("ToolsDaemonTcloCapReg: Unable to register "
1131
"auto-upgrading capability.\n");
1134
if (guestTempDirectory == NULL) {
1136
guestTempDirectory = File_GetTmpDir(FALSE);
1138
guestTempDirectory = Util_GetSafeTmpDir(FALSE);
1142
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.guest_temp_directory 1 %s",
1143
guestTempDirectory)) {
1144
Debug("ToolsDaemonTcloCapReg: Unable to register guest temp "
1145
"directory capability.\n");
1150
#if !defined(N_PLAT_NLM)
1152
char *confPath = GuestApp_GetConfPath();
1153
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.guest_conf_directory %s",
1154
confPath ? confPath : "")) {
1155
Debug("ToolsDaemonTcloCapReg: Unable to register guest conf "
1156
"directory capability.\n");
1162
* Send the uptime here so that the VMX can detect soft resets. This must be
1163
* sent before the Tools version RPC since the version RPC handler uses the
1164
* uptime to detect soft resets.
1166
if (!GuestInfoServer_SendUptime()) {
1167
Debug("Daemon: Error setting guest uptime during 'reset' request.\n");
1172
* Send the monolithic Tools version. Using a configuration option, users
1173
* can override the Tools version such that the VMX treats the Tools as not
1174
* to be managed by the VMware platform.
1176
#if defined(OPEN_VM_TOOLS)
1177
version = TOOLS_VERSION_UNMANAGED;
1179
version = GuestApp_GetDictEntryBool(*data->pConfDict,
1180
CONFNAME_DISABLETOOLSVERSION) ?
1181
TOOLS_VERSION_UNMANAGED : TOOLS_VERSION_CURRENT;
1183
if (!RpcOut_sendOne(NULL, NULL, "tools.set.version %u", version)) {
1184
Debug("Daemon: Error setting tools version during 'Capabilities_Register'"
1188
#if !defined(N_PLAT_NLM) && !defined(sun)
1189
if (!HgfsServerManager_CapReg(TOOLS_DAEMON_NAME, TRUE)) {
1190
Debug("ToolsDaemonTcloCapReg: Failed to register HGFS server capability.\n");
1195
HgfsUsability_RegisterServiceCaps();
1196
if (Hostinfo_GetOSType() >= OS_VISTA) {
1197
ServiceHelpers_SendResolutionCaps();
1201
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1206
*-----------------------------------------------------------------------------
1208
* ToolsDaemonTcloTimeSync --
1210
* Sync the guest's time with the host's.
1219
*-----------------------------------------------------------------------------
1223
ToolsDaemonTcloTimeSync(char const **result, // OUT
1224
size_t *resultLen, // OUT
1225
const char *name, // IN
1226
const char *args, // IN
1227
size_t argsSize, // Ignored
1228
void *clientData) // Ignored
1230
Bool slewCorrection = !strcmp(args, "1");
1232
if (!ToolsDaemon_SyncTime(slewCorrection, TRUE, clientData)) {
1233
return RpcIn_SetRetVals(result, resultLen,
1234
"Unable to sync time", FALSE);
1236
return RpcIn_SetRetVals(result, resultLen, "", TRUE);
1242
*-----------------------------------------------------------------------------
1244
* ToolsDaemonTcloSetOption --
1246
* Parse a "Set_Option" TCLO cmd from the VMX & update the local
1247
* value of the option.
1250
* TRUE if the set option command was executed
1251
* FALSE if something failed (detail displayed)
1254
* Start or stop processes (like time syncing) that could be affected
1255
* by option's new value.
1257
*-----------------------------------------------------------------------------
1261
ToolsDaemonTcloSetOption(char const **result, // OUT
1262
size_t *resultLen, // OUT
1263
const char *name, // IN
1264
const char *args, // IN
1265
size_t argsSize, // Ignored
1266
void *clientData) // IN
1268
Bool retVal = FALSE;
1271
unsigned int index = 0;
1272
static Bool timeSyncStartup = TRUE;
1273
static int oldTimeSyncValue = -1;
1275
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
1279
/* parse the option & value string */
1280
option = StrUtil_GetNextToken(&index, args, " ");
1281
index++; // ignore leading space before value
1282
value = StrUtil_GetNextToken(&index, args, "");
1283
if (option == NULL || value == NULL || strlen(value) == 0) {
1284
goto invalid_option;
1287
/* Validate the option name & value */
1288
if (strcmp(option, TOOLSOPTION_SYNCTIME) == 0) {
1289
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1292
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_SLEWCORRECTION) == 0) {
1293
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1296
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERCENTCORRECTION) == 0) {
1298
if (!StrUtil_StrToInt(&percent, value) || percent == 0 || percent > 100) {
1301
Debug("Daemon: update the slew correction percent.\n");
1302
} else if (strcmp(option, TOOLSOPTION_COPYPASTE) == 0) {
1303
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1306
} else if (strcmp(option, TOOLSOPTION_AUTOHIDE) == 0) {
1307
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1310
} else if (strcmp(option, TOOLSOPTION_BROADCASTIP) == 0) {
1311
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1314
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERIOD) == 0) {
1315
Debug("Daemon: update the time sync period.\n");
1316
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_ENABLE) == 0) {
1317
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1320
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_STARTUP) == 0) {
1321
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1324
} else if (strcmp(option, TOOLSOPTION_LINK_ROOT_HGFS_SHARE) == 0) {
1326
* Check to make sure that we actually support creating the link
1329
if (!data->linkHgfsCB || !data->unlinkHgfsCB) {
1330
goto invalid_option;
1333
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1336
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWERON) == 0) {
1337
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1340
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWEROFF) == 0) {
1341
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1344
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_SUSPEND) == 0) {
1345
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1348
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_RESUME) == 0) {
1349
if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
1353
goto invalid_option;
1356
Debug("Daemon: Setting option '%s' to '%s'\n", option, value);
1357
GuestApp_SetDictEntry(data->optionsDict, option, value);
1359
/* Take action that may be necessary given the new value */
1360
if (strcmp(option, TOOLSOPTION_SYNCTIME) == 0) {
1361
int start = (strcmp(value, "1") == 0);
1364
* Try the one-shot time sync if time sync transitions from
1367
if (oldTimeSyncValue == 0 && start &&
1368
GuestApp_GetDictEntry(data->optionsDict, TOOLSOPTION_SYNCTIME_ENABLE)) {
1369
ToolsDaemon_SyncTime(data->slewCorrection, TRUE, clientData);
1371
oldTimeSyncValue = start;
1373
/* Now start/stop the loop. */
1374
if (!ToolsDaemonStartStopTimeSyncLoop(data, start)) {
1375
RpcIn_SetRetVals(result, resultLen,
1376
"Unable to start/stop time sync loop",
1380
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_SLEWCORRECTION) == 0) {
1381
data->slewCorrection = strcmp(value, "0");
1382
Debug("Daemon: Setting slewCorrection, %d.\n", data->slewCorrection);
1383
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERCENTCORRECTION) == 0) {
1385
if (StrUtil_StrToInt(&percent, value)) {
1386
data->slewPercentCorrection = percent;
1388
} else if (strcmp(option, TOOLSOPTION_BROADCASTIP) == 0 &&
1389
strcmp(value, "1") == 0) {
1392
ip = NetUtil_GetPrimaryIP();
1395
RpcIn_SetRetVals(result, resultLen, "Error getting IP address of guest",
1397
RpcOut_sendOne(NULL, NULL, "info-set guestinfo.ip %s",
1398
GUESTINFO_IP_UNKNOWN);
1402
RpcOut_sendOne(NULL, NULL, "info-set guestinfo.ip %s",
1403
ip[0] == '\0' ? GUESTINFO_IP_UNKNOWN : ip);
1405
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERIOD) == 0) {
1406
uint32 period = atoi(value);
1409
* If the sync loop is running and
1410
* the time sync period has changed,
1411
* restart the loop with the new period value.
1412
* If the sync loop is not running,
1413
* just remember the new sync period value.
1415
if (period != data->timeSyncPeriod) {
1416
data->timeSyncPeriod = period * 100;
1418
if (data->timeSyncEvent != NULL) {
1421
/* Stop the loop. */
1422
status = ToolsDaemonStartStopTimeSyncLoop(data, FALSE);
1424
/* Start the loop with the new period value. */
1425
status = ToolsDaemonStartStopTimeSyncLoop(data, TRUE);
1428
RpcIn_SetRetVals(result, resultLen,
1429
"Unable to change time sync period value",
1435
} else if (strcmp(option, TOOLSOPTION_SYNCTIME_STARTUP) == 0) {
1436
uint32 syncStartupOk = atoi(value);
1438
if (timeSyncStartup) {
1439
timeSyncStartup = FALSE;
1441
if (syncStartupOk) {
1442
if (!ToolsDaemon_SyncTime(TRUE, TRUE, clientData)) {
1443
RpcIn_SetRetVals(result, resultLen,
1444
"Unable to sync time during startup",
1450
} else if (strcmp(option, TOOLSOPTION_LINK_ROOT_HGFS_SHARE) == 0) {
1451
if (strcmp(value, "1") == 0) {
1452
/* Validated that data->linkHgfsCB existed above. */
1453
retVal = data->linkHgfsCB(data->linkHgfsCBData);
1454
} else if (strcmp(value, "0") == 0) {
1455
/* Validated that data->unlinkHgfsCB existed above. */
1456
retVal = data->unlinkHgfsCB(data->unlinkHgfsCBData);
1460
RpcIn_SetRetVals(result, resultLen,
1461
"Could not link/unlink root share.",
1465
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWERON) == 0) {
1466
data->toolScriptOption[GUESTOS_STATECHANGE_POWERON] =
1467
strcmp(value, "0") ? TRUE : FALSE;
1468
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWEROFF) == 0) {
1469
data->toolScriptOption[GUESTOS_STATECHANGE_HALT] =
1470
data->toolScriptOption[GUESTOS_STATECHANGE_REBOOT] =
1471
strcmp(value, "0") ? TRUE : FALSE;
1472
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_SUSPEND) == 0) {
1473
data->toolScriptOption[GUESTOS_STATECHANGE_SUSPEND] =
1474
strcmp(value, "0") ? TRUE : FALSE;
1475
} else if (strcmp(option, TOOLSOPTION_SCRIPTS_RESUME) == 0) {
1476
data->toolScriptOption[GUESTOS_STATECHANGE_RESUME] =
1477
strcmp(value, "0") ? TRUE : FALSE;
1481
RpcIn_SetRetVals(result, resultLen, "", retVal = TRUE);
1485
RpcIn_SetRetVals(result, resultLen, "Unknown option", retVal = FALSE);
1489
RpcIn_SetRetVals(result, resultLen, "Invalid option value",
1501
*-----------------------------------------------------------------------------
1503
* ToolsDaemonTcloError --
1505
* Callback called when an error occurred in the receive loop
1513
*-----------------------------------------------------------------------------
1517
ToolsDaemonTcloError(void *clientData, // IN
1518
char const *status) // IN
1520
ToolsDaemon_Data *data = (ToolsDaemon_Data *)clientData;
1524
Warning("Error in the RPC receive loop: %s.\n\n", status);
1525
data->inError = TRUE;
1530
*-----------------------------------------------------------------------------
1532
* ToolsDaemon_Init_Backdoor --
1534
* Initializes the backdoor to the VMX.
1537
* TRUE if successful.
1543
*-----------------------------------------------------------------------------
1547
ToolsDaemon_Init_Backdoor(ToolsDaemon_Data * data) // IN/OUT
1549
data->in = RpcIn_Construct(ToolsDaemonEventQueue);
1550
if (data->in == NULL) {
1551
Warning("Unable to create the RpcIn object.\n\n");
1556
* Initialize 'inError' before starting the loop -- clients should
1557
* only read this flag.
1559
data->inError = FALSE;
1562
/* Start the TCLO receive loop */
1563
if (RpcIn_start(data->in, RPCIN_POLL_TIME,
1564
ToolsDaemonTcloReset, data,
1565
ToolsDaemonTcloError, data) == FALSE) {
1566
RpcIn_Destruct(data->in);
1568
Warning("Unable to start the receive loop.\n\n");
1572
RpcIn_RegisterCallback(data->in, "Time_Synchronize",
1573
ToolsDaemonTcloTimeSync, NULL);
1574
RpcIn_RegisterCallback(data->in, "Capabilities_Register",
1575
ToolsDaemonTcloCapReg, data);
1576
RpcIn_RegisterCallback(data->in, "Set_Option",
1577
ToolsDaemonTcloSetOption, data);
1580
* Bug 294328: Mac OS guests do not (yet) support the state change RPCs.
1585
for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
1586
RpcIn_RegisterCallback(data->in, stateChangeCmdTable[i].tcloCmd,
1587
ToolsDaemonTcloStateChange, data);
1590
#endif // ifndef __APPLE__
1592
#if !defined(N_PLAT_NLM)
1593
FoundryToolsDaemon_RegisterRoutines(data->in,
1595
ToolsDaemonEventQueue,
1597
if (!HgfsServerManager_Register(data->in, TOOLS_DAEMON_NAME)) {
1598
RpcIn_stop(data->in);
1599
RpcIn_Destruct(data->in);
1601
Warning("Could not initialize HGFS server\n");
1606
#ifdef TOOLSDAEMON_HAS_RESOLUTION
1607
Resolution_InitBackdoor(data->in);
1610
#if !defined(__FreeBSD__) && !defined(sun) && !defined(N_PLAT_NLM)
1611
DeployPkg_Register(data->in);
1619
*-----------------------------------------------------------------------------
1621
* ToolsDaemon_Init --
1623
* Setup a TCLO channel with VMware, and start it's event loop
1626
* the created RpcIn struct or
1627
* NULL if something failed (detail is displayed)
1632
*-----------------------------------------------------------------------------
1636
ToolsDaemon_Init(GuestApp_Dict **pConfDict, // IN
1637
const char *execLogPath, // IN
1638
ToolsDaemon_Callback haltCB, // IN
1639
void *haltCBData, // IN
1640
ToolsDaemon_Callback rebootCB, // IN
1641
void *rebootCBData, // IN
1642
ToolsDaemon_Callback resetCB, // IN
1643
void *resetCBData, // IN
1644
ToolsDaemon_Callback linkHgfsCB, // IN
1645
void *linkHgfsCBData, // IN
1646
ToolsDaemon_Callback unlinkHgfsCB, // IN
1647
void *unlinkHgfsCBData) // IN
1649
ToolsDaemon_Data *data;
1654
#endif // #ifndef N_PLAT_NLM
1658
ASSERT(haltCB != NULL);
1659
ASSERT(rebootCB != NULL);
1661
data = (ToolsDaemon_Data *) calloc(1, sizeof(ToolsDaemon_Data));
1662
ASSERT_MEM_ALLOC(data);
1664
data->pConfDict = pConfDict;
1665
data->execLogPath = execLogPath;
1666
data->inError = FALSE;
1667
data->haltCB = haltCB;
1668
data->haltCBData = haltCBData;
1669
data->rebootCB = rebootCB;
1670
data->rebootCBData = rebootCBData;
1671
data->stateChgInProgress = GUESTOS_STATECHANGE_NONE;
1672
data->lastFailedStateChg = GUESTOS_STATECHANGE_NONE;
1673
data->resetCB = resetCB;
1674
data->resetCBData = resetCBData;
1675
data->linkHgfsCB = linkHgfsCB;
1676
data->linkHgfsCBData = linkHgfsCBData;
1677
data->unlinkHgfsCB = unlinkHgfsCB;
1678
data->unlinkHgfsCBData = unlinkHgfsCBData;
1679
data->timeSyncPeriod = 0;
1680
data->slewPercentCorrection = PERCENT_CORRECTION;
1681
data->slewCorrection = TRUE;
1683
for (i = 0; i < GUESTOS_STATECHANGE_LAST; i++) {
1684
data->toolScriptOption[i] = TRUE;
1687
#if ALLOW_TOOLS_IN_FOREIGN_VM
1688
if (!VmCheck_IsVirtualWorld()) {
1689
ToolsDaemon_InitializeForeignVM(data);
1693
#if defined(VMX86_DEBUG) && !defined(__APPLE__)
1695
/* Make sure the confDict has all the confs we need */
1696
for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
1697
const char *confName;
1699
confName = stateChgConfNames[stateChangeCmdTable[i].id];
1700
ASSERT(GuestApp_GetDictEntry(*pConfDict, confName));
1705
ToolsDaemonEventQueue = EventManager_Init();
1706
if(!ToolsDaemonEventQueue) {
1707
Warning("Unable to create the event queue.\n\n");
1711
#ifdef TOOLSDAEMON_HAS_RESOLUTION
1712
if (!Resolution_Init(TOOLS_DAEMON_NAME, NULL)) {
1713
Debug("%s: Unable to initialize Guest Fit feature\n", __func__);
1718
* Load the conf file, then setup a periodic check and reload.
1720
Debug_Set(GuestApp_GetDictEntryBool(*pConfDict, CONFNAME_LOG), DEBUG_PREFIX);
1723
* All components except vmware-user will be logged to same file. Everytime after
1724
* reboot, tools daemon should rename existing log file and start logging to a new
1725
* one. In all other cases the backup flag for Debug_EnableToFile should be set to
1728
Debug_EnableToFile(GuestApp_GetDictEntry(*pConfDict, CONFNAME_LOGFILE), TRUE);
1730
EventManager_Add(ToolsDaemonEventQueue, CONF_POLL_TIME, ToolsDaemonConfFileLoop,
1733
if (!ToolsDaemon_Init_Backdoor(data)) {
1737
data->optionsDict = GuestApp_ConstructDict(NULL);
1742
if (ToolsDaemonEventQueue) {
1743
EventManager_Destroy(ToolsDaemonEventQueue);
1744
ToolsDaemonEventQueue = NULL;
1752
*-----------------------------------------------------------------------------
1754
* ToolsDaemon_Cleanup_Backdoor --
1756
* Closes the backdoor to the VMX.
1764
*-----------------------------------------------------------------------------
1768
ToolsDaemon_Cleanup_Backdoor(ToolsDaemon_Data *data) // IN/OUT
1772
#if !defined(N_PLAT_NLM)
1773
HgfsServerManager_Unregister(data->in, TOOLS_DAEMON_NAME);
1775
RpcIn_stop(data->in);
1776
RpcIn_Destruct(data->in);
1783
*-----------------------------------------------------------------------------
1785
* ToolsDaemon_Cleanup --
1787
* Cleanup the RpcIn channel if it hasn't been destructed yet& free the
1796
*-----------------------------------------------------------------------------
1800
ToolsDaemon_Cleanup(ToolsDaemon_Data *data) // IN
1803
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.resolution_set 0")) {
1804
Debug("%s: Unable to unregister resolution set capability\n",
1807
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.resolution_server %s 0",
1808
TOOLS_DAEMON_NAME)) {
1809
Debug("%s: Unable to unregister resolution server capability\n",
1812
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.display_topology_set 0")) {
1813
Debug("%s: Unable to unregister display topology set capability\n",
1816
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.display_global_offset 0")) {
1817
Debug("%s: Unable to unregister display global offset capability\n",
1820
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.color_depth_set 0")) {
1821
Debug("%s: Unable to unregister color depth set capability\n",
1826
* Clear the minimum resolution limitation.
1828
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.resolution_min 0 0")) {
1829
Debug("%s: Unable to clear minimum resolution\n", __FUNCTION__);
1832
HgfsUsability_UnregisterServiceCaps();
1835
#ifdef TOOLSDAEMON_HAS_RESOLUTION
1836
Resolution_Cleanup();
1839
#if defined(_WIN32) || defined(linux)
1840
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.auto_upgrade 0")) {
1841
Debug("%s: Unable to clear auto-upgrading capability.\n", __FUNCTION__);
1843
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.guest_temp_directory 0")) {
1844
Debug("%s: Unable to clear guest temp directory capability.\n",
1849
#if !defined(N_PLAT_NLM)
1850
if (!RpcOut_sendOne(NULL, NULL, "tools.capability.guest_conf_directory 0")) {
1851
Debug("%s: Unable to clear guest conf directory capability.\n",
1856
#if ALLOW_TOOLS_IN_FOREIGN_VM
1857
if (runningInForeignVM) {
1858
ToolsDaemon_ShutdownForeignVM();
1862
ToolsDaemon_Cleanup_Backdoor(data);
1864
GuestApp_FreeDict(data->optionsDict);
1866
if (data->asyncProc) {
1867
ProcMgr_Kill(data->asyncProc);
1868
ToolsDaemonStateChangeDone(FALSE, data);
1871
EventManager_Destroy(ToolsDaemonEventQueue);
1873
free(guestTempDirectory);
1878
*-----------------------------------------------------------------------------
1880
* ToolsDaemon_CheckReset --
1882
* Can/should be called in an app's main run loop before calling the
1883
* 'sleep' function, to check and potentially reset the rpc layer.
1886
* TRUE if no errors were encountered, or rpc re-initialization is in
1887
* progress and we haven't exceeded the maximum number of consecutive
1889
* FALSE rpc can't be re-initialized, or we exhausted our attempts quota
1894
*-----------------------------------------------------------------------------
1898
ToolsDaemon_CheckReset(ToolsDaemon_Data *data, // IN/OUT
1899
uint64 *sleepUsecs) // IN/OUT
1901
static int channelTimeoutAttempts = -1;
1906
if (channelTimeoutAttempts < 0) {
1907
Debug("Attempting to retrieve number of channel timeout attempts "
1910
* Currenly, we still use the 'guestinfo' alias. When the main branches
1911
* are synced up and the 'guestvars' code becomes stable, we'll move to
1912
* using the un-prefixed key.
1914
if (RpcOut_sendOne(&tmp, NULL,
1915
"info-get guestinfo.guest_rpc.tclo.timeout") && tmp) {
1916
Debug("Retrieved channel timeout attempts from vmx: %s\n", tmp);
1917
channelTimeoutAttempts = atoi(tmp);
1920
/* Safe-guard attempts against negative and too high-values. */
1921
if (channelTimeoutAttempts <= 0) {
1922
channelTimeoutAttempts = 60;
1923
Debug("Assuming %d channel timeout attempts\n",
1924
channelTimeoutAttempts);
1925
} else if (channelTimeoutAttempts > 180) {
1926
channelTimeoutAttempts = 180;
1927
Debug("Limiting to %d channel timeout attempts\n",
1928
channelTimeoutAttempts);
1931
* Double it. This handles the case where the host is heavily loaded and
1932
* host (real) and guest (virtual) times diverge to the point where the
1933
* guest process timeouts before the VMX can reset the channel. This
1934
* makes the guest process wait sufficiently long. Note that since the
1935
* max above is 180 attempts, it is possible to wait 360 * sleepUsecs,
1936
* which by default is 360 seconds.
1938
channelTimeoutAttempts *= 2;
1939
Debug("Backdoor resetting will be attemped at most %d times\n",
1940
channelTimeoutAttempts);
1943
if (data->inError) {
1944
if (++(data->errorCount) > channelTimeoutAttempts) {
1945
Warning("Failed to reset backdoor after %d attempts\n",
1946
data->errorCount - 1);
1950
Debug("Resetting backdoor [%d]\n", data->errorCount);
1951
if (RpcIn_restart(data->in) == FALSE) {
1952
Warning("Backdoor reset failed [%d]\n", data->errorCount);
1955
data->inError = FALSE;
1957
*sleepUsecs = (uint64)1000000;
1959
if ( *sleepUsecs > 0 && data->errorCount > 0) {
1960
Debug("Backdoor was reset successfully\n");
1961
data->errorCount = 0;
1969
*-----------------------------------------------------------------------------
1971
* ToolsDaemon_SetOsPhase --
1973
* Set the guest OS phase in the VMX
1982
*-----------------------------------------------------------------------------
1986
ToolsDaemon_SetOsPhase(Bool stateChangeSucceeded, // IN
1987
unsigned int cmdId) // IN
1989
return RpcOut_sendOne(NULL, NULL, "tools.os.statechange.status %d %d",
1990
stateChangeSucceeded, cmdId);
1995
*-----------------------------------------------------------------------------
1997
* ToolsDaemon_GetMinResolution --
1999
* Get the minimum resolution (height and width) that we support
2000
* setting this guest to.
2002
* This was originally added for bug 58681.
2010
*-----------------------------------------------------------------------------
2014
ToolsDaemon_GetMinResolution(GuestApp_Dict *dict, // IN
2015
unsigned int *width, // OUT
2016
unsigned int *height) // OUT
2022
* This code is no longer used for Win9x platforms, and it's assumed that
2023
* all other platforms don't have a minimum.
2031
*-----------------------------------------------------------------------------
2033
* ToolsDaemon_GetGuestTempDirectory --
2035
* Return the guest temp directory.
2038
* The guest temp directory
2043
*-----------------------------------------------------------------------------
2047
ToolsDaemon_GetGuestTempDirectory(void)
2049
return guestTempDirectory;
2054
*-----------------------------------------------------------------------------
2056
* ToolsDaemon_InitializeForeignVM --
2058
* This is called when the tools are not running in a VM in VMware.
2059
* Register appropriate backdoor procedures, and open the foreign tools
2068
*-----------------------------------------------------------------------------
2072
ToolsDaemon_InitializeForeignVM(ToolsDaemon_Data *toolsDaemonData) // IN
2076
runningInForeignVM = TRUE;
2078
MessageStub_RegisterTransport();
2080
success = ForeignTools_Initialize(toolsDaemonData->optionsDict);
2081
} // ToolsDaemon_InitializeForeignVM
2085
*-----------------------------------------------------------------------------
2087
* ToolsDaemon_ShutdownForeignVM --
2089
* This is called when the tools are not running in a VM in VMware.
2090
* Close the foreign tools listener socket.
2098
*-----------------------------------------------------------------------------
2102
ToolsDaemon_ShutdownForeignVM(void)
2104
ForeignTools_Shutdown();
2105
} // ToolsDaemon_ShutdownForeignVM