VirtualBox

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

Last change on this file was 99828, checked in by vboxsync, 12 months ago

*: A bunch of adjustments that allows using /permissive- with Visual C++ (qt 6.x necessity).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use