VirtualBox

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

Last change on this file since 44870 was 44863, checked in by vboxsync, 12 years ago

GuestCtrl: Infrastructure changes for handling and executing dedicated guest sessions and protocol versioning (untested, work in progress).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.6 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 44863 2013-02-28 12:18:17Z 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 *pvData,
68 uint32_t cbData)
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, pvData, cbData));
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 if (cbData != sizeof(VBOXGUESTCTRLHOSTCALLBACK))
89 return VERR_NOT_SUPPORTED;
90 PVBOXGUESTCTRLHOSTCALLBACK pSvcCb = (PVBOXGUESTCTRLHOSTCALLBACK)pvData;
91 AssertPtr(pSvcCb);
92
93 if (!pSvcCb->mParms) /* At least context ID must be present. */
94 return VERR_INVALID_PARAMETER;
95
96 uint32_t uContextID;
97 int rc = pSvcCb->mpaParms[0].getUInt32(&uContextID);
98 AssertMsgRC(rc, ("Unable to extract callback context ID, pvData=%p\n", pSvcCb));
99 if (RT_FAILURE(rc))
100 return rc;
101#ifdef DEBUG
102 LogFlowFunc(("CID=%RU32, uSession=%RU32, uObject=%RU32, uCount=%RU32\n",
103 uContextID,
104 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
105 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
106 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID)));
107#endif
108
109 VBOXGUESTCTRLHOSTCBCTX ctxCb = { u32Function, uContextID };
110 rc = pGuest->dispatchToSession(&ctxCb, pSvcCb);
111 LogFlowFuncLeaveRC(rc);
112 return rc;
113}
114#endif /* VBOX_WITH_GUEST_CONTROL */
115
116STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress)
117{
118#ifndef VBOX_WITH_GUEST_CONTROL
119 ReturnComNotImplemented();
120#else /* VBOX_WITH_GUEST_CONTROL */
121 CheckComArgStrNotEmptyOrNull(aSource);
122 CheckComArgOutPointerValid(aProgress);
123
124 AutoCaller autoCaller(this);
125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
126
127 /* Validate flags. */
128 uint32_t fFlags = AdditionsUpdateFlag_None;
129 if (aFlags)
130 {
131 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
132 for (size_t i = 0; i < flags.size(); i++)
133 fFlags |= flags[i];
134 }
135
136 if (fFlags)
137 {
138 if (!(fFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
139 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
140 }
141
142 HRESULT hr = S_OK;
143
144 /* Create an anonymous session. This is required to run the Guest Additions
145 * update process with administrative rights. */
146 ComObjPtr<GuestSession> pSession;
147 int rc = sessionCreate("" /* User */, "" /* Password */, "" /* Domain */,
148 "Updating Guest Additions" /* Name */, pSession);
149 if (RT_FAILURE(rc))
150 {
151 switch (rc)
152 {
153 case VERR_MAX_PROCS_REACHED:
154 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
155 VBOX_GUESTCTRL_MAX_SESSIONS);
156 break;
157
158 /** @todo Add more errors here. */
159
160 default:
161 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
162 break;
163 }
164 }
165 else
166 {
167 Assert(!pSession.isNull());
168 rc = pSession->queryInfo();
169 if (RT_FAILURE(rc))
170 {
171 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not query guest session information: %Rrc"), rc);
172 }
173 else
174 {
175 try
176 {
177 ComObjPtr<Progress> pProgress;
178 SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
179 Utf8Str(aSource), fFlags);
180 rc = pSession->startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
181 if (RT_SUCCESS(rc))
182 {
183 /* Return progress to the caller. */
184 hr = pProgress.queryInterfaceTo(aProgress);
185 }
186 else
187 hr = setError(VBOX_E_IPRT_ERROR,
188 tr("Starting task for updating Guest Additions on the guest failed: %Rrc"), rc);
189 }
190 catch(std::bad_alloc &)
191 {
192 hr = E_OUTOFMEMORY;
193 }
194 }
195 }
196 return hr;
197#endif /* VBOX_WITH_GUEST_CONTROL */
198}
199
200// private methods
201/////////////////////////////////////////////////////////////////////////////
202
203int Guest::dispatchToSession(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
204{
205 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
206
207 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
208 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
209
210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
211
212 uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtxCb->uContextID);
213#ifdef DEBUG
214 LogFlowFunc(("uSessionID=%RU32 (%zu total)\n",
215 uSessionID, mData.mGuestSessions.size()));
216#endif
217 GuestSessions::const_iterator itSession
218 = mData.mGuestSessions.find(uSessionID);
219
220 int rc;
221 if (itSession != mData.mGuestSessions.end())
222 {
223 ComObjPtr<GuestSession> pSession(itSession->second);
224 Assert(!pSession.isNull());
225
226 alock.release();
227
228 bool fDispatch = true;
229#ifdef DEBUG
230 /*
231 * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
232 * it means that that guest could not handle the entire message
233 * because of its exceeding size. This should not happen on daily
234 * use but testcases might try this. It then makes no sense to dispatch
235 * this further because we don't have a valid context ID.
236 */
237 if ( pCtxCb->uFunction == GUEST_EXEC_STATUS
238 && pSvcCb->mParms >= 5)
239 {
240 CALLBACKDATA_PROC_STATUS dataCb;
241 /* pSvcCb->mpaParms[0] always contains the context ID. */
242 pSvcCb->mpaParms[1].getUInt32(&dataCb.uPID);
243 pSvcCb->mpaParms[2].getUInt32(&dataCb.uStatus);
244 pSvcCb->mpaParms[3].getUInt32(&dataCb.uFlags);
245 pSvcCb->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
246
247 if ( (dataCb.uStatus == PROC_STS_ERROR)
248 && (dataCb.uFlags == VERR_TOO_MUCH_DATA))
249 {
250 LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
251
252 Assert(dataCb.uPID == 0);
253 fDispatch = false;
254 }
255 }
256#endif
257 if (fDispatch)
258 {
259 switch (pCtxCb->uFunction)
260 {
261 case GUEST_DISCONNECTED:
262 break;
263
264 case GUEST_SESSION_NOTIFY:
265 rc = pSession->dispatchToThis(pCtxCb, pSvcCb);
266 break;
267
268 case GUEST_EXEC_STATUS:
269 case GUEST_EXEC_OUTPUT:
270 case GUEST_EXEC_INPUT_STATUS:
271 case GUEST_EXEC_IO_NOTIFY:
272 rc = pSession->dispatchToProcess(pCtxCb, pSvcCb);
273 break;
274
275 case GUEST_FILE_NOTIFY:
276 rc = pSession->dispatchToFile(pCtxCb, pSvcCb);
277 break;
278
279 default:
280 rc = VERR_NOT_SUPPORTED;
281 break;
282 }
283 }
284 else
285 rc = VERR_NOT_FOUND;
286 }
287 else
288 rc = VERR_NOT_FOUND;
289
290 LogFlowFuncLeaveRC(rc);
291 return rc;
292}
293
294int Guest::sessionRemove(GuestSession *pSession)
295{
296 LogFlowThisFuncEnter();
297
298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
299
300 int rc = VERR_NOT_FOUND;
301
302 LogFlowFunc(("Closing session (ID=%RU32) ...\n", pSession->getId()));
303
304 for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
305 itSessions != mData.mGuestSessions.end(); ++itSessions)
306 {
307 if (pSession == itSessions->second)
308 {
309 LogFlowFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
310 (GuestSession *)itSessions->second, itSessions->second->getId(), mData.mGuestSessions.size() - 1));
311
312 mData.mGuestSessions.erase(itSessions);
313
314 rc = VINF_SUCCESS;
315 break;
316 }
317 }
318
319 LogFlowFuncLeaveRC(rc);
320 return rc;
321}
322
323int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, const Utf8Str &strDomain,
324 const Utf8Str &strSessionName, ComObjPtr<GuestSession> &pGuestSession)
325{
326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
327
328 int rc = VERR_MAX_PROCS_REACHED;
329 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
330 return rc;
331
332 try
333 {
334 /* Create a new session ID and assign it. */
335 uint32_t uNewSessionID = 0;
336 uint32_t uTries = 0;
337
338 for (;;)
339 {
340 /* Is the context ID already used? */
341 if (!sessionExists(uNewSessionID))
342 {
343 rc = VINF_SUCCESS;
344 break;
345 }
346 uNewSessionID++;
347 if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
348 uNewSessionID = 0;
349
350 if (++uTries == UINT32_MAX)
351 break; /* Don't try too hard. */
352 }
353 if (RT_FAILURE(rc)) throw rc;
354
355 /* Create the session object. */
356 HRESULT hr = pGuestSession.createObject();
357 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
358
359 rc = pGuestSession->queryInfo();
360 if (RT_FAILURE(rc)) throw rc;
361 rc = pGuestSession->init(this, uNewSessionID,
362 strUser, strPassword, strDomain, strSessionName);
363 if (RT_FAILURE(rc)) throw rc;
364
365 /*
366 * Add session object to our session map. This is necessary
367 * before calling openSession because the guest calls back
368 * with the creation result to this session.
369 */
370 mData.mGuestSessions[uNewSessionID] = pGuestSession;
371
372 /* Drop write lock before opening session, because this will
373 * involve the main dispatcher to run. */
374 alock.release();
375
376 /** @todo Do we need an openSessioAsync() call? This might be
377 * a problem on webservice calls (= timeouts) if opening the
378 * guest session takes too long -> Then we also would
379 * need some session.getStatus() API call! */
380
381 /* Open (create) the session on the guest. */
382 int guestRc;
383 rc = pGuestSession->openSession(0 /* Flags */, 30 * 1000 /* 30s timeout */,
384 &guestRc);
385 if (RT_FAILURE(rc))
386 {
387 switch (rc)
388 {
389 case VERR_MAX_PROCS_REACHED:
390 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
391 VBOX_GUESTCTRL_MAX_SESSIONS);
392 break;
393
394 /** @todo Add more errors here. */
395
396 default:
397 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session, rc=%Rrc"), rc);
398 break;
399 }
400
401 /* Remove failed session again. */
402 int rc2 = sessionRemove(pGuestSession);
403 AssertRC(rc2);
404 }
405 else
406 LogFlowFunc(("Created new guest session (pSession=%p, ID=%RU32), now %zu sessions total\n",
407 (GuestSession *)pGuestSession, uNewSessionID, mData.mGuestSessions.size()));
408 }
409 catch (int rc2)
410 {
411 rc = rc2;
412 }
413
414 LogFlowFuncLeaveRC(rc);
415 return rc;
416}
417
418inline bool Guest::sessionExists(uint32_t uSessionID)
419{
420 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
421 return (itSessions == mData.mGuestSessions.end()) ? false : true;
422}
423
424// implementation of public methods
425/////////////////////////////////////////////////////////////////////////////
426
427STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain, IN_BSTR aSessionName, IGuestSession **aGuestSession)
428{
429#ifndef VBOX_WITH_GUEST_CONTROL
430 ReturnComNotImplemented();
431#else /* VBOX_WITH_GUEST_CONTROL */
432
433 LogFlowFuncEnter();
434
435 /* Do not allow anonymous sessions (with system rights) with official API. */
436 if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
437 return setError(E_INVALIDARG, tr("No user name specified"));
438 CheckComArgOutPointerValid(aGuestSession);
439 /* Rest is optional. */
440
441 AutoCaller autoCaller(this);
442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
443
444 HRESULT hr = S_OK;
445
446 ComObjPtr<GuestSession> pSession;
447 int rc = sessionCreate(aUser, aPassword, aDomain, aSessionName, pSession);
448 if (RT_SUCCESS(rc))
449 {
450 /* Return guest session to the caller. */
451 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
452 if (FAILED(hr2))
453 rc = VERR_COM_OBJECT_NOT_FOUND;
454 }
455
456 if (RT_FAILURE(rc))
457 {
458 switch (rc)
459 {
460 case VERR_MAX_PROCS_REACHED:
461 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
462 VBOX_GUESTCTRL_MAX_SESSIONS);
463 break;
464
465 /** @todo Add more errors here. */
466
467 default:
468 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session, rc=%Rrc"), rc);
469 break;
470 }
471 }
472
473 LogFlowFuncLeaveRC(rc);
474 return hr;
475#endif /* VBOX_WITH_GUEST_CONTROL */
476}
477
478STDMETHODIMP Guest::FindSession(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions))
479{
480#ifndef VBOX_WITH_GUEST_CONTROL
481 ReturnComNotImplemented();
482#else /* VBOX_WITH_GUEST_CONTROL */
483
484 CheckComArgOutSafeArrayPointerValid(aSessions);
485
486 LogFlowFuncEnter();
487
488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
489
490 Utf8Str strName(aSessionName);
491 std::list < ComObjPtr<GuestSession> > listSessions;
492
493 GuestSessions::const_iterator itSessions = mData.mGuestSessions.begin();
494 while (itSessions != mData.mGuestSessions.end())
495 {
496 if (strName.contains(itSessions->second->getName())) /** @todo Use a (simple) pattern match (IPRT?). */
497 listSessions.push_back(itSessions->second);
498 itSessions++;
499 }
500
501 LogFlowFunc(("Sessions with \"%ls\" = %RU32\n",
502 aSessionName, listSessions.size()));
503
504 if (listSessions.size())
505 {
506 SafeIfaceArray<IGuestSession> sessionIfacs(listSessions);
507 sessionIfacs.detachTo(ComSafeArrayOutArg(aSessions));
508
509 return S_OK;
510 }
511
512 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
513 tr("Could not find sessions with name '%ls'"),
514 aSessionName);
515#endif /* VBOX_WITH_GUEST_CONTROL */
516}
517
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette