VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp@ 44591

Last change on this file since 44591 was 43170, checked in by vboxsync, 12 years ago

GuestCtrl: Handle bad_alloc, warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 43170 2012-09-04 16:37:40Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2012 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#include "GuestImpl.h"
19#include "GuestSessionImpl.h"
20#include "GuestCtrlImplPrivate.h"
21
22#include "Global.h"
23#include "ConsoleImpl.h"
24#include "ProgressImpl.h"
25#include "VMMDev.h"
26
27#include "AutoCaller.h"
28
29#include <VBox/VMMDev.h>
30#ifdef VBOX_WITH_GUEST_CONTROL
31# include <VBox/com/array.h>
32# include <VBox/com/ErrorInfo.h>
33#endif
34#include <iprt/cpp/utils.h>
35#include <iprt/file.h>
36#include <iprt/getopt.h>
37#include <iprt/isofs.h>
38#include <iprt/list.h>
39#include <iprt/path.h>
40#include <VBox/vmm/pgm.h>
41
42#include <memory>
43
44#ifdef LOG_GROUP
45 #undef LOG_GROUP
46#endif
47#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
48#include <VBox/log.h>
49
50
51// public methods only for internal purposes
52/////////////////////////////////////////////////////////////////////////////
53
54#ifdef VBOX_WITH_GUEST_CONTROL
55/**
56 * Static callback function for receiving updates on guest control commands
57 * from the guest. Acts as a dispatcher for the actual class instance.
58 *
59 * @returns VBox status code.
60 *
61 * @todo
62 *
63 */
64/* static */
65DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
66 uint32_t u32Function,
67 void *pvParms,
68 uint32_t cbParms)
69{
70 using namespace guestControl;
71
72 /*
73 * No locking, as this is purely a notification which does not make any
74 * changes to the object state.
75 */
76 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
77 pvExtension, u32Function, pvParms, cbParms));
78 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
79 Assert(!pGuest.isNull());
80
81 /*
82 * For guest control 2.0 using the legacy commands we need to do the following here:
83 * - Get the callback header to access the context ID
84 * - Get the context ID of the callback
85 * - Extract the session ID out of the context ID
86 * - Dispatch the whole stuff to the appropriate session (if still exists)
87 */
88
89 PCALLBACKHEADER pHeader = (PCALLBACKHEADER)pvParms;
90 AssertPtr(pHeader);
91
92#ifdef DEBUG
93 LogFlowFunc(("CID=%RU32, uSession=%RU32, uObject=%RU32, uCount=%RU32\n",
94 pHeader->u32ContextID,
95 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHeader->u32ContextID),
96 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHeader->u32ContextID),
97 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pHeader->u32ContextID)));
98#endif
99
100 bool fDispatch = true;
101#ifdef DEBUG
102 /*
103 * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
104 * it means that that guest could not handle the entire message
105 * because of its exceeding size. This should not happen on daily
106 * use but testcases might try this. It then makes no sense to dispatch
107 * this further because we don't have a valid context ID.
108 */
109 if (u32Function == GUEST_EXEC_SEND_STATUS)
110 {
111 PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
112 AssertPtr(pCallbackData);
113 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
114 AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
115
116 if ( pCallbackData->u32Status == PROC_STS_ERROR
117 && ((int)pCallbackData->u32Flags) == VERR_TOO_MUCH_DATA)
118 {
119 LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
120
121 Assert(pCallbackData->u32PID == 0);
122 fDispatch = false;
123 }
124 }
125#endif
126 int rc = VINF_SUCCESS;
127 if (fDispatch)
128 {
129 rc = pGuest->dispatchToSession(pHeader->u32ContextID, u32Function, pvParms, cbParms);
130 if (RT_SUCCESS(rc))
131 return rc;
132 }
133
134 LogFlowFuncLeaveRC(rc);
135 return rc;
136}
137#endif /* VBOX_WITH_GUEST_CONTROL */
138
139STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress)
140{
141#ifndef VBOX_WITH_GUEST_CONTROL
142 ReturnComNotImplemented();
143#else /* VBOX_WITH_GUEST_CONTROL */
144 CheckComArgStrNotEmptyOrNull(aSource);
145 CheckComArgOutPointerValid(aProgress);
146
147 AutoCaller autoCaller(this);
148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
149
150 /* Validate flags. */
151 uint32_t fFlags = AdditionsUpdateFlag_None;
152 if (aFlags)
153 {
154 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
155 for (size_t i = 0; i < flags.size(); i++)
156 fFlags |= flags[i];
157 }
158
159 if (fFlags)
160 {
161 if (!(fFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
162 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
163 }
164
165 HRESULT hr = S_OK;
166
167 /* Create an anonymous session. This is required to run the Guest Additions
168 * update process with administrative rights. */
169 ComObjPtr<GuestSession> pSession;
170 int rc = sessionCreate("" /* User */, "" /* Password */, "" /* Domain */,
171 "Updating Guest Additions" /* Name */, pSession);
172 if (RT_FAILURE(rc))
173 {
174 switch (rc)
175 {
176 case VERR_MAX_PROCS_REACHED:
177 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
178 VBOX_GUESTCTRL_MAX_SESSIONS);
179 break;
180
181 /** @todo Add more errors here. */
182
183 default:
184 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
185 break;
186 }
187 }
188 else
189 {
190 Assert(!pSession.isNull());
191 rc = pSession->queryInfo();
192 if (RT_FAILURE(rc))
193 {
194 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not query guest session information: %Rrc"), rc);
195 }
196 else
197 {
198 try
199 {
200 ComObjPtr<Progress> pProgress;
201 SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
202 Utf8Str(aSource), fFlags);
203 rc = pSession->startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
204 if (RT_SUCCESS(rc))
205 {
206 /* Return progress to the caller. */
207 hr = pProgress.queryInterfaceTo(aProgress);
208 }
209 else
210 hr = setError(VBOX_E_IPRT_ERROR,
211 tr("Starting task for updating Guest Additions on the guest failed: %Rrc"), rc);
212 }
213 catch(std::bad_alloc &)
214 {
215 hr = E_OUTOFMEMORY;
216 }
217 }
218 }
219 return hr;
220#endif /* VBOX_WITH_GUEST_CONTROL */
221}
222
223// private methods
224/////////////////////////////////////////////////////////////////////////////
225
226int Guest::dispatchToSession(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
227{
228 LogFlowFuncEnter();
229
230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
231
232 uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID);
233#ifdef DEBUG
234 LogFlowFunc(("uSessionID=%RU32 (%RU32 total)\n",
235 uSessionID, mData.mGuestSessions.size()));
236#endif
237 int rc;
238 GuestSessions::const_iterator itSession
239 = mData.mGuestSessions.find(uSessionID);
240 if (itSession != mData.mGuestSessions.end())
241 {
242 ComObjPtr<GuestSession> pSession(itSession->second);
243 Assert(!pSession.isNull());
244
245 alock.release();
246 rc = pSession->dispatchToProcess(uContextID, uFunction, pvData, cbData);
247 }
248 else
249 rc = VERR_NOT_FOUND;
250
251 LogFlowFuncLeaveRC(rc);
252 return rc;
253}
254
255int Guest::sessionRemove(GuestSession *pSession)
256{
257 LogFlowThisFuncEnter();
258
259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
260
261 int rc = VERR_NOT_FOUND;
262
263 LogFlowFunc(("Closing session (ID=%RU32) ...\n", pSession->getId()));
264
265 for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
266 itSessions != mData.mGuestSessions.end(); ++itSessions)
267 {
268 if (pSession == itSessions->second)
269 {
270 LogFlowFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
271 (GuestSession *)itSessions->second, itSessions->second->getId(), mData.mGuestSessions.size() - 1));
272
273 mData.mGuestSessions.erase(itSessions);
274
275 rc = VINF_SUCCESS;
276 break;
277 }
278 }
279
280 LogFlowFuncLeaveRC(rc);
281 return rc;
282}
283
284int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, const Utf8Str &strDomain,
285 const Utf8Str &strSessionName, ComObjPtr<GuestSession> &pGuestSession)
286{
287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
288
289 int rc = VERR_MAX_PROCS_REACHED;
290 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
291 return rc;
292
293 try
294 {
295 /* Create a new session ID and assign it. */
296 uint32_t uNewSessionID = 0;
297 uint32_t uTries = 0;
298
299 for (;;)
300 {
301 /* Is the context ID already used? */
302 if (!sessionExists(uNewSessionID))
303 {
304 rc = VINF_SUCCESS;
305 break;
306 }
307 uNewSessionID++;
308 if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
309 uNewSessionID = 0;
310
311 if (++uTries == UINT32_MAX)
312 break; /* Don't try too hard. */
313 }
314 if (RT_FAILURE(rc)) throw rc;
315
316 /* Create the session object. */
317 HRESULT hr = pGuestSession.createObject();
318 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
319
320 rc = pGuestSession->init(this, uNewSessionID,
321 strUser, strPassword, strDomain, strSessionName);
322 if (RT_FAILURE(rc)) throw rc;
323
324 mData.mGuestSessions[uNewSessionID] = pGuestSession;
325
326 LogFlowFunc(("Added new session (pSession=%p, ID=%RU32), now %ld sessions total\n",
327 (GuestSession *)pGuestSession, uNewSessionID, mData.mGuestSessions.size()));
328 }
329 catch (int rc2)
330 {
331 rc = rc2;
332 }
333
334 return rc;
335}
336
337inline bool Guest::sessionExists(uint32_t uSessionID)
338{
339 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
340 return (itSessions == mData.mGuestSessions.end()) ? false : true;
341}
342
343// implementation of public methods
344/////////////////////////////////////////////////////////////////////////////
345
346STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain, IN_BSTR aSessionName, IGuestSession **aGuestSession)
347{
348#ifndef VBOX_WITH_GUEST_CONTROL
349 ReturnComNotImplemented();
350#else /* VBOX_WITH_GUEST_CONTROL */
351
352 LogFlowFuncEnter();
353
354 /* Do not allow anonymous sessions (with system rights) with official API. */
355 if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
356 return setError(E_INVALIDARG, tr("No user name specified"));
357 CheckComArgOutPointerValid(aGuestSession);
358 /* Rest is optional. */
359
360 AutoCaller autoCaller(this);
361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
362
363 HRESULT hr = S_OK;
364
365 ComObjPtr<GuestSession> pSession;
366 int rc = sessionCreate(aUser, aPassword, aDomain, aSessionName, pSession);
367 if (RT_SUCCESS(rc))
368 {
369 /* Return guest session to the caller. */
370 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
371 if (FAILED(hr2))
372 rc = VERR_COM_OBJECT_NOT_FOUND;
373
374 if (RT_SUCCESS(rc))
375 rc = pSession->queryInfo();
376 }
377
378 if (RT_FAILURE(rc))
379 {
380 switch (rc)
381 {
382 case VERR_MAX_PROCS_REACHED:
383 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
384 VBOX_GUESTCTRL_MAX_SESSIONS);
385 break;
386
387 /** @todo Add more errors here. */
388
389 default:
390 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session, rc=%Rrc"), rc);
391 break;
392 }
393 }
394
395 LogFlowFuncLeaveRC(rc);
396 return hr;
397#endif /* VBOX_WITH_GUEST_CONTROL */
398}
399
400STDMETHODIMP Guest::FindSession(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions))
401{
402#ifndef VBOX_WITH_GUEST_CONTROL
403 ReturnComNotImplemented();
404#else /* VBOX_WITH_GUEST_CONTROL */
405
406 CheckComArgOutSafeArrayPointerValid(aSessions);
407
408 LogFlowFuncEnter();
409
410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
411
412 Utf8Str strName(aSessionName);
413 std::list < ComObjPtr<GuestSession> > listSessions;
414
415 GuestSessions::const_iterator itSessions = mData.mGuestSessions.begin();
416 while (itSessions != mData.mGuestSessions.end())
417 {
418 if (strName.contains(itSessions->second->getName())) /** @todo Use a (simple) pattern match (IPRT?). */
419 listSessions.push_back(itSessions->second);
420 itSessions++;
421 }
422
423 LogFlowFunc(("Sessions with \"%ls\" = %RU32\n",
424 aSessionName, listSessions.size()));
425
426 if (listSessions.size())
427 {
428 SafeIfaceArray<IGuestSession> sessionIfacs(listSessions);
429 sessionIfacs.detachTo(ComSafeArrayOutArg(aSessions));
430
431 return S_OK;
432 }
433
434 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
435 tr("Could not find sessions with name '%ls'"),
436 aSessionName);
437#endif /* VBOX_WITH_GUEST_CONTROL */
438}
439
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