VirtualBox

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

Last change on this file since 108149 was 107536, checked in by vboxsync, 4 months ago

src/VBox/Main/src-global/win/VirtualBoxSDSImpl.cpp: Properly guard some attributes with WITH_WATCHER. We probably should ditch WITH_WATCHER altogether sometime. jiraref:VBP-1424

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette