VirtualBox

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

Last change on this file since 78440 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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