VirtualBox

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

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

scm copyright and license note update

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

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