VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxLA.cpp@ 95967

最後變更 在這個檔案從95967是 95961,由 vboxsync 提交於 3 年 前

Additions/VBoxTray: Got rid of VBoxDisplay.h (renamed to VBoxTrayInternal.h, more stuff added later), as I also tripped over this several times in the past already, log include fixes.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 41.5 KB
 
1/* $Id: VBoxLA.cpp 95961 2022-08-01 14:08:20Z vboxsync $ */
2/** @file
3 * VBoxLA - VBox Location Awareness notifications.
4 */
5
6/*
7 * Copyright (C) 2014-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
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.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <iprt/assert.h>
23#include <iprt/alloc.h>
24#include <iprt/list.h>
25#include <iprt/ldr.h>
26#include <iprt/log.h>
27#include <iprt/utf16.h>
28
29#define _WIN32_WINNT 0x0501
30#include <iprt/win/windows.h>
31
32#include "VBoxTray.h"
33#include "VBoxLA.h"
34
35
36/*********************************************************************************************************************************
37* Defines *
38*********************************************************************************************************************************/
39#define REG_KEY_LEN 1024
40#define MAX_CLIENT_NAME_CHARS 1024
41
42#define LA_DO_NOTHING 0
43#define LA_DO_ATTACH 1
44#define LA_DO_DETACH 2
45#define LA_DO_DETACH_AND_ATTACH 3
46#define LA_DO_ATTACH_AND_DETACH 4
47
48
49#define LA_UTCINFO_CLIENT_NAME 0
50#define LA_UTCINFO_CLIENT_IPADDR 1
51#define LA_UTCINFO_CLIENT_LOCATION 2
52#define LA_UTCINFO_CLIENT_OTHERINFO 3
53#define LA_UTCINFO_CLIENT_INFO_LAST 3
54
55#define LA_UTCINFO_PROP_NAME 0
56#define LA_UTCINFO_PROP_VALUE 1
57
58
59typedef struct _VBOXLACONTEXT
60{
61 const VBOXSERVICEENV *pEnv;
62
63 bool fLogEnabled;
64 bool fDetachOnDisconnect;
65
66 uint32_t u32GuestPropHandle; /* The client identifier of the guest property system. */
67
68 RTLISTANCHOR listAttachActions;
69 RTLISTANCHOR listDetachActions;
70
71 uint64_t u64LastQuery; /* The timestamp of the last query of the properties. */
72
73 uint32_t u32Action; /* Which action to do: LA_DO_*. */
74 uint32_t u32PrevAction; /* Which action were done last time. */
75
76 struct /* Information about the client, which properties are monitored. */
77 {
78 uint32_t u32ClientId; /* The RDP client identifier. 0 if none. */
79
80 uint32_t u32LastAttach;
81 uint64_t u64LastAttachTimestamp;
82
83 char *pszLastName;
84 uint64_t u64LastNameTimestamp;
85
86 char *pszPropName; /* The actual Client/%ID%/Name property name with client id. */
87 char *pszPropIPAddr; /* The actual Client/%ID%/IPAddr property name with client id. */
88 char *pszPropLocation; /* The actual Client/%ID%/Location property name with client id. */
89 char *pszPropOtherInfo; /* The actual Client/%ID%/OtherInfo property name with client id. */
90
91 char *pszPropAttach; /* The actual Client/%ID%/Attach property name with client id. */
92
93 char *pszPropWaitPattern; /* Which properties are monitored. */
94 } activeClient;
95
96 BOOL (WINAPI * pfnProcessIdToSessionId)(DWORD dwProcessId, DWORD *pSessionId);
97} VBOXLACONTEXT, *PVBOXLACONTEXT;
98
99typedef struct _ACTIONENTRY
100{
101 RTLISTNODE nodeActionEntry;
102 uint32_t u32Index;
103 WCHAR wszCommandLine[1];
104} ACTIONENTRY, *PACTIONENTRY;
105
106
107static VBOXLACONTEXT g_Ctx = { 0 };
108
109static const char *g_pszPropActiveClient = "/VirtualBox/HostInfo/VRDP/ActiveClient";
110
111static const char *g_pszPropAttachTemplate = "/VirtualBox/HostInfo/VRDP/Client/%u/Attach";
112
113static const char *g_pszVolatileEnvironment = "Volatile Environment";
114
115static const WCHAR *g_pwszClientName = L"CLIENTNAME";
116
117static const WCHAR *g_pwszUTCINFOClientInfo[] = {
118 L"UTCINFO_CLIENTNAME",
119 L"UTCINFO_CLIENTIPA",
120 L"UTCINFO_CLIENTLOCATION",
121 L"UTCINFO_CLIENTOTHERINFO"
122 };
123
124static const char *g_pszPropInfoTemplates[] = {
125 "/VirtualBox/HostInfo/VRDP/Client/%u/Name",
126 "/VirtualBox/HostInfo/VRDP/Client/%u/IPAddr",
127 "/VirtualBox/HostInfo/VRDP/Client/%u/Location",
128 "/VirtualBox/HostInfo/VRDP/Client/%u/OtherInfo"
129 };
130
131#ifdef RT_ARCH_AMD64
132const WCHAR *g_pwszRegKeyDisconnectActions = L"Software\\Wow6432Node\\Oracle\\Sun Ray\\ClientInfoAgent\\DisconnectActions";
133const WCHAR *g_pwszRegKeyReconnectActions = L"Software\\Wow6432Node\\Oracle\\Sun Ray\\ClientInfoAgent\\ReconnectActions";
134#else
135const WCHAR *g_pwszRegKeyDisconnectActions = L"Software\\Oracle\\Sun Ray\\ClientInfoAgent\\DisconnectActions";
136const WCHAR *g_pwszRegKeyReconnectActions = L"Software\\Oracle\\Sun Ray\\ClientInfoAgent\\ReconnectActions";
137#endif /* !RT_ARCH_AMD64 */
138
139const char g_szCommandPrefix[] = "Command";
140
141static BOOL laGetRegistryDWORD(WCHAR *pwszRegKey, WCHAR *pwszName, DWORD *pdwValue)
142{
143 LONG lErr;
144
145 HKEY hKey;
146 lErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
147 pwszRegKey,
148 0,
149 KEY_QUERY_VALUE,
150 &hKey);
151
152 if (lErr != ERROR_SUCCESS)
153 {
154 LogRel(("LA: RegOpenKeyExW: failed [%ls]\n",
155 pwszRegKey));
156 return FALSE;
157 }
158
159 DWORD nRegData = sizeof(DWORD);
160 DWORD dwType = 0;
161 lErr = RegQueryValueExW(hKey,
162 pwszName,
163 NULL,
164 &dwType,
165 (BYTE *)pdwValue,
166 &nRegData);
167
168 if (lErr != ERROR_SUCCESS)
169 {
170 LogRel(("LA: RegQueryValueExW: failed [%ls/%ls]\n",
171 pwszRegKey, pwszName));
172 RegCloseKey(hKey);
173 return FALSE;
174 }
175
176 if (nRegData != sizeof(DWORD))
177 {
178 LogRel(("LA: buffer overflow reg %d, [%ls]\n",
179 nRegData, pwszRegKey));
180 RegCloseKey(hKey);
181 return FALSE;
182 }
183
184 if (dwType != REG_DWORD)
185 {
186 LogRel(("LA: wrong type %d, [%ls/%ls]\n",
187 dwType, pwszRegKey, pwszName));
188 RegCloseKey(hKey);
189 return FALSE;
190 }
191
192 RegCloseKey(hKey);
193
194 if (lErr != ERROR_SUCCESS)
195 {
196 return FALSE;
197 }
198
199 return TRUE;
200}
201
202static void ActionExecutorDeleteActions(RTLISTANCHOR *listActions)
203{
204 ACTIONENTRY *pIter;
205 ACTIONENTRY *pIterNext;
206 RTListForEachSafe(listActions, pIter, pIterNext, ACTIONENTRY, nodeActionEntry)
207 {
208 RTListNodeRemove(&pIter->nodeActionEntry);
209 RTMemFree(pIter);
210 }
211}
212
213static BOOL ActionExecutorEnumerateRegistryKey(const WCHAR *pwszRegKey,
214 RTLISTANCHOR *listActions)
215{
216 BOOL bRet = TRUE;
217 HKEY hKey;
218 DWORD dwErr;
219
220 dwErr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
221 pwszRegKey,
222 0,
223 KEY_QUERY_VALUE,
224 &hKey);
225
226 if (dwErr != ERROR_SUCCESS)
227 {
228 LogFlowFunc(("Can't open registry key [%ls], error %d\n",
229 pwszRegKey, dwErr));
230 return FALSE;
231 }
232
233 DWORD dwIndex = 0;
234
235 for (;;)
236 {
237 DWORD dwRet;
238
239 WCHAR wszValueName[256];
240 DWORD cchValueName = RT_ELEMENTS(wszValueName);
241 DWORD type;
242 BYTE abData[1024];
243 DWORD cbData = sizeof(abData);
244
245 dwRet = RegEnumValueW(hKey,
246 dwIndex++,
247 wszValueName,
248 &cchValueName,
249 NULL,
250 &type,
251 abData,
252 &cbData);
253
254 if (dwRet == ERROR_NO_MORE_ITEMS)
255 {
256 LogFlowFunc(("Enumeration exhausted\n"));
257 bRet = TRUE;
258 break;
259 }
260 else if (dwRet != ERROR_SUCCESS)
261 {
262 LogFlowFunc(("Enumeration failed, error %d\n",
263 dwRet));
264 bRet = FALSE;
265 break;
266 }
267
268 if ((type != REG_SZ) && (type != REG_EXPAND_SZ))
269 {
270 LogFlowFunc(("skipped type %d\n",
271 type));
272 continue;
273 }
274
275 char szName[256];
276 char *pszName = &szName[0];
277 int rc = RTUtf16ToUtf8Ex(wszValueName,
278 RT_ELEMENTS(wszValueName),
279 &pszName, sizeof(szName), NULL);
280 if (RT_FAILURE(rc))
281 {
282 LogFlowFunc(("RTUtf16ToUtf8Ex for [%ls] rc %Rrc\n",
283 wszValueName, rc));
284 continue;
285 }
286
287 /* Check if the name starts with "Command" */
288 if (RTStrNICmp(szName, g_szCommandPrefix, RT_ELEMENTS(g_szCommandPrefix) - 1) != 0)
289 {
290 LogFlowFunc(("skipped prefix %s\n",
291 szName));
292 continue;
293 }
294
295 char *pszIndex = &szName[RT_ELEMENTS(g_szCommandPrefix) - 1];
296
297 uint32_t nIndex = RTStrToUInt32(pszIndex);
298 if (nIndex == 0)
299 {
300 LogFlowFunc(("skipped index %s\n",
301 szName));
302 continue;
303 }
304
305 /* Allocate with terminating nul after data. */
306 ACTIONENTRY *pEntry = (ACTIONENTRY *)RTMemAlloc(sizeof(ACTIONENTRY) + cbData);
307 if (!pEntry)
308 {
309 LogFlowFunc(("RTMemAlloc failed\n"));
310 bRet = FALSE;
311 break;
312 }
313
314 RT_ZERO(pEntry->nodeActionEntry);
315 pEntry->u32Index = nIndex;
316 memcpy(pEntry->wszCommandLine, abData, cbData);
317 pEntry->wszCommandLine[cbData / sizeof(WCHAR)] = 0;
318
319 /* Insert the new entry to the list. Sort by index. */
320 if (RTListIsEmpty(listActions))
321 {
322 RTListAppend(listActions, &pEntry->nodeActionEntry);
323 }
324 else
325 {
326 bool fAdded = false;
327 ACTIONENTRY *pIter;
328 RTListForEach(listActions, pIter, ACTIONENTRY, nodeActionEntry)
329 {
330 if (pIter->u32Index > nIndex)
331 {
332 RTListNodeInsertBefore(&pIter->nodeActionEntry, &pEntry->nodeActionEntry);
333 fAdded = true;
334 break;
335 }
336 }
337 if (!fAdded)
338 {
339 RTListAppend(listActions, &pEntry->nodeActionEntry);
340 }
341 }
342
343 LogFlowFunc(("added %d %ls\n",
344 pEntry->u32Index, pEntry->wszCommandLine));
345 }
346
347 RegCloseKey(hKey);
348
349#ifdef LOG_ENABLED
350 ACTIONENTRY *pIter;
351 RTListForEach(listActions, pIter, ACTIONENTRY, nodeActionEntry)
352 {
353 LogFlowFunc(("[%u]: [%ls]\n",
354 pIter->u32Index, pIter->wszCommandLine));
355 }
356#endif
357
358 if (!bRet)
359 {
360 ActionExecutorDeleteActions(listActions);
361 }
362
363 LogFlowFunc(("action enum %d\n", bRet));
364
365 return bRet;
366}
367
368static void ActionExecutorExecuteActions(RTLISTANCHOR *listActions)
369{
370 LogFlowFunc(("ExecuteActions\n"));
371
372 ACTIONENTRY *pIter;
373 RTListForEach(listActions, pIter, ACTIONENTRY, nodeActionEntry)
374 {
375 LogFlowFunc(("[%u]: [%ls]\n",
376 pIter->u32Index, pIter->wszCommandLine));
377
378 STARTUPINFOW si;
379 PROCESS_INFORMATION pi;
380
381 GetStartupInfoW(&si);
382
383 if (!CreateProcessW(NULL, // lpApplicationName
384 pIter->wszCommandLine, // lpCommandLine
385 NULL, // lpProcessAttributes
386 NULL, // lpThreadAttributes
387 FALSE, // bInheritHandles
388 0, // dwCreationFlags
389 NULL, // lpEnvironment
390 NULL, // lpCurrentDirectory
391 &si, // lpStartupInfo
392 &pi)) // lpProcessInformation
393 {
394 LogFlowFunc(("Executing [%ls] failed, error %d\n",
395 pIter->wszCommandLine, GetLastError()));
396 }
397 else
398 {
399 LogFlowFunc(("Executing [%ls] succeeded\n",
400 pIter->wszCommandLine));
401
402 /* Don't care about waiting on the new process, so close these. */
403 CloseHandle(pi.hProcess);
404 CloseHandle(pi.hThread);
405 }
406 }
407
408 LogFlowFunc(("ExecuteActions leave\n"));
409}
410
411static BOOL GetVolatileEnvironmentKey(PVBOXLACONTEXT pCtx, WCHAR *pwszRegKey, DWORD cbRegKey)
412{
413 BOOL fFound = FALSE;
414
415 DWORD nSessionID;
416 LONG lErr;
417 HKEY hKey;
418 char szRegKey[REG_KEY_LEN];
419
420 /* Attempt to open HKCU\Volatile Environment\<session ID> first. */
421 if ( pCtx->pfnProcessIdToSessionId
422 && pCtx->pfnProcessIdToSessionId(GetCurrentProcessId(), &nSessionID))
423 {
424 RTStrPrintf(szRegKey, sizeof(szRegKey),
425 "%s\\%d",
426 g_pszVolatileEnvironment, nSessionID);
427
428 lErr = RegOpenKeyExA(HKEY_CURRENT_USER,
429 szRegKey,
430 0,
431 KEY_SET_VALUE,
432 &hKey);
433
434 if (lErr == ERROR_SUCCESS)
435 {
436 RegCloseKey(hKey);
437 fFound = TRUE;
438 }
439 }
440
441 if (!fFound)
442 {
443 /* Fall back to HKCU\Volatile Environment. */
444 RTStrPrintf(szRegKey, sizeof(szRegKey),
445 "%s",
446 g_pszVolatileEnvironment);
447
448 lErr = RegOpenKeyExA(HKEY_CURRENT_USER,
449 szRegKey,
450 0,
451 KEY_SET_VALUE,
452 &hKey);
453
454 if (lErr == ERROR_SUCCESS)
455 {
456 RegCloseKey(hKey);
457 fFound = TRUE;
458 }
459 }
460
461 if (fFound)
462 {
463 LogFlowFunc(("GetVolatileEnvironmentKey: [%s]\n", szRegKey));
464
465 /* Convert szRegKey to Utf16 string. */
466 PRTUTF16 putf16Unicode = pwszRegKey;
467 size_t cchUnicode = cbRegKey / sizeof(WCHAR);
468
469 int rc = RTStrToUtf16Ex(szRegKey, RTSTR_MAX,
470 &putf16Unicode, cchUnicode, NULL);
471 if (RT_FAILURE(rc))
472 {
473 LogFlowFunc(("RTStrToUtf16Ex failed %Rrc\n", rc));
474 fFound = FALSE;
475 }
476 else
477 {
478 LogFlowFunc(("unicode [%ls]\n", putf16Unicode));
479 }
480 }
481 else
482 {
483 LogFlowFunc(("GetVolatileEnvironmentKey: not found\n"));
484 }
485
486 return fFound;
487}
488
489static BOOL laGetUtcInfoClientName(PVBOXLACONTEXT pCtx, WCHAR *pwszClientName, DWORD cbClientName)
490{
491 LONG lErr;
492
493 WCHAR wszRegKey[REG_KEY_LEN];
494 if (!GetVolatileEnvironmentKey(pCtx, wszRegKey, sizeof(wszRegKey)))
495 {
496 return FALSE;
497 }
498
499 HKEY hKey;
500 lErr = RegOpenKeyExW(HKEY_CURRENT_USER,
501 wszRegKey,
502 0,
503 KEY_QUERY_VALUE,
504 &hKey);
505
506 if (lErr != ERROR_SUCCESS)
507 {
508 LogFlowFunc(("RegOpenKeyExW: failed [%ls]\n",
509 wszRegKey));
510 return FALSE;
511 }
512
513 DWORD nRegData;
514 DWORD dwType;
515 lErr = RegQueryValueExW(hKey,
516 g_pwszUTCINFOClientInfo[LA_UTCINFO_CLIENT_NAME],
517 NULL,
518 &dwType,
519 NULL,
520 &nRegData);
521
522 if (lErr != ERROR_SUCCESS)
523 {
524 LogFlowFunc(("RegQueryValueExW: failed [%ls]\n",
525 wszRegKey));
526 RegCloseKey(hKey);
527 return FALSE;
528 }
529
530 if (nRegData >= cbClientName)
531 {
532 LogFlowFunc(("buffer overflow reg %d, buffer %d, [%ls]\n",
533 nRegData, cbClientName, wszRegKey));
534 RegCloseKey(hKey);
535 return FALSE;
536 }
537
538 if (dwType != REG_SZ)
539 {
540 LogFlowFunc(("wrong type %d, [%ls]\n",
541 dwType, wszRegKey));
542 RegCloseKey(hKey);
543 return FALSE;
544 }
545
546 ZeroMemory(pwszClientName, cbClientName);
547
548 lErr = RegQueryValueExW(hKey,
549 g_pwszUTCINFOClientInfo[LA_UTCINFO_CLIENT_NAME],
550 NULL,
551 NULL,
552 (BYTE *)pwszClientName,
553 &nRegData);
554
555 RegCloseKey(hKey);
556
557 if (lErr != ERROR_SUCCESS)
558 {
559 return FALSE;
560 }
561
562 return TRUE;
563}
564
565static BOOL laSetClientName(PVBOXLACONTEXT pCtx, const WCHAR *pwszClientName)
566{
567 LONG lErr;
568
569 WCHAR wszRegKey[REG_KEY_LEN];
570 if (!GetVolatileEnvironmentKey(pCtx, wszRegKey, sizeof(wszRegKey)))
571 {
572 return FALSE;
573 }
574
575 HKEY hKey;
576 lErr = RegOpenKeyExW(HKEY_CURRENT_USER,
577 wszRegKey,
578 0,
579 KEY_SET_VALUE,
580 &hKey);
581
582 if (lErr != ERROR_SUCCESS)
583 {
584 return FALSE;
585 }
586
587 DWORD nClientName = (lstrlenW(pwszClientName) + 1) * sizeof(WCHAR);
588 lErr = RegSetValueExW(hKey,
589 g_pwszClientName,
590 0,
591 REG_SZ,
592 (BYTE*)pwszClientName,
593 nClientName);
594
595 RegCloseKey(hKey);
596
597 if (lErr != ERROR_SUCCESS)
598 {
599 return FALSE;
600 }
601
602 return TRUE;
603}
604
605static void laBroadcastSettingChange(void)
606{
607 DWORD_PTR dwResult;
608
609 if (SendMessageTimeoutA(HWND_BROADCAST,
610 WM_SETTINGCHANGE,
611 NULL,
612 (LPARAM)"Environment",
613 SMTO_ABORTIFHUNG,
614 5000,
615 &dwResult) == 0)
616 {
617 LogFlowFunc(("SendMessageTimeout failed, error %ld\n", GetLastError()));
618 }
619}
620
621static void laUpdateClientName(PVBOXLACONTEXT pCtx)
622{
623 WCHAR wszUtcInfoClientName[MAX_CLIENT_NAME_CHARS];
624
625 if (laGetUtcInfoClientName(pCtx, wszUtcInfoClientName, sizeof(wszUtcInfoClientName)))
626 {
627 if (laSetClientName(pCtx, wszUtcInfoClientName))
628 laBroadcastSettingChange();
629 }
630}
631
632static void laOnClientLocationInfo(PVBOXLACONTEXT pCtx, char *pszClientInfo[][2])
633{
634 /*
635 * Write the client location info to:
636 * HKCU\Volatile Environment\<CLIENT_LOCATION_INFO> or
637 * HKCU\Volatile Environment\<SessionID>\<CLIENT_LOCATION_INFO>
638 * depending on whether this is a Terminal Services or desktop session
639 * respectively.
640 * The client location info are: Name, IPAddr, Location, OtherInfo
641 */
642 unsigned int idx;
643 WCHAR wszRegKey[REG_KEY_LEN];
644 if (!GetVolatileEnvironmentKey(pCtx, wszRegKey, sizeof(wszRegKey)))
645 {
646 LogFlowFunc(("Failed to get 'Volatile Environment' registry key\n"));
647 return;
648 }
649
650 /* Now write the client name under the appropriate key. */
651 LONG lRet;
652 HKEY hKey;
653
654 lRet = RegOpenKeyExW(HKEY_CURRENT_USER,
655 wszRegKey,
656 0,
657 KEY_SET_VALUE,
658 &hKey);
659
660 if (lRet != ERROR_SUCCESS)
661 {
662 LogFlowFunc(("Failed to open key [%ls], error %lu\n", wszRegKey, lRet));
663 return;
664 }
665
666 PRTUTF16 putf16UnicodeClientInfo[LA_UTCINFO_CLIENT_INFO_LAST + 1] = {NULL};
667 for (idx = 0; idx <= LA_UTCINFO_CLIENT_INFO_LAST; idx++)
668 {
669 if (pszClientInfo[idx][LA_UTCINFO_PROP_VALUE] == NULL)
670 break;
671
672 /* pszClientInfo is UTF8, make an Unicode copy for registry. */
673 size_t cchUnicodeClientInfo = 0;
674
675 int rc = RTStrToUtf16Ex(pszClientInfo[idx][LA_UTCINFO_PROP_VALUE], MAX_CLIENT_NAME_CHARS,
676 &putf16UnicodeClientInfo[idx], 0, &cchUnicodeClientInfo);
677
678 if (RT_FAILURE(rc))
679 {
680 LogFlowFunc(("RTStrToUniEx failed %Rrc\n", rc));
681 break;
682 }
683
684 DWORD nDataLength = (DWORD)((cchUnicodeClientInfo + 1) * sizeof(WCHAR));
685 lRet = RegSetValueExW(hKey,
686 g_pwszUTCINFOClientInfo[idx],
687 0,
688 REG_SZ,
689 (BYTE *)putf16UnicodeClientInfo[idx],
690 nDataLength);
691
692 if (lRet != ERROR_SUCCESS)
693 {
694 LogFlowFunc(("RegSetValueExW failed error %lu for %s \n", lRet, g_pwszUTCINFOClientInfo[idx]));
695 }
696 }
697
698 RegCloseKey(hKey);
699
700 laBroadcastSettingChange();
701
702 /* Also, write these info (Name, IPAddr, Location and Other Info) to the environment of this process, as it
703 * doesn't listen for WM_SETTINGCHANGE messages.
704 */
705
706 for (idx = 0; idx <= LA_UTCINFO_CLIENT_INFO_LAST; idx++)
707 {
708 if (putf16UnicodeClientInfo[idx] == NULL)
709 break;
710
711 SetEnvironmentVariableW(g_pwszUTCINFOClientInfo[idx], putf16UnicodeClientInfo[idx]);
712
713 RTUtf16Free(putf16UnicodeClientInfo[idx]);
714 }
715}
716
717static void laDoAttach(PVBOXLACONTEXT pCtx)
718{
719 LogFlowFunc(("laDoAttach\n"));
720
721 /* Hardcoded action. */
722 laUpdateClientName(pCtx);
723
724 /* Process configured actions. */
725 ActionExecutorExecuteActions(&pCtx->listAttachActions);
726}
727
728static void laDoDetach(PVBOXLACONTEXT pCtx)
729{
730 LogFlowFunc(("laDoDetach\n"));
731
732 /* Process configured actions. */
733 ActionExecutorExecuteActions(&pCtx->listDetachActions);
734}
735
736static int laGetProperty(uint32_t u32GuestPropHandle, const char *pszName, uint64_t *pu64Timestamp, char **ppszValue)
737{
738 int rc = VINF_SUCCESS;
739
740 /* The buffer for storing the data and its initial size. We leave a bit
741 * of space here in case the maximum values are raised.
742 */
743 uint32_t cbBuf = 1024;
744 void *pvBuf = NULL;
745
746 /* Because there is a race condition between our reading the size of a
747 * property and the guest updating it, we loop a few times here and
748 * hope. Actually this should never go wrong, as we are generous
749 * enough with buffer space.
750 */
751 unsigned i;
752 for (i = 0; i < 3; ++i)
753 {
754 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
755 if (pvTmpBuf == NULL)
756 {
757 rc = VERR_NO_MEMORY;
758 break;
759 }
760
761 pvBuf = pvTmpBuf;
762
763 rc = VbglR3GuestPropRead(u32GuestPropHandle, pszName, pvBuf, cbBuf,
764 NULL, pu64Timestamp, NULL,
765 &cbBuf);
766 if (rc != VERR_BUFFER_OVERFLOW)
767 {
768 break;
769 }
770
771 cbBuf += 1024;
772 }
773
774 if (RT_SUCCESS(rc))
775 {
776 LogFlowFunc(("laGetProperty: [%s]\n"
777 " value: [%s]\n"
778 " timestamp: %lld ns\n",
779 pszName, (char *)pvBuf, *pu64Timestamp));
780
781 *ppszValue = (char *)pvBuf;
782 }
783 else if (rc == VERR_NOT_FOUND)
784 {
785 LogFlowFunc(("laGetProperty: not found [%s]\n", pszName));
786 RTMemFree(pvBuf);
787 }
788 else
789 {
790 LogFlowFunc(("Failed to retrieve the property value, error %Rrc\n", rc));
791 RTMemFree(pvBuf);
792 }
793
794 return rc;
795}
796
797static int laWaitProperties(uint32_t u32GuestPropHandle,
798 const char *pszPatterns,
799 uint64_t u64LastTimestamp,
800 uint64_t *pu64Timestamp,
801 uint32_t u32Timeout)
802{
803 int rc = VINF_SUCCESS;
804
805 /* The buffer for storing the data and its initial size. We leave a bit
806 * of space here in case the maximum values are raised.
807 */
808 void *pvBuf = NULL;
809 uint32_t cbBuf = 4096;
810
811 /* Because there is a race condition between our reading the size of a
812 * property and the guest updating it, we loop a few times here and
813 * hope. Actually this should never go wrong, as we are generous
814 * enough with buffer space.
815 */
816 unsigned i;
817 for (i = 0; i < 3; ++i)
818 {
819 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
820 if (NULL == pvTmpBuf)
821 {
822 rc = VERR_NO_MEMORY;
823 break;
824 }
825
826 pvBuf = pvTmpBuf;
827
828 rc = VbglR3GuestPropWait(u32GuestPropHandle, pszPatterns, pvBuf, cbBuf,
829 u64LastTimestamp, u32Timeout,
830 NULL /* ppszName */,
831 NULL /* ppszValue */,
832 pu64Timestamp,
833 NULL /* ppszFlags */,
834 &cbBuf,
835 NULL /* pfWasDeleted */);
836
837 if (rc != VERR_BUFFER_OVERFLOW)
838 break;
839
840 cbBuf += 1024;
841 }
842
843 RTMemFree(pvBuf);
844
845 return rc;
846}
847
848static int laGetUint32(uint32_t u32GuestPropHandle, const char *pszName, uint64_t *pu64Timestamp, uint32_t *pu32Value)
849{
850 uint64_t u64Timestamp = 0;
851 char *pszValue = NULL;
852
853 int rc = laGetProperty(u32GuestPropHandle,
854 pszName,
855 &u64Timestamp,
856 &pszValue);
857 if (RT_SUCCESS(rc))
858 {
859 if (pszValue && *pszValue)
860 {
861 uint32_t u32 = 0;
862 rc = RTStrToUInt32Full(pszValue, 10, &u32);
863
864 if (RT_SUCCESS(rc))
865 {
866 *pu64Timestamp = u64Timestamp;
867 *pu32Value = u32;
868 }
869 }
870 else
871 {
872 rc = VERR_NOT_SUPPORTED;
873 }
874 }
875
876 if (pszValue)
877 RTMemFree(pszValue);
878
879 LogFlowFunc(("laGetUint32: rc = %Rrc, [%s]\n", rc, pszName));
880 return rc;
881}
882
883static int laGetString(uint32_t u32GuestPropHandle, const char *pszName, uint64_t *pu64Timestamp, char **ppszValue)
884{
885 int rc = laGetProperty(u32GuestPropHandle,
886 pszName,
887 pu64Timestamp,
888 ppszValue);
889
890 LogFlowFunc(("laGetString: rc = %Rrc, [%s]\n", rc, pszName));
891 return rc;
892}
893
894static int laGetActiveClient(PVBOXLACONTEXT pCtx, uint64_t *pu64Timestamp, uint32_t *pu32Value)
895{
896 int rc = laGetUint32(pCtx->u32GuestPropHandle,
897 g_pszPropActiveClient,
898 pu64Timestamp,
899 pu32Value);
900
901 LogFlowFunc(("laGetActiveClient: rc %Rrc, %RU32, %RU64\n", rc, *pu32Value, *pu64Timestamp));
902 return rc;
903}
904
905static int laUpdateCurrentState(PVBOXLACONTEXT pCtx, uint32_t u32ActiveClientId, uint64_t u64ActiveClientTS)
906{
907 /* Prepare the current state for the active client.
908 * If u32ActiveClientId is 0, then there is no connected clients.
909 */
910 LogFlowFunc(("laUpdateCurrentState: %RU32 %RU64\n", u32ActiveClientId, u64ActiveClientTS));
911
912 int rc = VINF_SUCCESS;
913
914 int l;
915
916 char **pClientInfoMap[LA_UTCINFO_CLIENT_INFO_LAST + 1] =
917 {
918 &pCtx->activeClient.pszPropName,
919 &pCtx->activeClient.pszPropIPAddr,
920 &pCtx->activeClient.pszPropLocation,
921 &pCtx->activeClient.pszPropOtherInfo,
922 };
923
924 pCtx->activeClient.u32LastAttach = UINT32_MAX;
925 pCtx->activeClient.u64LastAttachTimestamp = u64ActiveClientTS;
926
927 if (pCtx->activeClient.pszLastName)
928 {
929 RTMemFree(pCtx->activeClient.pszLastName);
930 }
931 pCtx->activeClient.pszLastName = NULL;
932 pCtx->activeClient.u64LastNameTimestamp = u64ActiveClientTS;
933
934 unsigned int idx;
935
936 for (idx = 0; idx <= LA_UTCINFO_CLIENT_INFO_LAST; idx++)
937 {
938 if (*pClientInfoMap[idx])
939 {
940 RTMemFree(*pClientInfoMap[idx]);
941 *pClientInfoMap[idx] = NULL;
942 }
943
944 if (u32ActiveClientId != 0)
945 {
946 l = RTStrAPrintf(pClientInfoMap[idx],
947 g_pszPropInfoTemplates[idx],
948 u32ActiveClientId);
949
950 if (l == -1)
951 {
952 *pClientInfoMap[idx] = NULL;
953 rc = VERR_NO_MEMORY;
954 break;
955 }
956 }
957 }
958
959 if (RT_SUCCESS(rc))
960 {
961 if (pCtx->activeClient.pszPropAttach)
962 {
963 RTMemFree(pCtx->activeClient.pszPropAttach);
964 pCtx->activeClient.pszPropAttach = NULL;
965 }
966 if (u32ActiveClientId != 0)
967 {
968 l = RTStrAPrintf(&pCtx->activeClient.pszPropAttach,
969 g_pszPropAttachTemplate,
970 u32ActiveClientId);
971 if (l == -1)
972 {
973 pCtx->activeClient.pszPropAttach = NULL;
974 rc = VERR_NO_MEMORY;
975 }
976 }
977 }
978
979 if (RT_SUCCESS(rc))
980 {
981 if (pCtx->activeClient.pszPropWaitPattern)
982 {
983 RTMemFree(pCtx->activeClient.pszPropWaitPattern);
984 pCtx->activeClient.pszPropWaitPattern = NULL;
985 }
986 if (u32ActiveClientId != 0)
987 {
988 l = RTStrAPrintf(&pCtx->activeClient.pszPropWaitPattern,
989 "%s|%s|%s|%s|%s",
990 pCtx->activeClient.pszPropName,
991 pCtx->activeClient.pszPropAttach,
992 pCtx->activeClient.pszPropIPAddr,
993 pCtx->activeClient.pszPropLocation,
994 pCtx->activeClient.pszPropOtherInfo);
995 if (l == -1)
996 {
997 pCtx->activeClient.pszPropWaitPattern = NULL;
998 rc = VERR_NO_MEMORY;
999 }
1000 }
1001 }
1002
1003 if (RT_SUCCESS(rc))
1004 {
1005 pCtx->activeClient.u32ClientId = u32ActiveClientId;
1006 }
1007 else
1008 {
1009 pCtx->activeClient.u32ClientId = 0;
1010 }
1011
1012 LogFlowFunc(("laUpdateCurrentState rc = %Rrc\n", rc));
1013 return rc;
1014}
1015
1016static int laWait(PVBOXLACONTEXT pCtx, uint64_t *pu64Timestamp, uint32_t u32Timeout)
1017{
1018 LogFlowFunc(("laWait [%s]\n", pCtx->activeClient.pszPropWaitPattern));
1019
1020 int rc = laWaitProperties(pCtx->u32GuestPropHandle,
1021 pCtx->activeClient.pszPropWaitPattern,
1022 pCtx->u64LastQuery,
1023 pu64Timestamp,
1024 u32Timeout);
1025
1026 LogFlowFunc(("laWait rc %Rrc\n", rc));
1027 return rc;
1028}
1029
1030static void laProcessClientInfo(PVBOXLACONTEXT pCtx)
1031{
1032 /* Check if the name was changed. */
1033 /* Get the name string and check if it was changed since last time.
1034 * Write Client name, IPAddr, Location and Other Info to the registry if the name has changed.
1035 */
1036 uint64_t u64Timestamp = 0;
1037 int rc = VINF_SUCCESS;
1038 unsigned int idx;
1039
1040 char *pClientInfoMap[][2] = {
1041 {pCtx->activeClient.pszPropName, NULL},
1042 {pCtx->activeClient.pszPropIPAddr, NULL},
1043 {pCtx->activeClient.pszPropLocation, NULL},
1044 {pCtx->activeClient.pszPropOtherInfo, NULL}
1045 };
1046
1047 for (idx = 0; idx <= LA_UTCINFO_CLIENT_INFO_LAST; idx++)
1048 {
1049 rc = laGetString(pCtx->u32GuestPropHandle,
1050 pClientInfoMap[idx][LA_UTCINFO_PROP_NAME],
1051 &u64Timestamp,
1052 &pClientInfoMap[idx][LA_UTCINFO_PROP_VALUE]);
1053
1054 LogFlowFunc(("laProcessClientInfo: read [%s], at %RU64\n",
1055 pClientInfoMap[idx][LA_UTCINFO_PROP_VALUE], u64Timestamp));
1056
1057 if (RT_FAILURE(rc))
1058 {
1059 LogFlowFunc(("laProcessClientInfo failed at %s\n", pClientInfoMap[idx][LA_UTCINFO_PROP_NAME]));
1060 break;
1061 }
1062 }
1063
1064 if (pClientInfoMap[LA_UTCINFO_CLIENT_NAME][LA_UTCINFO_PROP_VALUE] != NULL)
1065 {
1066 if (u64Timestamp != pCtx->activeClient.u64LastNameTimestamp)
1067 {
1068 laOnClientLocationInfo(pCtx, pClientInfoMap);
1069
1070 pCtx->activeClient.u64LastNameTimestamp = u64Timestamp;
1071 }
1072 }
1073
1074 for (idx = 0; idx <= LA_UTCINFO_CLIENT_INFO_LAST; idx++)
1075 {
1076 if (pClientInfoMap[idx][LA_UTCINFO_PROP_VALUE])
1077 {
1078 RTMemFree(pClientInfoMap[idx][LA_UTCINFO_PROP_VALUE]);
1079 }
1080 }
1081}
1082
1083static void laProcessAttach(PVBOXLACONTEXT pCtx)
1084{
1085 /* Check if the attach was changed. */
1086 pCtx->u32Action = LA_DO_NOTHING;
1087
1088 uint64_t u64Timestamp = 0;
1089 uint32_t u32Attach = UINT32_MAX;
1090
1091 int rc = laGetUint32(pCtx->u32GuestPropHandle,
1092 pCtx->activeClient.pszPropAttach,
1093 &u64Timestamp,
1094 &u32Attach);
1095
1096 if (RT_SUCCESS(rc))
1097 {
1098 LogFlowFunc(("laProcessAttach: read %RU32, at %RU64\n", u32Attach, u64Timestamp));
1099 if (u64Timestamp != pCtx->activeClient.u64LastAttachTimestamp)
1100 {
1101 if (u32Attach != pCtx->activeClient.u32LastAttach)
1102 {
1103 LogFlowFunc(("laProcessAttach: changed\n"));
1104
1105 /* Just do the last action. */
1106 pCtx->u32Action = u32Attach
1107 ? LA_DO_ATTACH : LA_DO_DETACH;
1108
1109 pCtx->activeClient.u32LastAttach = u32Attach;
1110 }
1111 else
1112 {
1113 LogFlowFunc(("laProcessAttach: same\n"));
1114
1115 /* The property has changed but the value is the same,
1116 * which means that it was changed and restored.
1117 */
1118 pCtx->u32Action = u32Attach
1119 ? LA_DO_DETACH_AND_ATTACH : LA_DO_ATTACH_AND_DETACH;
1120 }
1121
1122 pCtx->activeClient.u64LastAttachTimestamp = u64Timestamp;
1123 }
1124
1125 }
1126
1127 LogFlowFunc(("laProcessAttach: action %RU32\n", pCtx->u32Action));
1128}
1129
1130static void laDoActions(PVBOXLACONTEXT pCtx)
1131{
1132 /*
1133 * Check if the attach was changed.
1134 *
1135 * Caller assumes that this function will filter double actions.
1136 * That is two or more LA_DO_ATTACH will do just one LA_DO_ATTACH.
1137 */
1138 LogFlowFunc(("laDoActions: action %RU32, prev %RU32\n", pCtx->u32Action, pCtx->u32PrevAction));
1139
1140 switch(pCtx->u32Action)
1141 {
1142 case LA_DO_ATTACH:
1143 {
1144 if (pCtx->u32PrevAction != LA_DO_ATTACH)
1145 {
1146 pCtx->u32PrevAction = LA_DO_ATTACH;
1147 laDoAttach(pCtx);
1148 }
1149 } break;
1150
1151 case LA_DO_DETACH:
1152 {
1153 if (pCtx->u32PrevAction != LA_DO_DETACH)
1154 {
1155 pCtx->u32PrevAction = LA_DO_DETACH;
1156 laDoDetach(pCtx);
1157 }
1158 } break;
1159
1160 case LA_DO_DETACH_AND_ATTACH:
1161 {
1162 if (pCtx->u32PrevAction != LA_DO_DETACH)
1163 {
1164 pCtx->u32PrevAction = LA_DO_DETACH;
1165 laDoDetach(pCtx);
1166 }
1167 pCtx->u32PrevAction = LA_DO_ATTACH;
1168 laDoAttach(pCtx);
1169 } break;
1170
1171 case LA_DO_ATTACH_AND_DETACH:
1172 {
1173 if (pCtx->u32PrevAction != LA_DO_ATTACH)
1174 {
1175 pCtx->u32PrevAction = LA_DO_ATTACH;
1176 laDoAttach(pCtx);
1177 }
1178 pCtx->u32PrevAction = LA_DO_DETACH;
1179 laDoDetach(pCtx);
1180 } break;
1181
1182 case LA_DO_NOTHING:
1183 default:
1184 break;
1185 }
1186
1187 pCtx->u32Action = LA_DO_NOTHING;
1188
1189 LogFlowFunc(("laDoActions: leave\n"));
1190}
1191
1192DECLCALLBACK(int) VBoxLAInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
1193{
1194 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
1195 AssertPtrReturn(ppInstance, VERR_INVALID_POINTER);
1196
1197 LogFlowFuncEnter();
1198
1199 PVBOXLACONTEXT pCtx = &g_Ctx; /* Only one instance at the moment. */
1200 AssertPtr(pCtx);
1201
1202 pCtx->pEnv = pEnv;
1203
1204 DWORD dwValue = 0;
1205 if ( laGetRegistryDWORD(L"SOFTWARE\\Oracle\\VirtualBox Guest Additions", L"VBoxTrayLog", &dwValue)
1206 && (dwValue & 0x10) != 0)
1207 {
1208 pCtx->fLogEnabled = true;
1209 }
1210 else
1211 {
1212 pCtx->fLogEnabled = false;
1213 }
1214
1215 /* DetachOnDisconnect is enabled by default. */
1216 dwValue = 0x02;
1217 if ( laGetRegistryDWORD(L"SOFTWARE\\Oracle\\VirtualBox Guest Additions", L"VBoxTrayLA", &dwValue)
1218 && (dwValue & 0x02) == 0)
1219 {
1220 pCtx->fDetachOnDisconnect = false;
1221 }
1222 else
1223 {
1224 pCtx->fDetachOnDisconnect = true;
1225 }
1226
1227 LogRel(("LA: DetachOnDisconnect=%RTbool\n", pCtx->fDetachOnDisconnect));
1228
1229 int rc = VbglR3GuestPropConnect(&pCtx->u32GuestPropHandle);
1230 if (RT_FAILURE(rc))
1231 return rc;
1232
1233 RTListInit(&pCtx->listAttachActions);
1234 RTListInit(&pCtx->listDetachActions);
1235
1236 RT_ZERO(pCtx->activeClient);
1237
1238 *(void **)&pCtx->pfnProcessIdToSessionId = RTLdrGetSystemSymbol("kernel32.dll", "ProcessIdToSessionId");
1239
1240 *ppInstance = pCtx;
1241 LogFlowFuncLeaveRC(VINF_SUCCESS);
1242 return VINF_SUCCESS;
1243}
1244
1245DECLCALLBACK(void) VBoxLADestroy(void *pInstance)
1246{
1247 AssertPtrReturnVoid(pInstance);
1248
1249 LogFlowFunc(("Destroying pInstance=%p\n", pInstance));
1250
1251 PVBOXLACONTEXT pCtx = (PVBOXLACONTEXT)pInstance;
1252 AssertPtr(pCtx);
1253
1254 if (pCtx->u32GuestPropHandle != 0)
1255 {
1256 VbglR3GuestPropDisconnect(pCtx->u32GuestPropHandle);
1257 }
1258
1259 ActionExecutorDeleteActions(&pCtx->listAttachActions);
1260 ActionExecutorDeleteActions(&pCtx->listDetachActions);
1261
1262 pCtx->pfnProcessIdToSessionId = NULL;
1263}
1264
1265/*
1266 * Thread function to wait for and process property changes
1267 */
1268DECLCALLBACK(int) VBoxLAWorker(void *pInstance, bool volatile *pfShutdown)
1269{
1270 AssertPtr(pInstance);
1271 LogFlowFunc(("pInstance=%p\n", pInstance));
1272
1273 /*
1274 * Tell the control thread that it can continue
1275 * spawning services.
1276 */
1277 RTThreadUserSignal(RTThreadSelf());
1278
1279 PVBOXLACONTEXT pCtx = (PVBOXLACONTEXT)pInstance;
1280
1281 /*
1282 * On name change event (/VirtualBox/HostInfo/VRDP/Client/%ID%/Name)
1283 * Store the name in the registry (HKCU\Volatile Environment\UTCINFO_CLIENTNAME).
1284 * On a client attach event (/VirtualBox/HostInfo/VRDP/Client/%ID%/Attach -> 1):
1285 * Execute ReconnectActions
1286 * On a client detach event (/VirtualBox/HostInfo/VRDP/Client/%ID%/Attach -> 0):
1287 * Execute DisconnectActions
1288 *
1289 * The active connected client id is /VirtualBox/HostInfo/VRDP/ActiveClientClient.
1290 */
1291
1292 if (!ActionExecutorEnumerateRegistryKey(g_pwszRegKeyReconnectActions, &pCtx->listAttachActions))
1293 {
1294 LogFlowFunc(("Can't enumerate registry key %ls\n", g_pwszRegKeyReconnectActions));
1295 }
1296 if (!ActionExecutorEnumerateRegistryKey(g_pwszRegKeyDisconnectActions, &pCtx->listDetachActions))
1297 {
1298 LogFlowFunc(("Can't enumerate registry key %ls\n", g_pwszRegKeyDisconnectActions));
1299 }
1300
1301 /* A non zero timestamp in the past. */
1302 pCtx->u64LastQuery = 1;
1303 /* Start at Detached state. */
1304 pCtx->u32PrevAction = LA_DO_DETACH;
1305
1306 int rc;
1307
1308 for (;;)
1309 {
1310 /* Query current ActiveClient.
1311 * if it differs from the current active client
1312 * rebuild the context;
1313 * wait with timeout for properties change since the active client was changed;
1314 * if 'Name' was changed
1315 * update the name;
1316 * if 'Attach' was changed
1317 * do respective actions.
1318 * remember the query timestamp;
1319 */
1320 uint64_t u64Timestamp = 0;
1321 uint32_t u32ActiveClientId = 0;
1322 rc = laGetActiveClient(pCtx, &u64Timestamp, &u32ActiveClientId);
1323
1324 if (RT_SUCCESS(rc))
1325 {
1326 bool fClientIdChanged = pCtx->activeClient.u32ClientId != u32ActiveClientId;
1327
1328 if (fClientIdChanged)
1329 {
1330 rc = laUpdateCurrentState(pCtx, u32ActiveClientId, u64Timestamp);
1331 }
1332
1333 if (RT_SUCCESS(rc))
1334 {
1335 if (pCtx->activeClient.u32ClientId != 0)
1336 {
1337 rc = laWait(pCtx, &u64Timestamp, 1000);
1338
1339 if (RT_SUCCESS(rc))
1340 {
1341 laProcessAttach(pCtx);
1342
1343 laProcessClientInfo(pCtx);
1344
1345 laDoActions(pCtx);
1346
1347 pCtx->u64LastQuery = u64Timestamp;
1348 }
1349 }
1350 else
1351 {
1352 /* If the client has been disconnected, do the detach actions. */
1353 if ( pCtx->fDetachOnDisconnect
1354 && fClientIdChanged)
1355 {
1356 LogFlowFunc(("Client disconnected\n"));
1357
1358 /* laDoActions will prevent a repeated detach action. So if there
1359 * was a detach already, then this detach will be ignored.
1360 */
1361 pCtx->u32Action = LA_DO_DETACH;
1362
1363 laDoActions(pCtx);
1364
1365 pCtx->u64LastQuery = u64Timestamp;
1366 }
1367 }
1368 }
1369 }
1370
1371 /*
1372 * Check if it is time to exit.
1373 * If the code above failed, wait a bit until repeating to avoid a loop.
1374 * Otherwise just check if the stop event was signalled.
1375 */
1376 RTMSINTERVAL msWait;
1377 if ( rc == VERR_NOT_FOUND
1378 || pCtx->activeClient.u32ClientId == 0)
1379 {
1380 /* No connections, wait longer. */
1381 msWait = 5000;
1382 rc = VINF_SUCCESS;
1383 }
1384 else if (RT_FAILURE(rc))
1385 {
1386 static int s_iBitchedAboutFailedGetActiveClient = 0;
1387 if (s_iBitchedAboutFailedGetActiveClient++ < 32)
1388 LogRel(("LA: Retrieving current client(s) failed with %Rrc\n", rc));
1389
1390 msWait = 10000;
1391 }
1392 else
1393 msWait = 0;
1394
1395 if (*pfShutdown)
1396 break;
1397
1398 if (msWait)
1399 RTThreadSleep(msWait);
1400 }
1401
1402 LogFlowFuncLeaveRC(rc);
1403 return rc;
1404}
1405
1406/**
1407 * The service description.
1408 */
1409VBOXSERVICEDESC g_SvcDescLA =
1410{
1411 /* pszName. */
1412 "LA",
1413 /* pszDescription. */
1414 "Location Awareness",
1415 /* methods */
1416 VBoxLAInit,
1417 VBoxLAWorker,
1418 NULL /* pfnStop */,
1419 VBoxLADestroy
1420};
1421
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette