VirtualBox

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

Last change on this file was 103841, checked in by vboxsync, 2 months ago

VBoxSDS: Fixed log format string in VirtualBoxSDS::LaunchVMProcess().

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

© 2023 Oracle
ContactPrivacy policyTerms of Use