VirtualBox

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

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

VBoxTray: different registry keys for 64 bit guests

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