VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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