1 | /* $Id: VirtualBoxClientListImpl.cpp 71183 2018-03-03 00:53:44Z 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 | /** @todo r=bird: Hanging indent on '(', please. */
|
---|
76 | RTTHREAD watcherThread;
|
---|
77 | rc = RTThreadCreate(&watcherThread,
|
---|
78 | (PFNRTTHREAD)CClientListWatcher::WatcherWorker,
|
---|
79 | this, // pVUser
|
---|
80 | 0, // cbStack
|
---|
81 | RTTHREADTYPE_DEFAULT,
|
---|
82 | RTTHREADFLAGS_WAITABLE,
|
---|
83 | "CLWatcher");
|
---|
84 | if (RT_SUCCESS(rc))
|
---|
85 | {
|
---|
86 | ASMAtomicWritePtr((void* volatile*)&CClientListWatcher::m_WatcherThread, watcherThread);
|
---|
87 | LogRelFunc(("Created client list watcher thread.\n"));
|
---|
88 | }
|
---|
89 | else
|
---|
90 | LogRelFunc(("Failed to create client list watcher thread: %Rrs\n", rc));
|
---|
91 | }
|
---|
92 |
|
---|
93 |
|
---|
94 | CClientListWatcher::~CClientListWatcher()
|
---|
95 | {
|
---|
96 | Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
|
---|
97 |
|
---|
98 | // mark watcher thread to finish
|
---|
99 | ASMAtomicWriteBool(&m_fWatcherRunning, false);
|
---|
100 |
|
---|
101 | // Wake up watcher thread to finish it faster
|
---|
102 | int rc = RTSemEventSignal(m_wakeUpWatcherEvent);
|
---|
103 | Assert(RT_SUCCESS(rc));
|
---|
104 |
|
---|
105 | rc = RTThreadWait(CClientListWatcher::m_WatcherThread, RT_INDEFINITE_WAIT, NULL);
|
---|
106 | if (RT_FAILURE(rc))
|
---|
107 | LogRelFunc(("Error: watcher thread didn't finished. Possible thread leak. %Rrs\n", rc));
|
---|
108 | else
|
---|
109 | LogRelFunc(("Watcher thread finished.\n"));
|
---|
110 |
|
---|
111 | ASMAtomicWriteNullPtr((void* volatile*)&CClientListWatcher::m_WatcherThread);
|
---|
112 |
|
---|
113 | RTSemEventDestroy(m_wakeUpWatcherEvent);
|
---|
114 | }
|
---|
115 |
|
---|
116 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
117 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
118 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
119 |
|
---|
120 | /*
|
---|
121 | * Notifies the VBoxSDS that API client list is empty.
|
---|
122 | * Initiates the chain of closing VBoxSDS/VBoxSVC.
|
---|
123 | * VBoxSDS, in it's turn notifies VBoxSvc that relates on it,
|
---|
124 | * VBoxSvc instances releases itself and releases their references to VBoxSDS.
|
---|
125 | * in result VBoxSDS and VBoxSvc finishes itself after delay
|
---|
126 | */
|
---|
127 | void CClientListWatcher::NotifySDSAllClientsFinished()
|
---|
128 | {
|
---|
129 | ComPtr<IVirtualBoxSDS> ptrVirtualBoxSDS;
|
---|
130 | /*
|
---|
131 | * Connect to VBoxSDS.
|
---|
132 | */
|
---|
133 | HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
|
---|
134 | (void **)ptrVirtualBoxSDS.asOutParam());
|
---|
135 | if (SUCCEEDED(hrc))
|
---|
136 | {
|
---|
137 | LogRelFunc(("Notifying SDS that all API clients finished...\n"));
|
---|
138 | ptrVirtualBoxSDS->NotifyClientsFinished();
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 |
|
---|
143 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
144 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
145 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
146 | /*
|
---|
147 | * Deregister all staled VBoxSVC through VBoxSDS and forcebly close VBoxSVC process
|
---|
148 | * Parameters:
|
---|
149 | * ThreadSelf - current thread id
|
---|
150 | * pvUser - pointer to CClientListWatcher that created this thread. */
|
---|
151 | DECLCALLBACK(int) CClientListWatcher::WatcherWorker(RTTHREAD ThreadSelf, void *pvUser)
|
---|
152 | {
|
---|
153 | NOREF(ThreadSelf);
|
---|
154 | Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
|
---|
155 | LogRelFunc(("Enter watcher thread\n"));
|
---|
156 |
|
---|
157 | CClientListWatcher *pThis = (CClientListWatcher *)pvUser;
|
---|
158 | Assert(pThis);
|
---|
159 |
|
---|
160 | ASMAtomicWriteBool(&pThis->m_fWatcherRunning, true);
|
---|
161 |
|
---|
162 | while (ASMAtomicReadBool(&pThis->m_fWatcherRunning))
|
---|
163 | {
|
---|
164 | /* remove finished API clients from list */
|
---|
165 | int rc = RTCritSectRwEnterShared(&pThis->m_clientListCritSect);
|
---|
166 | Assert(RT_SUCCESS(rc));
|
---|
167 | NOREF(rc);
|
---|
168 |
|
---|
169 | TClientSet::iterator it = pThis->m_clientList.begin();
|
---|
170 | TClientSet::iterator end = pThis->m_clientList.end();
|
---|
171 | for (; it != end; ++it)
|
---|
172 | {
|
---|
173 | /** @todo r=bird: this is a bit inefficient because RTProcWait will try open
|
---|
174 | * all the process each time. Would be better have registerClient open the
|
---|
175 | * process and just to a WaitForSingleObject here (if you want to be really
|
---|
176 | * performant, you could keep wait arrays of 64 objects each and use
|
---|
177 | * WaitForMultipleObjects on each of them).
|
---|
178 | *
|
---|
179 | * And please, don't give me the portability argument here, because this
|
---|
180 | * RTProcWait call only works on windows. We're not the parent of any of these
|
---|
181 | * clients and can't wait on them.
|
---|
182 | */
|
---|
183 | // check status of client process by his pid
|
---|
184 | int rc = RTProcWait(*it, RTPROCWAIT_FLAGS_NOBLOCK, NULL);
|
---|
185 | if (rc == VERR_PROCESS_NOT_FOUND)
|
---|
186 | {
|
---|
187 | LogRelFunc(("Finished process detected: %d\n", *it));
|
---|
188 | /* delete finished process from client list */
|
---|
189 | it = pThis->m_clientList.erase(it);
|
---|
190 |
|
---|
191 | if (pThis->m_clientList.empty())
|
---|
192 | {
|
---|
193 | /*
|
---|
194 | Starts here chain of events between SDS and VBoxSVC
|
---|
195 | to shutdown them in case when all clients finished and
|
---|
196 | some of them crashed
|
---|
197 | */
|
---|
198 | pThis->NotifySDSAllClientsFinished();
|
---|
199 | }
|
---|
200 | }
|
---|
201 | }
|
---|
202 |
|
---|
203 | rc = RTCritSectRwLeaveShared(&pThis->m_clientListCritSect);
|
---|
204 | Assert(RT_SUCCESS(rc));
|
---|
205 |
|
---|
206 | /*
|
---|
207 | * Wait for two second before next iteration.
|
---|
208 | * Destructor will wake up it immidietely.
|
---|
209 | */
|
---|
210 | /** @todo r=bird: This is where wait for multiple objects would be really nice, as
|
---|
211 | * you could wait on the first 63 client processes here in addition to the event.
|
---|
212 | * That would speed up the response time. */
|
---|
213 | RTSemEventWait(pThis->m_wakeUpWatcherEvent, 2000);
|
---|
214 | }
|
---|
215 | LogRelFunc(("Finish watcher thread. Client list size: %d\n", pThis->m_clientList.size()));
|
---|
216 | return 0;
|
---|
217 | }
|
---|
218 |
|
---|
219 | ///////////////////////// VirtualBoxClientList implementation //////////////////////////
|
---|
220 |
|
---|
221 | HRESULT VirtualBoxClientList::FinalConstruct()
|
---|
222 | {
|
---|
223 | int rc = RTCritSectRwInit(&m_MapCritSect);
|
---|
224 | AssertLogRelRCReturn(rc, E_FAIL);
|
---|
225 |
|
---|
226 | try
|
---|
227 | {
|
---|
228 | m_pWatcher = new CClientListWatcher(m_ClientSet, m_MapCritSect);
|
---|
229 | }
|
---|
230 | catch (std::bad_alloc)
|
---|
231 | {
|
---|
232 | AssertLogRelFailedReturn(E_OUTOFMEMORY);
|
---|
233 | }
|
---|
234 | Assert(m_pWatcher);
|
---|
235 |
|
---|
236 | AutoInitSpan autoInitSpan(this);
|
---|
237 | autoInitSpan.setSucceeded();
|
---|
238 | BaseFinalConstruct();
|
---|
239 |
|
---|
240 | LogRelFunc(("VirtualBoxClientList initialized.\n"));
|
---|
241 | return S_OK;
|
---|
242 | }
|
---|
243 |
|
---|
244 | void VirtualBoxClientList::FinalRelease()
|
---|
245 | {
|
---|
246 | Assert(m_pWatcher);
|
---|
247 | if (m_pWatcher)
|
---|
248 | {
|
---|
249 | delete m_pWatcher;
|
---|
250 | m_pWatcher = NULL;
|
---|
251 | }
|
---|
252 |
|
---|
253 | int rc = RTCritSectRwDelete(&m_MapCritSect);
|
---|
254 | AssertRC(rc);
|
---|
255 |
|
---|
256 | BaseFinalRelease();
|
---|
257 | LogRelFunc(("VirtualBoxClientList released.\n"));
|
---|
258 | }
|
---|
259 |
|
---|
260 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
261 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
262 | /** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
|
---|
263 | /*
|
---|
264 | * Deregister API client to add it to API client list
|
---|
265 | * API client process calls this function at start to include this process to client list
|
---|
266 | *
|
---|
267 | */
|
---|
268 | HRESULT VirtualBoxClientList::registerClient(LONG aPid)
|
---|
269 | {
|
---|
270 | int rc = RTCritSectRwEnterExcl(&m_MapCritSect);
|
---|
271 | AssertRCReturn(rc, E_FAIL);
|
---|
272 | Assert(m_pWatcher);
|
---|
273 |
|
---|
274 | try
|
---|
275 | {
|
---|
276 | m_ClientSet.insert(aPid);
|
---|
277 | }
|
---|
278 | catch (std::bad_alloc)
|
---|
279 | {
|
---|
280 | RTCritSectRwLeaveExcl(&m_MapCritSect);
|
---|
281 | AssertLogRelFailedReturn(E_OUTOFMEMORY);
|
---|
282 | }
|
---|
283 |
|
---|
284 | rc = RTCritSectRwLeaveExcl(&m_MapCritSect);
|
---|
285 | AssertRC(rc);
|
---|
286 | LogRelFunc(("VirtualBoxClientList client registered. pid: %d\n", aPid, rc));
|
---|
287 | return S_OK;
|
---|
288 | }
|
---|
289 |
|
---|
290 |
|
---|
291 | /**
|
---|
292 | * Returns PIDs of the API client processes.
|
---|
293 | *
|
---|
294 | * @returns COM status code.
|
---|
295 | * @param aEnvironment Reference to vector that is to receive the PID list.
|
---|
296 | *
|
---|
297 | * @todo r=bird: There is no obvious reason why this is called 'aEnvironment', a
|
---|
298 | * more natural name would be 'aPids'.
|
---|
299 | */
|
---|
300 | HRESULT VirtualBoxClientList::getClients(std::vector<LONG> &aEnvironment)
|
---|
301 | {
|
---|
302 | int rc = RTCritSectRwEnterShared(&m_MapCritSect);
|
---|
303 | AssertLogRelRCReturn(rc, E_FAIL);
|
---|
304 | if (!m_ClientSet.empty())
|
---|
305 | {
|
---|
306 | Assert(aEnvironment.empty());
|
---|
307 | size_t const cClients = m_ClientSet.size();
|
---|
308 | try
|
---|
309 | {
|
---|
310 | aEnvironment.reserve(cClients);
|
---|
311 | aEnvironment.assign(m_ClientSet.begin(), m_ClientSet.end());
|
---|
312 | }
|
---|
313 | catch (std::bad_alloc)
|
---|
314 | {
|
---|
315 | RTCritSectRwLeaveShared(&m_MapCritSect);
|
---|
316 | AssertLogRelMsgFailedReturn(("cClients=%zu\n", cClients), E_OUTOFMEMORY);
|
---|
317 | }
|
---|
318 | Assert(aEnvironment.size() == cClients);
|
---|
319 | }
|
---|
320 | else
|
---|
321 | {
|
---|
322 | LogFunc(("Client list is empty\n"));
|
---|
323 | }
|
---|
324 |
|
---|
325 | rc = RTCritSectRwLeaveShared(&m_MapCritSect);
|
---|
326 | AssertRC(rc);
|
---|
327 |
|
---|
328 | LogRelFunc(("VirtualBoxClientList client list requested.\n"));
|
---|
329 | return S_OK;
|
---|
330 | }
|
---|
331 |
|
---|