VirtualBox

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

Last change on this file since 78425 was 76592, checked in by vboxsync, 6 years ago

Main: Don't use Logging.h.

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