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