VirtualBox

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

Last change on this file since 94660 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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