VirtualBox

source: vbox/trunk/src/VBox/Main/src-global/win/VirtualBoxSDSImpl.cpp@ 95421

Last change on this file since 95421 was 95353, checked in by vboxsync, 3 years ago

FE/VBoxAutostart/adi: Added documentation for running in session 0 + made running in session 0 runtime-configurable through the Windows registry (disabled by default). ​bugref:9341

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.9 KB
Line 
1/* $Id: VirtualBoxSDSImpl.cpp 95353 2022-06-23 10:35:25Z vboxsync $ */
2/** @file
3 * VBox Global COM Class implementation.
4 */
5
6/*
7 * Copyright (C) 2015-2022 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_VIRTUALBOXSDS
23#include <VBox/com/VirtualBox.h>
24#include <VBox/com/utils.h>
25#include "VirtualBoxSDSImpl.h"
26
27#include "AutoCaller.h"
28#include "LoggingNew.h"
29#include "Wrapper.h" /* for ArrayBSTRInConverter */
30
31#include <iprt/errcore.h>
32#include <iprt/asm.h>
33#include <iprt/critsect.h>
34#include <iprt/env.h>
35#include <iprt/err.h>
36#include <iprt/mem.h>
37#include <iprt/path.h>
38#include <iprt/process.h>
39#include <iprt/system.h>
40
41#include <rpcasync.h>
42#include <rpcdcep.h>
43#include <sddl.h>
44#include <lmcons.h> /* UNLEN */
45
46#include "MachineLaunchVMCommonWorker.h"
47
48
49/*********************************************************************************************************************************
50* Defined Constants And Macros *
51*********************************************************************************************************************************/
52#define INTERACTIVE_SID_FLAG 0x1
53#define LOCAL_SID_FLAG 0x2
54#define LOGON_SID_FLAG 0x4
55#define IS_INTERACTIVE (LOCAL_SID_FLAG|INTERACTIVE_SID_FLAG|LOGON_SID_FLAG)
56
57
58/**
59 * Per user data.
60 *
61 * @note We never delete instances of this class, except in case of an insertion
62 * race. This allows us to separate the map lock from the user data lock
63 * and avoid DoS issues.
64 */
65class VBoxSDSPerUserData
66{
67public:
68 /** The SID (secure identifier) for the user. This is the key. */
69 com::Utf8Str m_strUserSid;
70 /** The user name (if we could get it). */
71 com::Utf8Str m_strUsername;
72 /** The VBoxSVC chosen to instantiate CLSID_VirtualBox.
73 * This is NULL if not set. */
74 ComPtr<IVBoxSVCRegistration> m_ptrTheChosenOne;
75 /** The PID of the chosen one. */
76 RTPROCESS m_pidTheChosenOne;
77 /** The tick count when the process in Windows session 0 started */
78 uint32_t m_tickTheChosenOne;
79 /** The current watcher thread index, UINT32_MAX if not watched. */
80 uint32_t m_iWatcher;
81 /** The chosen one revision number.
82 * This is used to detect races while waiting for a full watcher queue. */
83 uint32_t volatile m_iTheChosenOneRevision;
84private:
85 /** Reference count to make destruction safe wrt hung callers.
86 * (References are retain while holding the map lock in some form, but
87 * released while holding no locks.) */
88 uint32_t volatile m_cRefs;
89 /** Critical section protecting everything here. */
90 RTCRITSECT m_Lock;
91
92public:
93 VBoxSDSPerUserData(com::Utf8Str const &a_rStrUserSid, com::Utf8Str const &a_rStrUsername)
94 : m_strUserSid(a_rStrUserSid)
95 , m_strUsername(a_rStrUsername)
96 , m_pidTheChosenOne(NIL_RTPROCESS)
97 , m_tickTheChosenOne(0)
98#ifdef WITH_WATCHER
99 , m_iWatcher(UINT32_MAX)
100 , m_iTheChosenOneRevision(0)
101#endif
102 , m_cRefs(1)
103 {
104 RTCritSectInit(&m_Lock);
105 }
106
107 ~VBoxSDSPerUserData()
108 {
109 RTCritSectDelete(&m_Lock);
110 i_unchooseTheOne(true /*fIrregular*/);
111 }
112
113 uint32_t i_retain()
114 {
115 uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
116 Assert(cRefs > 1);
117 return cRefs;
118 }
119
120 uint32_t i_release()
121 {
122 uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
123 Assert(cRefs < _1K);
124 if (cRefs == 0)
125 delete this;
126 return cRefs;
127 }
128
129 void i_lock()
130 {
131 RTCritSectEnter(&m_Lock);
132 }
133
134 void i_unlock()
135 {
136 RTCritSectLeave(&m_Lock);
137 }
138
139 /** Reset the chosen one. */
140 void i_unchooseTheOne(bool fIrregular)
141 {
142 if (m_ptrTheChosenOne.isNotNull())
143 {
144 if (!fIrregular)
145 m_ptrTheChosenOne.setNull();
146 else
147 {
148 LogRel(("i_unchooseTheOne: Irregular release ... (pid=%d (%#x) user=%s sid=%s)\n",
149 m_pidTheChosenOne, m_pidTheChosenOne, m_strUsername.c_str(), m_strUserSid.c_str()));
150 m_ptrTheChosenOne.setNull();
151 LogRel(("i_unchooseTheOne: ... done.\n"));
152 }
153 }
154 m_pidTheChosenOne = NIL_RTPROCESS;
155 m_tickTheChosenOne = 0;
156 }
157
158};
159
160
161
162/*********************************************************************************************************************************
163* VirtualBoxSDS - constructor / destructor *
164*********************************************************************************************************************************/
165
166VirtualBoxSDS::VirtualBoxSDS()
167 : m_cVBoxSvcProcesses(0)
168#ifdef WITH_WATCHER
169 , m_cWatchers(0)
170 , m_papWatchers(NULL)
171#endif
172{
173}
174
175
176VirtualBoxSDS::~VirtualBoxSDS()
177{
178#ifdef WITH_WATCHER
179 i_shutdownAllWatchers();
180 RTMemFree(m_papWatchers);
181 m_papWatchers = NULL;
182 m_cWatchers = 0;
183#endif
184}
185
186
187HRESULT VirtualBoxSDS::FinalConstruct()
188{
189 LogRelFlowThisFuncEnter();
190
191 int vrc = RTCritSectRwInit(&m_MapCritSect);
192 AssertLogRelRCReturn(vrc, E_FAIL);
193
194#ifdef WITH_WATCHER
195 vrc = RTCritSectInit(&m_WatcherCritSect);
196 AssertLogRelRCReturn(vrc, E_FAIL);
197#endif
198
199 LogRelFlowThisFuncLeave();
200 return S_OK;
201}
202
203
204void VirtualBoxSDS::FinalRelease()
205{
206 LogRelFlowThisFuncEnter();
207
208#ifdef WITH_WATCHER
209 i_shutdownAllWatchers();
210 RTCritSectDelete(&m_WatcherCritSect);
211#endif
212
213 RTCritSectRwDelete(&m_MapCritSect);
214
215 for (UserDataMap_T::iterator it = m_UserDataMap.begin(); it != m_UserDataMap.end(); ++it)
216 {
217 VBoxSDSPerUserData *pUserData = it->second;
218 if (pUserData)
219 {
220 it->second = NULL;
221 pUserData->i_release();
222 }
223 }
224
225 LogRelFlowThisFuncLeave();
226}
227
228/* static */
229bool VirtualBoxSDS::i_isFeatureEnabled(com::Utf8Str const &a_rStrFeature)
230{
231 HKEY hKey;
232 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Oracle\\VirtualBox\\VBoxSDS", 0, KEY_READ, &hKey);
233 /* Treat any errors as the feature is off. Because the actual error value doesn't matter. */
234 if (lrc != ERROR_SUCCESS)
235 return false;
236
237 PRTUTF16 pwszFeature;
238 int rc = RTStrToUtf16(a_rStrFeature.c_str(), &pwszFeature);
239 AssertRCReturn(rc, false);
240
241 DWORD dwType = 0;
242 DWORD dwValue = 0;
243 DWORD cbValue = sizeof(DWORD);
244 lrc = RegQueryValueExW(hKey, pwszFeature, NULL, &dwType, (LPBYTE)&dwValue, &cbValue);
245
246 RTUtf16Free(pwszFeature);
247
248 bool const fEnabled = lrc == ERROR_SUCCESS
249 && dwType == REG_DWORD
250 /* A lot of people are used to putting 0xFF or similar to DWORDs for enabling stuff. */
251 && dwValue >= 1;
252
253 RegCloseKey(hKey);
254 return fEnabled;
255}
256
257
258/*********************************************************************************************************************************
259* VirtualBoxSDS - IVirtualBoxSDS methods *
260*********************************************************************************************************************************/
261
262/* SDS plan B interfaces: */
263STDMETHODIMP VirtualBoxSDS::RegisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid, IUnknown **aExistingVirtualBox)
264{
265 LogRel(("registerVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
266
267 /*
268 * Get the caller PID so we can validate the aPid parameter with the other two.
269 * The V2 structure requires Vista or later, so fake it if older.
270 */
271 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
272 RPC_STATUS rcRpc;
273 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
274 rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
275 else
276 {
277 CallAttribs.ClientPID = (HANDLE)(intptr_t)aPid;
278 rcRpc = RPC_S_OK;
279 }
280
281 HRESULT hrc;
282 if ( RT_VALID_PTR(aVBoxSVC)
283 && RT_VALID_PTR(aExistingVirtualBox)
284 && rcRpc == RPC_S_OK
285 && (intptr_t)CallAttribs.ClientPID == aPid)
286 {
287 *aExistingVirtualBox = NULL;
288 /*
289 * Get the client user SID and name.
290 */
291 com::Utf8Str strSid;
292 com::Utf8Str strUsername;
293 if (i_getClientUserSid(&strSid, &strUsername))
294 {
295 VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername); /* (returns holding the lock) */
296 if (pUserData)
297 {
298 /*
299 * If there already is a chosen one, ask it for a IVirtualBox instance
300 * to return to the caller. Should it be dead or unresponsive, the caller
301 * takes its place.
302 */
303 if (pUserData->m_ptrTheChosenOne.isNotNull())
304 {
305 try
306 {
307 hrc = pUserData->m_ptrTheChosenOne->GetVirtualBox(aExistingVirtualBox);
308 /* seems the VBoxSVC in windows session 0 is not yet finished object creation.
309 * Give it a time. */
310 if (FAILED(hrc) && GetTickCount() - pUserData->m_tickTheChosenOne < 60 * 1000)
311 hrc = E_PENDING;
312 }
313 catch (...)
314 {
315 LogRel(("registerVBoxSVC: Unexpected exception calling GetVirtualBox!!\n"));
316 hrc = E_FAIL;
317 }
318 if (FAILED_DEAD_INTERFACE(hrc))
319 {
320 LogRel(("registerVBoxSVC: Seems VBoxSVC instance died. Dropping it and letting caller take over. (hrc=%Rhrc)\n", hrc));
321#ifdef WITH_WATCHER
322 i_stopWatching(pUserData, pUserData->m_pidTheChosenOne);
323#endif
324 pUserData->i_unchooseTheOne(true /*fIrregular*/);
325 hrc = S_OK;
326 }
327 }
328 else
329 hrc = S_OK;
330
331 /* No chosen one? Make the caller the new chosen one! */
332 if (SUCCEEDED(hrc) && pUserData->m_ptrTheChosenOne.isNull())
333 {
334#ifdef VBOX_WITH_VBOXSVC_SESSION_0
335 DWORD dwSessionId = 0;
336 if (VirtualBoxSDS::i_isFeatureEnabled("ServerSession0"))
337 {
338 /* Get user token. */
339 HANDLE hThreadToken = NULL;
340 hrc = CoImpersonateClient();
341 if (SUCCEEDED(hrc))
342 {
343 hrc = E_FAIL;
344 if (OpenThreadToken(GetCurrentThread(),
345 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE
346 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
347 TRUE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
348 &hThreadToken))
349 {
350 HANDLE hNewToken;
351 if (DuplicateTokenEx(hThreadToken, MAXIMUM_ALLOWED, NULL /*SecurityAttribs*/,
352 SecurityIdentification, TokenPrimary, &hNewToken))
353 {
354 CloseHandle(hThreadToken);
355 hThreadToken = hNewToken;
356 hrc = S_OK;
357 }
358 else
359 LogRel(("registerVBoxSVC: DuplicateTokenEx failed: %ld\n", GetLastError()));
360 }
361 else
362 LogRel(("registerVBoxSVC: OpenThreadToken failed: %ld\n", GetLastError()));
363
364 CoRevertToSelf();
365 }
366 else
367 LogRel(("registerVBoxSVC: CoImpersonateClient failed: %Rhrc\n", hrc));
368
369 /* check windows session */
370 if (SUCCEEDED(hrc) && hThreadToken != NULL)
371 {
372 hrc = E_FAIL;
373 DWORD cbSessionId = sizeof(DWORD);
374 if (GetTokenInformation(hThreadToken, TokenSessionId, (LPVOID)&dwSessionId, cbSessionId, &cbSessionId))
375 {
376 if (cbSessionId == sizeof(DWORD))
377 hrc = S_OK;
378 else
379 LogRel(("registerVBoxSVC: GetTokenInformation return value has invalid size\n"));
380 }
381 else
382 LogRel(("registerVBoxSVC: GetTokenInformation failed: %ld\n", GetLastError()));
383 }
384 /* Either the "VBoxSVC in windows session 0" feature is off or the request from VBoxSVC running
385 * in windows session 0. */
386 if (SUCCEEDED(hrc) && dwSessionId != 0)
387 {
388 /* if VBoxSVC in the Windows session 0 is not started or if it did not
389 * registered during a minute, start new one */
390 if ( pUserData->m_pidTheChosenOne == NIL_RTPROCESS
391 || GetTickCount() - pUserData->m_tickTheChosenOne > 60 * 1000)
392 {
393 uint32_t uSessionId = 0;
394 if (SetTokenInformation(hThreadToken, TokenSessionId, &uSessionId, sizeof(uint32_t)))
395 {
396 /* start VBoxSVC process */
397 /* Get the path to the executable directory w/ trailing slash: */
398 char szPath[RTPATH_MAX];
399 int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath));
400 AssertRCReturn(vrc, vrc);
401 size_t cbBufLeft = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
402 AssertReturn(cbBufLeft > 0, VERR_FILENAME_TOO_LONG);
403 char *pszNamePart = &szPath[cbBufLeft]; NOREF(pszNamePart);
404 cbBufLeft = sizeof(szPath) - cbBufLeft;
405 static const char s_szVirtualBox_exe[] = "VBoxSVC.exe";
406 vrc = RTStrCopy(pszNamePart, cbBufLeft, s_szVirtualBox_exe);
407 AssertRCReturn(vrc, vrc);
408 const char *apszArgs[] =
409 {
410 szPath,
411 "--registervbox",
412 NULL
413 };
414
415 RTPROCESS pid;
416 vrc = RTProcCreateEx(szPath,
417 apszArgs,
418 RTENV_DEFAULT,
419 RTPROC_FLAGS_TOKEN_SUPPLIED,
420 NULL, NULL, NULL, NULL, NULL, &hThreadToken, &pid);
421
422 if (RT_SUCCESS(vrc))
423 {
424 pUserData->m_pidTheChosenOne = pid;
425 pUserData->m_tickTheChosenOne = GetTickCount();
426 hrc = E_PENDING;
427 }
428 else
429 LogRel(("registerVBoxSVC: Create VBoxSVC process failed: %Rrc\n", vrc));
430 }
431 else
432 {
433 hrc = E_FAIL;
434 LogRel(("registerVBoxSVC: SetTokenInformation failed: %ld\n", GetLastError()));
435 }
436 }
437 else /* the VBoxSVC in Windows session 0 already started */
438 hrc = E_PENDING;
439 }
440 CloseHandle(hThreadToken);
441 } /* Feature enabled */
442
443 if (SUCCEEDED(hrc) && dwSessionId == 0)
444 {
445#endif
446 LogRel(("registerVBoxSVC: Making aPid=%u (%#x) the chosen one for user %s (%s)!\n",
447 aPid, aPid, pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
448#ifdef WITH_WATCHER
449 /* Open the process so we can watch it. */
450 HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE /*fInherit*/, aPid);
451 if (hProcess == NULL)
452 hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE /*fInherit*/, aPid);
453 if (hProcess == NULL)
454 hProcess = OpenProcess(SYNCHRONIZE, FALSE /*fInherit*/, aPid);
455 if (hProcess != NULL)
456 {
457 if (i_watchIt(pUserData, hProcess, aPid))
458#endif
459 {
460 /* Make it official... */
461 pUserData->m_ptrTheChosenOne = aVBoxSVC;
462 pUserData->m_pidTheChosenOne = aPid;
463 hrc = S_OK;
464 }
465#ifdef WITH_WATCHER
466 else
467 {
468
469 LogRel(("registerVBoxSVC: i_watchIt failed!\n"));
470 hrc = RPC_E_OUT_OF_RESOURCES;
471 }
472 }
473 else
474 {
475 LogRel(("registerVBoxSVC: OpenProcess() failed: %ld\n", GetLastError()));
476 hrc = E_ACCESSDENIED;
477 }
478#endif
479#ifdef VBOX_WITH_VBOXSVC_SESSION_0
480 }
481#endif
482 }
483 pUserData->i_unlock();
484 pUserData->i_release();
485 }
486 else
487 hrc = E_OUTOFMEMORY;
488 }
489 else
490 hrc = E_FAIL;
491 }
492 else if ( !RT_VALID_PTR(aVBoxSVC)
493 || !RT_VALID_PTR(aExistingVirtualBox))
494 hrc = E_INVALIDARG;
495 else if (rcRpc != RPC_S_OK)
496 {
497 LogRel(("registerVBoxSVC: rcRpc=%d (%#x)!\n", rcRpc, rcRpc));
498 hrc = E_UNEXPECTED;
499 }
500 else
501 {
502 LogRel(("registerVBoxSVC: Client PID mismatch: aPid=%d (%#x), RPC ClientPID=%zd (%#zx)\n",
503 aPid, aPid, CallAttribs.ClientPID, CallAttribs.ClientPID));
504 hrc = E_INVALIDARG;
505 }
506 LogRel2(("VirtualBoxSDS::registerVBoxSVC: returns %Rhrc aExistingVirtualBox=%p\n", hrc, (IUnknown *)aExistingVirtualBox));
507 return hrc;
508}
509
510
511STDMETHODIMP VirtualBoxSDS::DeregisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid)
512{
513 LogRel(("deregisterVBoxSVC: aVBoxSVC=%p aPid=%u (%#x)\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid, aPid));
514 HRESULT hrc;
515 if (RT_VALID_PTR(aVBoxSVC))
516 {
517 /* Get the client user SID and name. */
518 com::Utf8Str strSid;
519 com::Utf8Str strUsername;
520 if (i_getClientUserSid(&strSid, &strUsername))
521 {
522 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(strSid);
523 if (pUserData)
524 {
525 if (aVBoxSVC == (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne)
526 {
527 LogRel(("deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
528 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
529#ifdef WITH_WATCHER
530 i_stopWatching(pUserData, pUserData->m_pidTheChosenOne);
531#endif
532 pUserData->i_unchooseTheOne(false /*fIrregular*/);
533 }
534 else
535 LogRel(("deregisterVBoxSVC: not the choosen one (%p != %p)\n",
536 (IVBoxSVCRegistration *)aVBoxSVC, (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne));
537 pUserData->i_unlock();
538 pUserData->i_release();
539
540 hrc = S_OK;
541 }
542 else
543 {
544 LogRel(("deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
545 strSid.c_str(), strUsername.c_str(), aPid));
546 hrc = S_OK;
547 }
548 }
549 else
550 hrc = E_FAIL;
551 }
552 else
553 hrc = E_INVALIDARG;
554 LogRel2(("VirtualBoxSDS::deregisterVBoxSVC: returns %Rhrc\n", hrc));
555 return hrc;
556}
557
558
559STDMETHODIMP VirtualBoxSDS::LaunchVMProcess(IN_BSTR aMachine, IN_BSTR aComment, IN_BSTR aFrontend,
560 ComSafeArrayIn(IN_BSTR, aEnvironmentChanges),
561 IN_BSTR aCmdOptions, ULONG aSessionId, ULONG *aPid)
562{
563 /*
564 * Convert parameters to UTF-8.
565 */
566 Utf8Str strMachine(aMachine);
567 Utf8Str strComment(aComment);
568 Utf8Str strFrontend(aFrontend);
569 ArrayBSTRInConverter aStrEnvironmentChanges(ComSafeArrayInArg(aEnvironmentChanges));
570 Utf8Str strCmdOptions(aCmdOptions);
571
572 /*
573 * Impersonate the caller.
574 */
575 HRESULT hrc = CoImpersonateClient();
576 if (SUCCEEDED(hrc))
577 {
578 try
579 {
580 /*
581 * Try launch the VM process as the client.
582 */
583 RTPROCESS pid;
584 AssertCompile(sizeof(aSessionId) == sizeof(uint32_t));
585 int vrc = ::MachineLaunchVMCommonWorker(strMachine, strComment, strFrontend, aStrEnvironmentChanges.array(),
586 strCmdOptions, Utf8Str(),
587 RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_SERVICE
588 | RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_DESIRED_SESSION_ID,
589 &aSessionId, pid);
590 if (RT_SUCCESS(vrc))
591 {
592 *aPid = (ULONG)pid;
593 LogRel(("VirtualBoxSDS::LaunchVMProcess: launchVM succeeded\n"));
594 }
595 else if (vrc == VERR_INVALID_PARAMETER)
596 {
597 hrc = E_INVALIDARG;
598 LogRel(("VirtualBoxSDS::LaunchVMProcess: launchVM failed: %Rhrc\n", hrc));
599 }
600 else
601 {
602 hrc = VBOX_E_IPRT_ERROR;
603 LogRel(("VirtualBoxSDS::LaunchVMProcess: launchVM failed: %Rhrc (%Rrc)\n", hrc));
604 }
605 }
606 catch (...)
607 {
608 hrc = E_UNEXPECTED;
609 }
610 CoRevertToSelf();
611 }
612 else
613 LogRel(("VirtualBoxSDS::LaunchVMProcess: CoImpersonateClient failed: %Rhrc\n", hrc));
614 return hrc;
615}
616
617
618/*********************************************************************************************************************************
619* VirtualBoxSDS - Internal Methods *
620*********************************************************************************************************************************/
621
622/*static*/ bool VirtualBoxSDS::i_getClientUserSid(com::Utf8Str *a_pStrSid, com::Utf8Str *a_pStrUsername)
623{
624 bool fRet = false;
625 a_pStrSid->setNull();
626 a_pStrUsername->setNull();
627
628 HRESULT hrc = CoImpersonateClient();
629 if (SUCCEEDED(hrc))
630 {
631 HANDLE hToken = INVALID_HANDLE_VALUE;
632 if (::OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE /*OpenAsSelf*/, &hToken))
633 {
634 CoRevertToSelf();
635
636 union
637 {
638 TOKEN_USER TokenUser;
639 uint8_t abPadding[SECURITY_MAX_SID_SIZE + 256];
640 WCHAR wszUsername[UNLEN + 1];
641 } uBuf;
642 RT_ZERO(uBuf);
643 DWORD cbActual = 0;
644 if (::GetTokenInformation(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbActual))
645 {
646 WCHAR *pwszString;
647 if (ConvertSidToStringSidW(uBuf.TokenUser.User.Sid, &pwszString))
648 {
649 try
650 {
651 *a_pStrSid = pwszString;
652 a_pStrSid->toUpper(); /* (just to be on the safe side) */
653 fRet = true;
654 }
655 catch (std::bad_alloc &)
656 {
657 LogRel(("i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
658 }
659 LocalFree((HLOCAL)pwszString);
660
661 /*
662 * Get the username too. We don't care if this step fails.
663 */
664 if (fRet)
665 {
666 WCHAR wszUsername[UNLEN * 2 + 1];
667 DWORD cwcUsername = RT_ELEMENTS(wszUsername);
668 WCHAR wszDomain[UNLEN * 2 + 1];
669 DWORD cwcDomain = RT_ELEMENTS(wszDomain);
670 SID_NAME_USE enmNameUse;
671 if (LookupAccountSidW(NULL, uBuf.TokenUser.User.Sid, wszUsername, &cwcUsername,
672 wszDomain, &cwcDomain, &enmNameUse))
673 {
674 wszUsername[RT_ELEMENTS(wszUsername) - 1] = '\0';
675 wszDomain[RT_ELEMENTS(wszDomain) - 1] = '\0';
676 try
677 {
678 *a_pStrUsername = wszDomain;
679 a_pStrUsername->append('/');
680 a_pStrUsername->append(Utf8Str(wszUsername));
681 }
682 catch (std::bad_alloc &)
683 {
684 LogRel(("i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
685 a_pStrUsername->setNull();
686 }
687 }
688 else
689 LogRel(("i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
690 GetLastError(), cwcUsername, cwcDomain));
691 }
692 }
693 else
694 LogRel(("i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
695 }
696 else
697 LogRel(("i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
698 CloseHandle(hToken);
699 }
700 else
701 {
702 CoRevertToSelf();
703 LogRel(("i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
704 }
705 }
706 else
707 LogRel(("i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
708 return fRet;
709}
710
711
712/**
713 * Looks up the given user.
714 *
715 * @returns Pointer to the LOCKED and RETAINED per user data.
716 * NULL if not found.
717 * @param a_rStrUserSid The user SID.
718 */
719VBoxSDSPerUserData *VirtualBoxSDS::i_lookupPerUserData(com::Utf8Str const &a_rStrUserSid)
720{
721 int vrc = RTCritSectRwEnterShared(&m_MapCritSect);
722 if (RT_SUCCESS(vrc))
723 {
724
725 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
726 if (it != m_UserDataMap.end())
727 {
728 VBoxSDSPerUserData *pUserData = it->second;
729 pUserData->i_retain();
730
731 RTCritSectRwLeaveShared(&m_MapCritSect);
732
733 pUserData->i_lock();
734 return pUserData;
735 }
736
737 RTCritSectRwLeaveShared(&m_MapCritSect);
738 }
739 return NULL;
740}
741
742
743/**
744 * Looks up the given user, creating it if not found
745 *
746 * @returns Pointer to the LOCKED and RETAINED per user data.
747 * NULL on allocation error.
748 * @param a_rStrUserSid The user SID.
749 * @param a_rStrUsername The user name if available.
750 */
751VBoxSDSPerUserData *VirtualBoxSDS::i_lookupOrCreatePerUserData(com::Utf8Str const &a_rStrUserSid,
752 com::Utf8Str const &a_rStrUsername)
753{
754 /*
755 * Try do a simple lookup first.
756 */
757 VBoxSDSPerUserData *pUserData = i_lookupPerUserData(a_rStrUserSid);
758 if (!pUserData)
759 {
760 /*
761 * SID is not in map, create a new one.
762 */
763 try
764 {
765 pUserData = new VBoxSDSPerUserData(a_rStrUserSid, a_rStrUsername);
766 }
767 catch (std::bad_alloc &)
768 {
769 pUserData = NULL;
770 }
771 if (pUserData)
772 {
773 /*
774 * Insert it. We must check if someone raced us here.
775 */
776 VBoxSDSPerUserData *pUserDataFree = pUserData;
777 pUserData->i_lock();
778
779 int vrc = RTCritSectRwEnterExcl(&m_MapCritSect);
780 if (RT_SUCCESS(vrc))
781 {
782
783 UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
784 if (it == m_UserDataMap.end())
785 {
786 try
787 {
788 m_UserDataMap[a_rStrUserSid] = pUserData;
789 pUserData->i_retain();
790 }
791 catch (std::bad_alloc &)
792 {
793 pUserData = NULL;
794 }
795 }
796 else
797 pUserData = NULL;
798
799 RTCritSectRwLeaveExcl(&m_MapCritSect);
800
801 if (pUserData)
802 LogRel(("i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
803 pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str() ));
804 else
805 {
806 pUserDataFree->i_unlock();
807 delete pUserDataFree;
808 }
809 }
810 }
811 }
812
813 return pUserData;
814}
815
816
817#ifdef WITH_WATCHER
818/**
819 * Data about what's being watched.
820 */
821typedef struct VBoxSDSWatcherData
822{
823 /** The per-user data (referenced). */
824 VBoxSDSPerUserData *pUserData;
825 /** The chosen one revision number (for handling an almost impossible race
826 * where a client terminates while making a deregistration call). */
827 uint32_t iRevision;
828 /** The PID we're watching. */
829 RTPROCESS pid;
830
831 /** Sets the members to NULL values. */
832 void setNull()
833 {
834 pUserData = NULL;
835 iRevision = UINT32_MAX;
836 pid = NIL_RTPROCESS;
837 }
838} VBoxSDSWatcherData;
839
840/**
841 * Per watcher data.
842 */
843typedef struct VBoxSDSWatcher
844{
845 /** Pointer to the VBoxSDS instance. */
846 VirtualBoxSDS *pVBoxSDS;
847 /** The thread handle. */
848 RTTHREAD hThread;
849 /** Number of references to this structure. */
850 uint32_t volatile cRefs;
851 /** Set if the thread should shut down. */
852 bool volatile fShutdown;
853 /** Number of pending items in the todo array. */
854 uint32_t cTodos;
855 /** The watcher number. */
856 uint32_t iWatcher;
857 /** The number of handles once TODOs have been taken into account. */
858 uint32_t cHandlesEffective;
859 /** Number of handles / user data items being monitored. */
860 uint32_t cHandles;
861 /** Array of handles.
862 * The zero'th entry is the event semaphore use to signal the thread. */
863 HANDLE aHandles[MAXIMUM_WAIT_OBJECTS];
864 /** Array the runs parallel to aHandles with the VBoxSVC data. */
865 VBoxSDSWatcherData aData[MAXIMUM_WAIT_OBJECTS];
866 /** Pending changes. */
867 struct
868 {
869 /** If NULL the data is being removed, otherwise it's being added and
870 * this is the process handle to watch for termination. */
871 HANDLE hProcess;
872 /** The data about what's being watched. */
873 VBoxSDSWatcherData Data;
874 } aTodos[MAXIMUM_WAIT_OBJECTS * 4];
875
876
877 /** Helper for removing a handle & data table entry. */
878 uint32_t removeHandle(uint32_t a_iEntry, uint32_t a_cHandles)
879 {
880 uint32_t cToShift = a_cHandles - a_iEntry - 1;
881 if (cToShift > 0)
882 {
883 memmove(&aData[a_iEntry], &aData[a_iEntry + 1], sizeof(aData[0]) * cToShift);
884 memmove(&aHandles[a_iEntry], &aHandles[a_iEntry + 1], sizeof(aHandles[0]) * cToShift);
885 }
886 a_cHandles--;
887 aHandles[a_cHandles] = NULL;
888 aData[a_cHandles].setNull();
889
890 return a_cHandles;
891 }
892} VBoxSDSWatcher;
893
894
895
896/**
897 * Watcher thread.
898 */
899/*static*/ DECLCALLBACK(int) VirtualBoxSDS::i_watcherThreadProc(RTTHREAD hSelf, void *pvUser)
900{
901 VBoxSDSWatcher *pThis = (VBoxSDSWatcher *)pvUser;
902 VirtualBoxSDS *pVBoxSDS = pThis->pVBoxSDS;
903 RT_NOREF(hSelf);
904
905 /*
906 * This thread may release references to IVBoxSVCRegistration objects.
907 */
908 CoInitializeEx(NULL, COINIT_MULTITHREADED);
909
910 /*
911 * The loop.
912 */
913 RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);
914 while (!pThis->fShutdown)
915 {
916 /*
917 * Deal with the todo list.
918 */
919 uint32_t cHandles = pThis->cHandles;
920 uint32_t cTodos = pThis->cTodos;
921
922 for (uint32_t i = 0; i < cTodos; i++)
923 {
924 VBoxSDSPerUserData *pUserData = pThis->aTodos[i].Data.pUserData;
925 AssertContinue(pUserData);
926 if (pThis->aTodos[i].hProcess != NULL)
927 {
928 /* Add: */
929 AssertLogRelMsgBreakStmt(cHandles < RT_ELEMENTS(pThis->aHandles),
930 ("cHandles=%u cTodos=%u i=%u iWatcher=%u\n", cHandles, cTodos, i, pThis->iWatcher),
931 pThis->fShutdown = true);
932 pThis->aHandles[cHandles] = pThis->aTodos[i].hProcess;
933 pThis->aData[cHandles] = pThis->aTodos[i].Data;
934 cHandles++;
935 }
936 else
937 {
938 /* Remove: */
939 uint32_t cRemoved = 0;
940 uint32_t j = cHandles;
941 while (j-- > 1)
942 if (pThis->aData[j].pUserData == pUserData)
943 {
944 cHandles = pThis->removeHandle(j, cHandles);
945 pUserData->i_release();
946 cRemoved++;
947 }
948 if (cRemoved != 1)
949 LogRel(("i_watcherThreadProc/#%u: Warning! cRemoved=%u pUserData=%p\n", pThis->iWatcher, cRemoved, pUserData));
950 }
951 /* Zap the entry in case we assert and leave further up. */
952 pThis->aTodos[i].Data.setNull();
953 pThis->aTodos[i].hProcess = NULL;
954 }
955
956 Assert(cHandles > 0 && cHandles <= RT_ELEMENTS(pThis->aHandles));
957 pThis->cHandles = cHandles;
958 pThis->cHandlesEffective = cHandles;
959 pThis->cTodos = 0;
960
961 if (pThis->fShutdown)
962 break;
963
964 /*
965 * Wait.
966 */
967 RTCritSectLeave(&pVBoxSDS->m_WatcherCritSect);
968
969 LogRel(("i_watcherThreadProc/#%u: Waiting on %u handles...\n", pThis->iWatcher, cHandles));
970 DWORD const dwWait = WaitForMultipleObjects(cHandles, pThis->aHandles, FALSE /*fWaitAll*/, INFINITE);
971 LogRel(("i_watcherThreadProc/#%u: ... wait returned: %#x (%d)\n", pThis->iWatcher, dwWait, dwWait));
972
973 uint32_t const iHandle = dwWait - WAIT_OBJECT_0;
974 if (iHandle < cHandles && iHandle > 0)
975 {
976 /*
977 * A VBoxSVC process has terminated.
978 *
979 * Note! We need to take the user data lock before the watcher one here.
980 */
981 VBoxSDSPerUserData * const pUserData = pThis->aData[iHandle].pUserData;
982 uint32_t const iRevision = pThis->aData[iHandle].iRevision;
983 RTPROCESS const pid = pThis->aData[iHandle].pid;
984
985 pUserData->i_lock();
986 RTCritSectEnter(&pVBoxSDS->m_WatcherCritSect);
987
988 DWORD dwExit = 0;
989 GetExitCodeProcess(pThis->aHandles[iHandle], &dwExit);
990 LogRel(("i_watcherThreadProc/#%u: %p/%s: PID %u/%#x termination detected: %d (%#x) [iRev=%u, cur %u]\n",
991 pThis->iWatcher, pUserData, pUserData->m_strUsername.c_str(), pid, pid, dwExit, dwExit,
992 iRevision, pUserData->m_iTheChosenOneRevision));
993
994 /* Remove it from the handle array. */
995 CloseHandle(pThis->aHandles[iHandle]);
996 pThis->cHandles = cHandles = pThis->removeHandle(iHandle, cHandles);
997 pThis->cHandlesEffective -= 1;
998
999 /* If the process we were watching is still the current chosen one,
1000 unchoose it and decrement the client count. Otherwise we were subject
1001 to a deregistration/termination race (unlikely). */
1002 if (pUserData->m_iTheChosenOneRevision == iRevision)
1003 {
1004 pUserData->i_unchooseTheOne(true /*fIrregular*/);
1005 pUserData->i_unlock();
1006 pVBoxSDS->i_decrementClientCount();
1007 }
1008 else
1009 pUserData->i_unlock();
1010 pUserData->i_release();
1011 }
1012 else
1013 {
1014 RTCritSectEnter(&pThis->pVBoxSDS->m_WatcherCritSect);
1015 AssertLogRelMsgBreak(iHandle == 0 || dwWait == WAIT_TIMEOUT,
1016 ("dwWait=%u (%#x) cHandles=%u\n", dwWait, dwWait, cHandles));
1017 }
1018 }
1019
1020 RTCritSectLeave(&pThis->pVBoxSDS->m_WatcherCritSect);
1021
1022 /*
1023 * In case we quit w/o being told, signal i_watchIt that we're out of action.
1024 */
1025 pThis->fShutdown = true;
1026
1027 /*
1028 * Release all our data on the way out.
1029 */
1030 uint32_t i = pThis->cHandles;
1031 while (i-- > 1)
1032 {
1033 if (pThis->aData[i].pUserData)
1034 {
1035 pThis->aData[i].pUserData->i_release();
1036 pThis->aData[i].pUserData = NULL;
1037 }
1038 if (pThis->aHandles[i])
1039 {
1040 CloseHandle(pThis->aHandles[i]);
1041 pThis->aHandles[i] = NULL;
1042 }
1043 }
1044 if (pThis->aHandles[0])
1045 {
1046 CloseHandle(pThis->aHandles[0]);
1047 pThis->aHandles[0] = NULL;
1048 }
1049
1050 i = pThis->cTodos;
1051 pThis->cTodos = 0;
1052 while (i-- > 0)
1053 {
1054 if (pThis->aTodos[i].Data.pUserData)
1055 {
1056 pThis->aTodos[i].Data.pUserData->i_release();
1057 pThis->aTodos[i].Data.pUserData = NULL;
1058 }
1059 if (pThis->aTodos[i].hProcess)
1060 {
1061 CloseHandle(pThis->aTodos[i].hProcess);
1062 pThis->aTodos[i].hProcess = NULL;
1063 }
1064 }
1065
1066 if (ASMAtomicDecU32(&pThis->cRefs) == 0)
1067 RTMemFree(pThis);
1068
1069 return VINF_SUCCESS;
1070}
1071
1072
1073/**
1074 * Starts monitoring a VBoxSVC process.
1075 *
1076 * @param pUserData The user which chosen VBoxSVC should be watched.
1077 * @param hProcess Handle to the VBoxSVC process. Consumed.
1078 * @param pid The VBoxSVC PID.
1079 * @returns Success indicator.
1080 */
1081bool VirtualBoxSDS::i_watchIt(VBoxSDSPerUserData *pUserData, HANDLE hProcess, RTPROCESS pid)
1082{
1083 RTCritSectEnter(&m_WatcherCritSect);
1084
1085 /*
1086 * Find a watcher with capacity left over (we save 8 entries for removals).
1087 */
1088 for (uint32_t i = 0; i < m_cWatchers; i++)
1089 {
1090 VBoxSDSWatcher *pWatcher = m_papWatchers[i];
1091 if ( pWatcher->cHandlesEffective < RT_ELEMENTS(pWatcher->aHandles)
1092 && !pWatcher->fShutdown)
1093 {
1094 uint32_t iTodo = pWatcher->cTodos;
1095 if (iTodo + 8 < RT_ELEMENTS(pWatcher->aTodos))
1096 {
1097 pWatcher->aTodos[iTodo].hProcess = hProcess;
1098 pWatcher->aTodos[iTodo].Data.pUserData = pUserData;
1099 pWatcher->aTodos[iTodo].Data.iRevision = ++pUserData->m_iTheChosenOneRevision;
1100 pWatcher->aTodos[iTodo].Data.pid = pid;
1101 pWatcher->cTodos = iTodo + 1;
1102
1103 pUserData->m_iWatcher = pWatcher->iWatcher;
1104 pUserData->i_retain();
1105
1106 BOOL fRc = SetEvent(pWatcher->aHandles[0]);
1107 AssertLogRelMsg(fRc, ("SetEvent(%p) failed: %u\n", pWatcher->aHandles[0], GetLastError()));
1108 LogRel(("i_watchIt: Added %p/%p to watcher #%u: %RTbool\n", pUserData, hProcess, pWatcher->iWatcher, fRc));
1109
1110 i_incrementClientCount();
1111 RTCritSectLeave(&m_WatcherCritSect);
1112 RTThreadYield();
1113 return true;
1114 }
1115 }
1116 }
1117
1118 /*
1119 * No watcher with capacity was found, so create a new one with
1120 * the user/handle prequeued.
1121 */
1122 void *pvNew = RTMemRealloc(m_papWatchers, sizeof(m_papWatchers[0]) * (m_cWatchers + 1));
1123 if (pvNew)
1124 {
1125 m_papWatchers = (VBoxSDSWatcher **)pvNew;
1126 VBoxSDSWatcher *pWatcher = (VBoxSDSWatcher *)RTMemAllocZ(sizeof(*pWatcher));
1127 if (pWatcher)
1128 {
1129 for (uint32_t i = 0; i < RT_ELEMENTS(pWatcher->aData); i++)
1130 pWatcher->aData[i].setNull();
1131 for (uint32_t i = 0; i < RT_ELEMENTS(pWatcher->aTodos); i++)
1132 pWatcher->aTodos[i].Data.setNull();
1133
1134 pWatcher->pVBoxSDS = this;
1135 pWatcher->iWatcher = m_cWatchers;
1136 pWatcher->cRefs = 2;
1137 pWatcher->cHandlesEffective = 2;
1138 pWatcher->cHandles = 2;
1139 pWatcher->aHandles[0] = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL);
1140 if (pWatcher->aHandles[0])
1141 {
1142 /* Add incoming VBoxSVC process in slot #1: */
1143 pWatcher->aHandles[1] = hProcess;
1144 pWatcher->aData[1].pid = pid;
1145 pWatcher->aData[1].pUserData = pUserData;
1146 pWatcher->aData[1].iRevision = ++pUserData->m_iTheChosenOneRevision;
1147 pUserData->i_retain();
1148 pUserData->m_iWatcher = pWatcher->iWatcher;
1149
1150 /* Start the thread and we're good. */
1151 m_papWatchers[m_cWatchers++] = pWatcher;
1152 int rc = RTThreadCreateF(&pWatcher->hThread, i_watcherThreadProc, pWatcher, 0, RTTHREADTYPE_MAIN_WORKER,
1153 RTTHREADFLAGS_WAITABLE, "watcher%u", pWatcher->iWatcher);
1154 if (RT_SUCCESS(rc))
1155 {
1156 LogRel(("i_watchIt: Created new watcher #%u for %p/%p\n", m_cWatchers, pUserData, hProcess));
1157
1158 i_incrementClientCount();
1159 RTCritSectLeave(&m_WatcherCritSect);
1160 return true;
1161 }
1162
1163 LogRel(("i_watchIt: Error starting watcher thread: %Rrc\n", rc));
1164 m_papWatchers[--m_cWatchers] = NULL;
1165
1166 pUserData->m_iWatcher = UINT32_MAX;
1167 pUserData->i_release();
1168 CloseHandle(pWatcher->aHandles[0]);
1169 }
1170 else
1171 LogRel(("i_watchIt: CreateEventW failed: %u\n", GetLastError()));
1172 RTMemFree(pWatcher);
1173 }
1174 else
1175 LogRel(("i_watchIt: failed to allocate watcher structure!\n"));
1176 }
1177 else
1178 LogRel(("i_watchIt: Failed to grow watcher array to %u entries!\n", m_cWatchers + 1));
1179
1180 RTCritSectLeave(&m_WatcherCritSect);
1181 CloseHandle(hProcess);
1182 return false;
1183}
1184
1185
1186/**
1187 * Stops monitoring a VBoxSVC process.
1188 *
1189 * @param pUserData The user which chosen VBoxSVC should be watched.
1190 * @param pid The VBoxSVC PID.
1191 */
1192void VirtualBoxSDS::i_stopWatching(VBoxSDSPerUserData *pUserData, RTPROCESS pid)
1193{
1194 /*
1195 * Add a remove order in the watcher's todo queue.
1196 */
1197 RTCritSectEnter(&m_WatcherCritSect);
1198 for (uint32_t iRound = 0; ; iRound++)
1199 {
1200 uint32_t const iWatcher = pUserData->m_iWatcher;
1201 if (iWatcher < m_cWatchers)
1202 {
1203 VBoxSDSWatcher *pWatcher = m_papWatchers[pUserData->m_iWatcher];
1204 if (!pWatcher->fShutdown)
1205 {
1206 /*
1207 * Remove duplicate todo entries.
1208 */
1209 bool fAddIt = true;
1210 uint32_t iTodo = pWatcher->cTodos;
1211 while (iTodo-- > 0)
1212 if (pWatcher->aTodos[iTodo].Data.pUserData == pUserData)
1213 {
1214 if (pWatcher->aTodos[iTodo].hProcess == NULL)
1215 fAddIt = true;
1216 else
1217 {
1218 fAddIt = false;
1219 CloseHandle(pWatcher->aTodos[iTodo].hProcess);
1220 }
1221 uint32_t const cTodos = --pWatcher->cTodos;
1222 uint32_t const cToShift = cTodos - iTodo;
1223 if (cToShift > 0)
1224 memmove(&pWatcher->aTodos[iTodo], &pWatcher->aTodos[iTodo + 1], sizeof(pWatcher->aTodos[0]) * cToShift);
1225 pWatcher->aTodos[cTodos].hProcess = NULL;
1226 pWatcher->aTodos[cTodos].Data.setNull();
1227 }
1228
1229 /*
1230 * Did we just eliminated the add and cancel out this operation?
1231 */
1232 if (!fAddIt)
1233 {
1234 pUserData->m_iWatcher = UINT32_MAX;
1235 pUserData->m_iTheChosenOneRevision++;
1236 i_decrementClientCount();
1237
1238 RTCritSectLeave(&m_WatcherCritSect);
1239 RTThreadYield();
1240 return;
1241 }
1242
1243 /*
1244 * No we didn't. So, try append a removal item.
1245 */
1246 iTodo = pWatcher->cTodos;
1247 if (iTodo < RT_ELEMENTS(pWatcher->aTodos))
1248 {
1249 pWatcher->aTodos[iTodo].hProcess = NULL;
1250 pWatcher->aTodos[iTodo].Data.pUserData = pUserData;
1251 pWatcher->aTodos[iTodo].Data.pid = pid;
1252 pWatcher->aTodos[iTodo].Data.iRevision = pUserData->m_iTheChosenOneRevision++;
1253 pWatcher->cTodos = iTodo + 1;
1254 SetEvent(pWatcher->aHandles[0]);
1255
1256 pUserData->m_iWatcher = UINT32_MAX;
1257 i_decrementClientCount();
1258
1259 RTCritSectLeave(&m_WatcherCritSect);
1260 RTThreadYield();
1261 return;
1262 }
1263 }
1264 else
1265 {
1266 LogRel(("i_stopWatching: Watcher #%u has shut down.\n", iWatcher));
1267 break;
1268 }
1269
1270 /*
1271 * Todo queue is full. Sleep a little and let the watcher process it.
1272 */
1273 LogRel(("i_stopWatching: Watcher #%u todo queue is full! (round #%u)\n", iWatcher, iRound));
1274
1275 uint32_t const iTheChosenOneRevision = pUserData->m_iTheChosenOneRevision;
1276 SetEvent(pWatcher->aHandles[0]);
1277
1278 RTCritSectLeave(&m_WatcherCritSect);
1279 RTThreadSleep(1 + (iRound & 127));
1280 RTCritSectEnter(&m_WatcherCritSect);
1281
1282 AssertLogRelMsgBreak(pUserData->m_iTheChosenOneRevision == iTheChosenOneRevision,
1283 ("Impossible! m_iTheChosenOneRevision changed %#x -> %#x!\n",
1284 iTheChosenOneRevision, pUserData->m_iTheChosenOneRevision));
1285 }
1286 else
1287 {
1288 AssertLogRelMsg(pUserData->m_iWatcher == UINT32_MAX,
1289 ("Impossible! iWatcher=%d m_cWatcher=%u\n", iWatcher, m_cWatchers));
1290 break;
1291 }
1292 }
1293 RTCritSectLeave(&m_WatcherCritSect);
1294}
1295
1296
1297/**
1298 * Shutdowns all the watchers.
1299 */
1300void VirtualBoxSDS::i_shutdownAllWatchers(void)
1301{
1302 LogRel(("i_shutdownAllWatchers: %u watchers\n", m_cWatchers));
1303
1304 /* Notify them all. */
1305 uint32_t i = m_cWatchers;
1306 while (i-- > 0)
1307 {
1308 ASMAtomicWriteBool(&m_papWatchers[i]->fShutdown, true);
1309 SetEvent(m_papWatchers[i]->aHandles[0]);
1310 }
1311
1312 /* Wait for them to complete and destroy their data. */
1313 i = m_cWatchers;
1314 m_cWatchers = 0;
1315 while (i-- > 0)
1316 {
1317 VBoxSDSWatcher *pWatcher = m_papWatchers[i];
1318 if (pWatcher)
1319 {
1320 m_papWatchers[i] = NULL;
1321
1322 int rc = RTThreadWait(pWatcher->hThread, RT_MS_1MIN / 2, NULL);
1323 if (RT_SUCCESS(rc))
1324 pWatcher->hThread = NIL_RTTHREAD;
1325 else
1326 LogRel(("i_shutdownAllWatchers: RTThreadWait failed on #%u: %Rrc\n", i, rc));
1327
1328 if (ASMAtomicDecU32(&pWatcher->cRefs) == 0)
1329 RTMemFree(pWatcher);
1330 }
1331 }
1332}
1333
1334
1335/**
1336 * Increments the VBoxSVC client count.
1337 */
1338void VirtualBoxSDS::i_incrementClientCount()
1339{
1340 Assert(RTCritSectIsOwner(&m_WatcherCritSect));
1341 uint32_t cClients = ++m_cVBoxSvcProcesses;
1342 Assert(cClients < 4096);
1343 VBoxSDSNotifyClientCount(cClients);
1344}
1345
1346
1347/**
1348 * Decrements the VBoxSVC client count.
1349 */
1350void VirtualBoxSDS::i_decrementClientCount()
1351{
1352 Assert(RTCritSectIsOwner(&m_WatcherCritSect));
1353 uint32_t cClients = --m_cVBoxSvcProcesses;
1354 Assert(cClients < 4096);
1355 VBoxSDSNotifyClientCount(cClients);
1356}
1357
1358
1359#endif /* WITH_WATCHER */
1360
1361
1362/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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