VirtualBox

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

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

VBoxSDS: Implemented (disabled) monitoring client (VBoxSVC) process termination to bypass the 5 min denalty for abended clients. Really handy for development. bugref:3300

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