1 | /* $Id: VirtualBoxClientListImpl.cpp 76066 2018-12-07 22:17:39Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBox Global COM Class implementation.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2017-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 "Logging.h"
|
---|
23 | #include "VirtualBoxClientListImpl.h"
|
---|
24 |
|
---|
25 | #include <iprt/process.h>
|
---|
26 | #include <iprt/asm.h>
|
---|
27 |
|
---|
28 |
|
---|
29 | /*********************************************************************************************************************************
|
---|
30 | * CClientListWatcher *
|
---|
31 | *********************************************************************************************************************************/
|
---|
32 |
|
---|
33 | /**
|
---|
34 | * Helper class that tracks existance of registered client processes.
|
---|
35 | *
|
---|
36 | * It removes the client process from client list if it shutdowned unexpectedly,
|
---|
37 | * without calling DeRegisterClient().
|
---|
38 | *
|
---|
39 | * It also notifies VBoxSVC and VBoxSDS that this client process finished.
|
---|
40 | */
|
---|
41 | class CClientListWatcher
|
---|
42 | {
|
---|
43 | public:
|
---|
44 | CClientListWatcher(TClientSet& list, RTCRITSECTRW& m_clientListCritSect);
|
---|
45 | virtual ~CClientListWatcher();
|
---|
46 |
|
---|
47 | protected:
|
---|
48 | static DECLCALLBACK(int) WatcherThreadProc(RTTHREAD hThreadSelf, void *pvUser);
|
---|
49 | void NotifySDSAllClientsFinished();
|
---|
50 |
|
---|
51 | volatile bool m_fWatcherRunning;
|
---|
52 | TClientSet &m_clientList;
|
---|
53 | RTCRITSECTRW &m_rClientListCritSect;
|
---|
54 | RTSEMEVENT m_hEvtWakeUpWatcher;
|
---|
55 |
|
---|
56 | /** There is a single watcher thread. */
|
---|
57 | static volatile RTTHREAD s_hThreadWatcher;
|
---|
58 | };
|
---|
59 |
|
---|
60 |
|
---|
61 | volatile RTTHREAD CClientListWatcher::s_hThreadWatcher = NIL_RTTHREAD;
|
---|
62 |
|
---|
63 |
|
---|
64 | CClientListWatcher::CClientListWatcher(TClientSet &list, RTCRITSECTRW &rClientListCritSect)
|
---|
65 | : m_clientList(list), m_rClientListCritSect(rClientListCritSect)
|
---|
66 | {
|
---|
67 | Assert(ASMAtomicReadPtr((void * volatile *)&CClientListWatcher::s_hThreadWatcher) == NULL);
|
---|
68 |
|
---|
69 | if (ASMAtomicReadPtr((void * volatile *)&CClientListWatcher::s_hThreadWatcher) != NULL)
|
---|
70 | {
|
---|
71 | LogRelFunc(("Error: Watcher thread already created!\n"));
|
---|
72 | return;
|
---|
73 | }
|
---|
74 |
|
---|
75 | int rc = RTSemEventCreate(&m_hEvtWakeUpWatcher);
|
---|
76 | if (RT_FAILURE(rc))
|
---|
77 | {
|
---|
78 | LogRelFunc(("Error: Failed to create wake up event for watcher thread: %Rrs\n", rc));
|
---|
79 | return;
|
---|
80 | }
|
---|
81 |
|
---|
82 | ASMAtomicWriteBool(&m_fWatcherRunning, true);
|
---|
83 | RTTHREAD hThreadWatcher;
|
---|
84 | rc = RTThreadCreate(&hThreadWatcher,
|
---|
85 | (PFNRTTHREAD)CClientListWatcher::WatcherThreadProc,
|
---|
86 | this, // pVUser
|
---|
87 | 0, // cbStack
|
---|
88 | RTTHREADTYPE_DEFAULT,
|
---|
89 | RTTHREADFLAGS_WAITABLE,
|
---|
90 | "CLWatcher");
|
---|
91 | Assert(RT_SUCCESS(rc));
|
---|
92 | if (RT_SUCCESS(rc))
|
---|
93 | {
|
---|
94 | ASMAtomicWriteHandle(&CClientListWatcher::s_hThreadWatcher, hThreadWatcher);
|
---|
95 | LogRelFunc(("Created client list watcher thread.\n"));
|
---|
96 | }
|
---|
97 | else
|
---|
98 | {
|
---|
99 | ASMAtomicWriteBool(&m_fWatcherRunning, false);
|
---|
100 | LogRelFunc(("Failed to create client list watcher thread: %Rrs\n", rc));
|
---|
101 | }
|
---|
102 | }
|
---|
103 |
|
---|
104 |
|
---|
105 | CClientListWatcher::~CClientListWatcher()
|
---|
106 | {
|
---|
107 | // mark watcher thread to finish
|
---|
108 | ASMAtomicWriteBool(&m_fWatcherRunning, false);
|
---|
109 |
|
---|
110 | // Wake up watcher thread to finish it faster
|
---|
111 | int rc = RTSemEventSignal(m_hEvtWakeUpWatcher);
|
---|
112 | AssertRC(rc);
|
---|
113 |
|
---|
114 | rc = RTThreadWait(CClientListWatcher::s_hThreadWatcher, RT_INDEFINITE_WAIT, NULL);
|
---|
115 | if (RT_FAILURE(rc))
|
---|
116 | LogRelFunc(("Error: watcher thread didn't finished. Possible thread leak. %Rrs\n", rc));
|
---|
117 | else
|
---|
118 | LogRelFunc(("Watcher thread finished.\n"));
|
---|
119 |
|
---|
120 | ASMAtomicWriteHandle(&CClientListWatcher::s_hThreadWatcher, NIL_RTTHREAD);
|
---|
121 |
|
---|
122 | RTSemEventDestroy(m_hEvtWakeUpWatcher);
|
---|
123 | }
|
---|
124 |
|
---|
125 |
|
---|
126 | /**
|
---|
127 | * Notifies the VBoxSDS that API client list is empty.
|
---|
128 | * Initiates the chain of closing VBoxSDS/VBoxSVC.
|
---|
129 | * VBoxSDS, in it's turn notifies VBoxSvc that relates on it,
|
---|
130 | * VBoxSvc instances releases itself and releases their references to VBoxSDS.
|
---|
131 | * in result VBoxSDS and VBoxSvc finishes itself after delay
|
---|
132 | */
|
---|
133 | void CClientListWatcher::NotifySDSAllClientsFinished()
|
---|
134 | {
|
---|
135 | ComPtr<IVirtualBoxSDS> ptrVirtualBoxSDS;
|
---|
136 |
|
---|
137 | /*
|
---|
138 | * Connect to VBoxSDS.
|
---|
139 | */
|
---|
140 | HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
|
---|
141 | (void **)ptrVirtualBoxSDS.asOutParam());
|
---|
142 | if (SUCCEEDED(hrc))
|
---|
143 | {
|
---|
144 | LogRelFunc(("Notifying SDS that all API clients finished...\n"));
|
---|
145 | ptrVirtualBoxSDS->NotifyClientsFinished();
|
---|
146 | }
|
---|
147 | }
|
---|
148 |
|
---|
149 |
|
---|
150 | /**
|
---|
151 | * Deregister all staled VBoxSVC through VBoxSDS and forcebly close VBoxSVC process
|
---|
152 | * @param hThreadSelf The thread handle.
|
---|
153 | * @param pvUser pointer to CClientListWatcher that created this thread.
|
---|
154 | */
|
---|
155 | DECLCALLBACK(int) CClientListWatcher::WatcherThreadProc(RTTHREAD hThreadSelf, void *pvUser)
|
---|
156 | {
|
---|
157 | LogRelFunc(("Enter watcher thread (%p)\n", hThreadSelf)); NOREF(hThreadSelf);
|
---|
158 | CClientListWatcher *pThis = (CClientListWatcher *)pvUser;
|
---|
159 | Assert(pThis);
|
---|
160 |
|
---|
161 | while (ASMAtomicReadBool(&pThis->m_fWatcherRunning))
|
---|
162 | {
|
---|
163 | /* remove finished API clients from list */
|
---|
164 | int rc = RTCritSectRwEnterShared(&pThis->m_rClientListCritSect);
|
---|
165 | Assert(RT_SUCCESS(rc));
|
---|
166 | NOREF(rc);
|
---|
167 |
|
---|
168 | TClientSet::iterator it = pThis->m_clientList.begin();
|
---|
169 | TClientSet::iterator end = pThis->m_clientList.end();
|
---|
170 | for (; it != end; ++it)
|
---|
171 | {
|
---|
172 | /** @todo r=bird: this is a bit inefficient because RTProcWait will try open
|
---|
173 | * all the process each time. Would be better have registerClient open the
|
---|
174 | * process and just to a WaitForSingleObject here (if you want to be really
|
---|
175 | * performant, you could keep wait arrays of 64 objects each and use
|
---|
176 | * WaitForMultipleObjects on each of them).
|
---|
177 | *
|
---|
178 | * And please, don't give me the portability argument here, because this
|
---|
179 | * RTProcWait call only works on windows. We're not the parent of any of these
|
---|
180 | * clients and can't wait on them.
|
---|
181 | */
|
---|
182 | // check status of client process by his pid
|
---|
183 | int rc = RTProcWait(*it, RTPROCWAIT_FLAGS_NOBLOCK, NULL);
|
---|
184 | if (rc == VERR_PROCESS_NOT_FOUND)
|
---|
185 | {
|
---|
186 | LogRelFunc(("Finished process detected: %d\n", *it));
|
---|
187 | /* delete finished process from client list */
|
---|
188 | it = pThis->m_clientList.erase(it);
|
---|
189 |
|
---|
190 | if (pThis->m_clientList.empty())
|
---|
191 | {
|
---|
192 | /*
|
---|
193 | Starts here chain of events between SDS and VBoxSVC
|
---|
194 | to shutdown them in case when all clients finished and
|
---|
195 | some of them crashed
|
---|
196 | */
|
---|
197 | pThis->NotifySDSAllClientsFinished();
|
---|
198 | }
|
---|
199 | }
|
---|
200 | }
|
---|
201 |
|
---|
202 | rc = RTCritSectRwLeaveShared(&pThis->m_rClientListCritSect);
|
---|
203 | Assert(RT_SUCCESS(rc));
|
---|
204 |
|
---|
205 | /*
|
---|
206 | * Wait for two second before next iteration.
|
---|
207 | * Destructor will wake up it immidietely.
|
---|
208 | */
|
---|
209 | /** @todo r=bird: This is where wait for multiple objects would be really nice, as
|
---|
210 | * you could wait on the first 63 client processes here in addition to the event.
|
---|
211 | * That would speed up the response time. */
|
---|
212 | RTSemEventWait(pThis->m_hEvtWakeUpWatcher, 2000);
|
---|
213 | }
|
---|
214 | LogRelFunc(("Finish watcher thread. Client list size: %d\n", pThis->m_clientList.size()));
|
---|
215 | return 0;
|
---|
216 | }
|
---|
217 |
|
---|
218 |
|
---|
219 |
|
---|
220 | /*********************************************************************************************************************************
|
---|
221 | * VirtualBoxClientList *
|
---|
222 | *********************************************************************************************************************************/
|
---|
223 |
|
---|
224 | ///////////////////////// VirtualBoxClientList implementation //////////////////////////
|
---|
225 |
|
---|
226 | HRESULT VirtualBoxClientList::FinalConstruct()
|
---|
227 | {
|
---|
228 | int rc = RTCritSectRwInit(&m_MapCritSect);
|
---|
229 | AssertLogRelRCReturn(rc, E_FAIL);
|
---|
230 |
|
---|
231 | try
|
---|
232 | {
|
---|
233 | m_pWatcher = new CClientListWatcher(m_ClientSet, m_MapCritSect);
|
---|
234 | }
|
---|
235 | catch (std::bad_alloc)
|
---|
236 | {
|
---|
237 | AssertLogRelFailedReturn(E_OUTOFMEMORY);
|
---|
238 | }
|
---|
239 | Assert(m_pWatcher);
|
---|
240 |
|
---|
241 | AutoInitSpan autoInitSpan(this);
|
---|
242 | autoInitSpan.setSucceeded();
|
---|
243 | BaseFinalConstruct();
|
---|
244 |
|
---|
245 | LogRelFunc(("VirtualBoxClientList initialized.\n"));
|
---|
246 | return S_OK;
|
---|
247 | }
|
---|
248 |
|
---|
249 | void VirtualBoxClientList::FinalRelease()
|
---|
250 | {
|
---|
251 | Assert(m_pWatcher);
|
---|
252 | if (m_pWatcher)
|
---|
253 | {
|
---|
254 | delete m_pWatcher;
|
---|
255 | m_pWatcher = NULL;
|
---|
256 | }
|
---|
257 |
|
---|
258 | int rc = RTCritSectRwDelete(&m_MapCritSect);
|
---|
259 | AssertRC(rc);
|
---|
260 |
|
---|
261 | BaseFinalRelease();
|
---|
262 | LogRelFunc(("VirtualBoxClientList released.\n"));
|
---|
263 | }
|
---|
264 |
|
---|
265 |
|
---|
266 | /**
|
---|
267 | * Deregister API client to add it to API client list
|
---|
268 | * API client process calls this function at start to include this process to client list
|
---|
269 | * @param aPid process ID of registering client process
|
---|
270 | */
|
---|
271 | HRESULT VirtualBoxClientList::registerClient(LONG aPid)
|
---|
272 | {
|
---|
273 | int rc = RTCritSectRwEnterExcl(&m_MapCritSect);
|
---|
274 | AssertRCReturn(rc, E_FAIL);
|
---|
275 | Assert(m_pWatcher);
|
---|
276 |
|
---|
277 | try
|
---|
278 | {
|
---|
279 | m_ClientSet.insert(aPid);
|
---|
280 | }
|
---|
281 | catch (std::bad_alloc)
|
---|
282 | {
|
---|
283 | RTCritSectRwLeaveExcl(&m_MapCritSect);
|
---|
284 | AssertLogRelFailedReturn(E_OUTOFMEMORY);
|
---|
285 | }
|
---|
286 |
|
---|
287 | rc = RTCritSectRwLeaveExcl(&m_MapCritSect);
|
---|
288 | AssertRC(rc);
|
---|
289 | LogRelFunc(("VirtualBoxClientList client registered. pid: %d\n", aPid, rc));
|
---|
290 | return S_OK;
|
---|
291 | }
|
---|
292 |
|
---|
293 |
|
---|
294 | /**
|
---|
295 | * Returns PIDs of the API client processes.
|
---|
296 | *
|
---|
297 | * @returns COM status code.
|
---|
298 | * @param aPids Reference to vector that is to receive the PID list.
|
---|
299 | */
|
---|
300 | HRESULT VirtualBoxClientList::getClients(std::vector<LONG> &aPids)
|
---|
301 | {
|
---|
302 | int rc = RTCritSectRwEnterShared(&m_MapCritSect);
|
---|
303 | AssertLogRelRCReturn(rc, E_FAIL);
|
---|
304 | if (!m_ClientSet.empty())
|
---|
305 | {
|
---|
306 | Assert(aPids.empty());
|
---|
307 | size_t const cClients = m_ClientSet.size();
|
---|
308 | try
|
---|
309 | {
|
---|
310 | aPids.reserve(cClients);
|
---|
311 | aPids.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(aPids.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 |
|
---|