VirtualBox

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

Last change on this file since 55628 was 51469, checked in by vboxsync, 11 years ago

VBoxTray: Logging; ripped out all custom logging.

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