VirtualBox

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

Last change on this file since 69360 was 64766, checked in by vboxsync, 8 years ago

src/VBox: Make the use of the iterator for RTListForEach()/RTListForEachSafe() more obvious. There is no need to initialize the iterator and we also must not depend on the iterator being NULL if the list was empty.

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

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