VirtualBox

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

Last change on this file since 40797 was 40797, checked in by vboxsync, 13 years ago

VBoxTray: optionally enable loggingkmd

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