VirtualBox

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

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

VBoxTray: monitor the guest properties

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