1 | /* $Id: VirtualBoxClientListImpl.cpp 71714 2018-04-06 17:30:32Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBox Global COM Class implementation.
|
---|
4 | *
|
---|
5 | * @todo r=bird: Why is this file in src-all? Shouldn't it be in src-global/win/?!?
|
---|
6 | */
|
---|
7 |
|
---|
8 | /*
|
---|
9 | * Copyright (C) 2017-2018 Oracle Corporation
|
---|
10 | *
|
---|
11 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
12 | * available from http://www.virtualbox.org. This file is free software;
|
---|
13 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
14 | * General Public License (GPL) as published by the Free Software
|
---|
15 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
16 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
17 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
18 | */
|
---|
19 |
|
---|
20 |
|
---|
21 | #include "Logging.h"
|
---|
22 | #include "VirtualBoxClientListImpl.h"
|
---|
23 |
|
---|
24 | #include <iprt/process.h>
|
---|
25 | #include <iprt/asm.h>
|
---|
26 |
|
---|
27 |
|
---|
28 | ////////////////// CClientListWatcher implementation /////////////////
|
---|
29 |
|
---|
30 | /**
|
---|
31 | * Helper class that tracks existance of registered client processes.
|
---|
32 | *
|
---|
33 | * It removes the client process from client list if it shutdowned unexpectedly,
|
---|
34 | * without calling DeRegisterClient().
|
---|
35 | *
|
---|
36 | * It also notifies VBoxSVC and VBoxSDS that this client process finished.
|
---|
37 | */
|
---|
38 | class CClientListWatcher
|
---|
39 | {
|
---|
40 | public:
|
---|
41 | CClientListWatcher(TClientSet& list, RTCRITSECTRW& m_clientListCritSect);
|
---|
42 | virtual ~CClientListWatcher();
|
---|
43 |
|
---|
44 | protected:
|
---|
45 | static DECLCALLBACK(int) WatcherWorker(RTTHREAD ThreadSelf, void *pvUser);
|
---|
46 | void NotifySDSAllClientsFinished();
|
---|
47 | // m_WatcherThread is static to check that single watcher thread used only
|
---|
48 | static volatile RTTHREAD m_WatcherThread;
|
---|
49 | volatile bool m_fWatcherRunning;
|
---|
50 | TClientSet& m_clientList;
|
---|
51 | RTCRITSECTRW& m_clientListCritSect;
|
---|
52 | RTSEMEVENT m_wakeUpWatcherEvent;
|
---|
53 | };
|
---|
54 |
|
---|
55 | volatile RTTHREAD CClientListWatcher::m_WatcherThread = NULL; /** @todo r=bird: Wrong prefix for a static variable (s_)*/
|
---|
56 |
|
---|
57 | CClientListWatcher::CClientListWatcher(TClientSet& list, RTCRITSECTRW& clientListCritSect)
|
---|
58 | : m_clientList(list), m_clientListCritSect(clientListCritSect)
|
---|
59 | {
|
---|
60 | Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread) == NULL);
|
---|
61 |
|
---|
62 | if (ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread) != NULL)
|
---|
63 | {
|
---|
64 | LogRelFunc(("Error: Watcher thread already created!\n"));
|
---|
65 | return;
|
---|
66 | }
|
---|
67 |
|
---|
68 | int rc = RTSemEventCreate(&m_wakeUpWatcherEvent);
|
---|
69 | if (RT_FAILURE(rc))
|
---|
70 | {
|
---|
71 | LogRelFunc(("Error: Failed to create wake up event for watcher thread: %Rrs\n", rc));
|
---|
72 | return;
|
---|
73 | }
|
---|
74 |
|
---|
75 | RTTHREAD watcherThread;
|
---|
76 | rc = RTThreadCreate(&watcherThread,
|
---|
77 | (PFNRTTHREAD)CClientListWatcher::WatcherWorker,
|
---|
78 | this, // pVUser
|
---|
79 | 0, // cbStack
|
---|
80 | RTTHREADTYPE_DEFAULT,
|
---|
81 | RTTHREADFLAGS_WAITABLE,
|
---|
82 | "CLWatcher");
|
---|
83 | if (RT_SUCCESS(rc))
|
---|
84 | {
|
---|
85 | ASMAtomicWritePtr((void* volatile*)&CClientListWatcher::m_WatcherThread, watcherThread);
|
---|
86 | LogRelFunc(("Created client list watcher thread.\n"));
|
---|
87 | }
|
---|
88 | else
|
---|
89 | LogRelFunc(("Failed to create client list watcher thread: %Rrs\n", rc));
|
---|
90 | }
|
---|
91 |
|
---|
92 |
|
---|
93 | CClientListWatcher::~CClientListWatcher()
|
---|
94 | {
|
---|
95 | /** @todo r=bird: This assertion is patently wrong in the unlikely
|
---|
96 | * case that RTThreadCreate fails... This is why we either let
|
---|
97 | * constructor throw stuff or we have separate init() methods
|
---|
98 | * for doing things that may fail */
|
---|
99 | Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
|
---|
100 |
|
---|
101 | // mark watcher thread to finish
|
---|
102 | ASMAtomicWriteBool(&m_fWatcherRunning, false);
|
---|
103 |
|
---|
104 | // Wake up watcher thread to finish it faster
|
---|
105 | int rc = RTSemEventSignal(m_wakeUpWatcherEvent);
|
---|
106 | Assert(RT_SUCCESS(rc));
|
---|
107 |
|
---|
108 | rc = RTThreadWait(CClientListWatcher::m_WatcherThread, RT_INDEFINITE_WAIT, NULL);
|
---|
109 | if (RT_FAILURE(rc))
|
---|
110 | LogRelFunc(("Error: watcher thread didn't finished. Possible thread leak. %Rrs\n", rc));
|
---|
111 | else
|
---|
112 | LogRelFunc(("Watcher thread finished.\n"));
|
---|
113 |
|
---|
114 | ASMAtomicWriteNullPtr((void* volatile*)&CClientListWatcher::m_WatcherThread);
|
---|
115 |
|
---|
116 | RTSemEventDestroy(m_wakeUpWatcherEvent);
|
---|
117 | }
|
---|
118 |
|
---|
119 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
120 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
121 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
122 |
|
---|
123 | /*
|
---|
124 | * Notifies the VBoxSDS that API client list is empty.
|
---|
125 | * Initiates the chain of closing VBoxSDS/VBoxSVC.
|
---|
126 | * VBoxSDS, in it's turn notifies VBoxSvc that relates on it,
|
---|
127 | * VBoxSvc instances releases itself and releases their references to VBoxSDS.
|
---|
128 | * in result VBoxSDS and VBoxSvc finishes itself after delay
|
---|
129 | */
|
---|
130 | void CClientListWatcher::NotifySDSAllClientsFinished()
|
---|
131 | {
|
---|
132 | ComPtr<IVirtualBoxSDS> ptrVirtualBoxSDS;
|
---|
133 | /*
|
---|
134 | * Connect to VBoxSDS.
|
---|
135 | */
|
---|
136 | HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
|
---|
137 | (void **)ptrVirtualBoxSDS.asOutParam());
|
---|
138 | if (SUCCEEDED(hrc))
|
---|
139 | {
|
---|
140 | LogRelFunc(("Notifying SDS that all API clients finished...\n"));
|
---|
141 | ptrVirtualBoxSDS->NotifyClientsFinished();
|
---|
142 | }
|
---|
143 | }
|
---|
144 |
|
---|
145 |
|
---|
146 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
147 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
148 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
149 | /*
|
---|
150 | * Deregister all staled VBoxSVC through VBoxSDS and forcebly close VBoxSVC process
|
---|
151 | * Parameters:
|
---|
152 | * ThreadSelf - current thread id
|
---|
153 | * pvUser - pointer to CClientListWatcher that created this thread. */
|
---|
154 | DECLCALLBACK(int) CClientListWatcher::WatcherWorker(RTTHREAD ThreadSelf, void *pvUser)
|
---|
155 | {
|
---|
156 | NOREF(ThreadSelf);
|
---|
157 | /** @todo r=bird: This will fail once in a while because you don't know
|
---|
158 | * for sure how the scheduling is going to be. So, RTThreadCreate
|
---|
159 | * may return and set g_hWatcherThread after the thread started
|
---|
160 | * executing and got here! */
|
---|
161 | Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
|
---|
162 | LogRelFunc(("Enter watcher thread\n"));
|
---|
163 |
|
---|
164 | CClientListWatcher *pThis = (CClientListWatcher *)pvUser;
|
---|
165 | Assert(pThis);
|
---|
166 |
|
---|
167 | ASMAtomicWriteBool(&pThis->m_fWatcherRunning, true);
|
---|
168 |
|
---|
169 | while (ASMAtomicReadBool(&pThis->m_fWatcherRunning))
|
---|
170 | {
|
---|
171 | /* remove finished API clients from list */
|
---|
172 | int rc = RTCritSectRwEnterShared(&pThis->m_clientListCritSect);
|
---|
173 | Assert(RT_SUCCESS(rc));
|
---|
174 | NOREF(rc);
|
---|
175 |
|
---|
176 | TClientSet::iterator it = pThis->m_clientList.begin();
|
---|
177 | TClientSet::iterator end = pThis->m_clientList.end();
|
---|
178 | for (; it != end; ++it)
|
---|
179 | {
|
---|
180 | /** @todo r=bird: this is a bit inefficient because RTProcWait will try open
|
---|
181 | * all the process each time. Would be better have registerClient open the
|
---|
182 | * process and just to a WaitForSingleObject here (if you want to be really
|
---|
183 | * performant, you could keep wait arrays of 64 objects each and use
|
---|
184 | * WaitForMultipleObjects on each of them).
|
---|
185 | *
|
---|
186 | * And please, don't give me the portability argument here, because this
|
---|
187 | * RTProcWait call only works on windows. We're not the parent of any of these
|
---|
188 | * clients and can't wait on them.
|
---|
189 | */
|
---|
190 | // check status of client process by his pid
|
---|
191 | int rc = RTProcWait(*it, RTPROCWAIT_FLAGS_NOBLOCK, NULL);
|
---|
192 | if (rc == VERR_PROCESS_NOT_FOUND)
|
---|
193 | {
|
---|
194 | LogRelFunc(("Finished process detected: %d\n", *it));
|
---|
195 | /* delete finished process from client list */
|
---|
196 | it = pThis->m_clientList.erase(it);
|
---|
197 |
|
---|
198 | if (pThis->m_clientList.empty())
|
---|
199 | {
|
---|
200 | /*
|
---|
201 | Starts here chain of events between SDS and VBoxSVC
|
---|
202 | to shutdown them in case when all clients finished and
|
---|
203 | some of them crashed
|
---|
204 | */
|
---|
205 | pThis->NotifySDSAllClientsFinished();
|
---|
206 | }
|
---|
207 | }
|
---|
208 | }
|
---|
209 |
|
---|
210 | rc = RTCritSectRwLeaveShared(&pThis->m_clientListCritSect);
|
---|
211 | Assert(RT_SUCCESS(rc));
|
---|
212 |
|
---|
213 | /*
|
---|
214 | * Wait for two second before next iteration.
|
---|
215 | * Destructor will wake up it immidietely.
|
---|
216 | */
|
---|
217 | /** @todo r=bird: This is where wait for multiple objects would be really nice, as
|
---|
218 | * you could wait on the first 63 client processes here in addition to the event.
|
---|
219 | * That would speed up the response time. */
|
---|
220 | RTSemEventWait(pThis->m_wakeUpWatcherEvent, 2000);
|
---|
221 | }
|
---|
222 | LogRelFunc(("Finish watcher thread. Client list size: %d\n", pThis->m_clientList.size()));
|
---|
223 | return 0;
|
---|
224 | }
|
---|
225 |
|
---|
226 | ///////////////////////// VirtualBoxClientList implementation //////////////////////////
|
---|
227 |
|
---|
228 | HRESULT VirtualBoxClientList::FinalConstruct()
|
---|
229 | {
|
---|
230 | int rc = RTCritSectRwInit(&m_MapCritSect);
|
---|
231 | AssertLogRelRCReturn(rc, E_FAIL);
|
---|
232 |
|
---|
233 | try
|
---|
234 | {
|
---|
235 | m_pWatcher = new CClientListWatcher(m_ClientSet, m_MapCritSect);
|
---|
236 | }
|
---|
237 | catch (std::bad_alloc)
|
---|
238 | {
|
---|
239 | AssertLogRelFailedReturn(E_OUTOFMEMORY);
|
---|
240 | }
|
---|
241 | Assert(m_pWatcher);
|
---|
242 |
|
---|
243 | AutoInitSpan autoInitSpan(this);
|
---|
244 | autoInitSpan.setSucceeded();
|
---|
245 | BaseFinalConstruct();
|
---|
246 |
|
---|
247 | LogRelFunc(("VirtualBoxClientList initialized.\n"));
|
---|
248 | return S_OK;
|
---|
249 | }
|
---|
250 |
|
---|
251 | void VirtualBoxClientList::FinalRelease()
|
---|
252 | {
|
---|
253 | Assert(m_pWatcher);
|
---|
254 | if (m_pWatcher)
|
---|
255 | {
|
---|
256 | delete m_pWatcher;
|
---|
257 | m_pWatcher = NULL;
|
---|
258 | }
|
---|
259 |
|
---|
260 | int rc = RTCritSectRwDelete(&m_MapCritSect);
|
---|
261 | AssertRC(rc);
|
---|
262 |
|
---|
263 | BaseFinalRelease();
|
---|
264 | LogRelFunc(("VirtualBoxClientList released.\n"));
|
---|
265 | }
|
---|
266 |
|
---|
267 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
268 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
269 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
270 | /*
|
---|
271 | * Deregister API client to add it to API client list
|
---|
272 | * API client process calls this function at start to include this process to client list
|
---|
273 | *
|
---|
274 | */
|
---|
275 | HRESULT VirtualBoxClientList::registerClient(LONG aPid)
|
---|
276 | {
|
---|
277 | int rc = RTCritSectRwEnterExcl(&m_MapCritSect);
|
---|
278 | AssertRCReturn(rc, E_FAIL);
|
---|
279 | Assert(m_pWatcher);
|
---|
280 |
|
---|
281 | try
|
---|
282 | {
|
---|
283 | m_ClientSet.insert(aPid);
|
---|
284 | }
|
---|
285 | catch (std::bad_alloc)
|
---|
286 | {
|
---|
287 | RTCritSectRwLeaveExcl(&m_MapCritSect);
|
---|
288 | AssertLogRelFailedReturn(E_OUTOFMEMORY);
|
---|
289 | }
|
---|
290 |
|
---|
291 | rc = RTCritSectRwLeaveExcl(&m_MapCritSect);
|
---|
292 | AssertRC(rc);
|
---|
293 | LogRelFunc(("VirtualBoxClientList client registered. pid: %d\n", aPid, rc));
|
---|
294 | return S_OK;
|
---|
295 | }
|
---|
296 |
|
---|
297 |
|
---|
298 | /**
|
---|
299 | * Returns PIDs of the API client processes.
|
---|
300 | *
|
---|
301 | * @returns COM status code.
|
---|
302 | * @param aEnvironment Reference to vector that is to receive the PID list.
|
---|
303 | *
|
---|
304 | * @todo r=bird: There is no obvious reason why this is called 'aEnvironment', a
|
---|
305 | * more natural name would be 'aPids'.
|
---|
306 | */
|
---|
307 | HRESULT VirtualBoxClientList::getClients(std::vector<LONG> &aEnvironment)
|
---|
308 | {
|
---|
309 | int rc = RTCritSectRwEnterShared(&m_MapCritSect);
|
---|
310 | AssertLogRelRCReturn(rc, E_FAIL);
|
---|
311 | if (!m_ClientSet.empty())
|
---|
312 | {
|
---|
313 | Assert(aEnvironment.empty());
|
---|
314 | size_t const cClients = m_ClientSet.size();
|
---|
315 | try
|
---|
316 | {
|
---|
317 | aEnvironment.reserve(cClients);
|
---|
318 | aEnvironment.assign(m_ClientSet.begin(), m_ClientSet.end());
|
---|
319 | }
|
---|
320 | catch (std::bad_alloc)
|
---|
321 | {
|
---|
322 | RTCritSectRwLeaveShared(&m_MapCritSect);
|
---|
323 | AssertLogRelMsgFailedReturn(("cClients=%zu\n", cClients), E_OUTOFMEMORY);
|
---|
324 | }
|
---|
325 | Assert(aEnvironment.size() == cClients);
|
---|
326 | }
|
---|
327 | else
|
---|
328 | {
|
---|
329 | LogFunc(("Client list is empty\n"));
|
---|
330 | }
|
---|
331 |
|
---|
332 | rc = RTCritSectRwLeaveShared(&m_MapCritSect);
|
---|
333 | AssertRC(rc);
|
---|
334 |
|
---|
335 | LogRelFunc(("VirtualBoxClientList client list requested.\n"));
|
---|
336 | return S_OK;
|
---|
337 | }
|
---|
338 |
|
---|