VirtualBox

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

Last change on this file since 43267 was 43224, checked in by vboxsync, 12 years ago

VBoxTray: RTMemFree in error case

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