VirtualBox

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

Last change on this file since 45029 was 45010, checked in by vboxsync, 12 years ago

GuestCtrl: More code for guest session infrastructure handling (untested, work in progress).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 45010 2013-03-12 17:47:56Z 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 /*
145 * Create an anonymous session. This is required to run the Guest Additions
146 * update process with administrative rights.
147 */
148 GuestSessionStartupInfo startupInfo;
149 startupInfo.mName = "Updating Guest Additions";
150
151 GuestCredentials guestCreds;
152 RT_ZERO(guestCreds);
153
154 ComObjPtr<GuestSession> pSession;
155 int rc = sessionCreate(startupInfo, guestCreds, pSession);
156 if (RT_FAILURE(rc))
157 {
158 switch (rc)
159 {
160 case VERR_MAX_PROCS_REACHED:
161 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
162 VBOX_GUESTCTRL_MAX_SESSIONS);
163 break;
164
165 /** @todo Add more errors here. */
166
167 default:
168 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
169 break;
170 }
171 }
172 else
173 {
174 Assert(!pSession.isNull());
175 int guestRc;
176 rc = pSession->startSessionIntenal(&guestRc);
177 if (RT_FAILURE(rc))
178 {
179 /** @todo Handle guestRc! */
180
181 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not open guest session: %Rrc"), rc);
182 }
183 else
184 {
185 try
186 {
187 ComObjPtr<Progress> pProgress;
188 SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
189 Utf8Str(aSource), fFlags);
190 rc = pSession->startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
191 if (RT_SUCCESS(rc))
192 {
193 /* Return progress to the caller. */
194 hr = pProgress.queryInterfaceTo(aProgress);
195 }
196 else
197 hr = setError(VBOX_E_IPRT_ERROR,
198 tr("Starting task for updating Guest Additions on the guest failed: %Rrc"), rc);
199 }
200 catch(std::bad_alloc &)
201 {
202 hr = E_OUTOFMEMORY;
203 }
204 }
205 }
206 return hr;
207#endif /* VBOX_WITH_GUEST_CONTROL */
208}
209
210// private methods
211/////////////////////////////////////////////////////////////////////////////
212
213int Guest::dispatchToSession(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
214{
215 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
216
217 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
218 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
219
220 LogFlowFunc(("uFunction=%RU32, uContextID=%RU32, uProtocol=%RU32\n",
221 pCtxCb->uFunction, pCtxCb->uContextID, pCtxCb->uProtocol));
222
223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
224
225 uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtxCb->uContextID);
226#ifdef DEBUG
227 LogFlowFunc(("uSessionID=%RU32 (%zu total)\n",
228 uSessionID, mData.mGuestSessions.size()));
229#endif
230 GuestSessions::const_iterator itSession
231 = mData.mGuestSessions.find(uSessionID);
232
233 int rc;
234 if (itSession != mData.mGuestSessions.end())
235 {
236 ComObjPtr<GuestSession> pSession(itSession->second);
237 Assert(!pSession.isNull());
238
239 alock.release();
240
241 bool fDispatch = true;
242#ifdef DEBUG
243 /*
244 * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
245 * it means that that guest could not handle the entire message
246 * because of its exceeding size. This should not happen on daily
247 * use but testcases might try this. It then makes no sense to dispatch
248 * this further because we don't have a valid context ID.
249 */
250 if ( pCtxCb->uFunction == GUEST_EXEC_STATUS
251 && pSvcCb->mParms >= 5)
252 {
253 CALLBACKDATA_PROC_STATUS dataCb;
254 /* pSvcCb->mpaParms[0] always contains the context ID. */
255 pSvcCb->mpaParms[1].getUInt32(&dataCb.uPID);
256 pSvcCb->mpaParms[2].getUInt32(&dataCb.uStatus);
257 pSvcCb->mpaParms[3].getUInt32(&dataCb.uFlags);
258 pSvcCb->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
259
260 if ( (dataCb.uStatus == PROC_STS_ERROR)
261 && (dataCb.uFlags == VERR_TOO_MUCH_DATA))
262 {
263 LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
264
265 Assert(dataCb.uPID == 0);
266 fDispatch = false;
267 }
268 }
269#endif
270 if (fDispatch)
271 {
272 switch (pCtxCb->uFunction)
273 {
274 case GUEST_DISCONNECTED:
275 rc = pSession->dispatchToThis(pCtxCb, pSvcCb);
276 break;
277
278 case GUEST_SESSION_NOTIFY:
279 rc = pSession->dispatchToThis(pCtxCb, pSvcCb);
280 break;
281
282 case GUEST_EXEC_STATUS:
283 case GUEST_EXEC_OUTPUT:
284 case GUEST_EXEC_INPUT_STATUS:
285 case GUEST_EXEC_IO_NOTIFY:
286 rc = pSession->dispatchToProcess(pCtxCb, pSvcCb);
287 break;
288
289 case GUEST_FILE_NOTIFY:
290 rc = pSession->dispatchToFile(pCtxCb, pSvcCb);
291 break;
292
293 default:
294 rc = VERR_NOT_SUPPORTED;
295 break;
296 }
297 }
298 else
299 rc = VERR_NOT_FOUND;
300 }
301 else
302 rc = VERR_NOT_FOUND;
303
304 LogFlowFuncLeaveRC(rc);
305 return rc;
306}
307
308int Guest::sessionRemove(GuestSession *pSession)
309{
310 LogFlowThisFuncEnter();
311
312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
313
314 int rc = VERR_NOT_FOUND;
315
316 LogFlowFunc(("Closing session (ID=%RU32) ...\n", pSession->getId()));
317
318 for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
319 itSessions != mData.mGuestSessions.end(); ++itSessions)
320 {
321 if (pSession == itSessions->second)
322 {
323 LogFlowFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
324 (GuestSession *)itSessions->second, itSessions->second->getId(), mData.mGuestSessions.size() - 1));
325
326 mData.mGuestSessions.erase(itSessions++);
327
328 rc = VINF_SUCCESS;
329 break;
330 }
331 }
332
333 LogFlowFuncLeaveRC(rc);
334 return rc;
335}
336
337int Guest::sessionCreate(const GuestSessionStartupInfo &ssInfo,
338 const GuestCredentials &guestCreds, ComObjPtr<GuestSession> &pGuestSession)
339{
340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
341
342 int rc = VERR_MAX_PROCS_REACHED;
343 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
344 return rc;
345
346 try
347 {
348 /* Create a new session ID and assign it. */
349 uint32_t uNewSessionID = 0;
350 uint32_t uTries = 0;
351
352 for (;;)
353 {
354 /* Is the context ID already used? */
355 if (!sessionExists(uNewSessionID))
356 {
357 rc = VINF_SUCCESS;
358 break;
359 }
360 uNewSessionID++;
361 if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
362 uNewSessionID = 0;
363
364 if (++uTries == VBOX_GUESTCTRL_MAX_SESSIONS)
365 break; /* Don't try too hard. */
366 }
367 if (RT_FAILURE(rc)) throw rc;
368
369 /* Create the session object. */
370 HRESULT hr = pGuestSession.createObject();
371 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
372
373 /** @todo Use an overloaded copy operator. Later. */
374 GuestSessionStartupInfo startupInfo;
375 startupInfo.mID = uNewSessionID; /* Assign new session ID. */
376 startupInfo.mName = ssInfo.mName;
377 startupInfo.mOpenFlags = ssInfo.mOpenFlags;
378 startupInfo.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
379
380 GuestCredentials guestCredentials;
381 if (!guestCreds.mUser.isEmpty())
382 {
383 /** @todo Use an overloaded copy operator. Later. */
384 guestCredentials.mUser = guestCreds.mUser;
385 guestCredentials.mPassword = guestCreds.mPassword;
386 guestCredentials.mDomain = guestCreds.mDomain;
387 }
388 else
389 {
390 /* Internal (annonymous) session. */
391 startupInfo.mIsInternal = true;
392 }
393
394 rc = pGuestSession->init(this, startupInfo, guestCredentials);
395 if (RT_FAILURE(rc)) throw rc;
396
397 /*
398 * Add session object to our session map. This is necessary
399 * before calling openSession because the guest calls back
400 * with the creation result to this session.
401 */
402 mData.mGuestSessions[uNewSessionID] = pGuestSession;
403
404 /* Drop write lock before opening session, because this will
405 * involve the main dispatcher to run. */
406 alock.release();
407 }
408 catch (int rc2)
409 {
410 rc = rc2;
411 }
412
413 LogFlowFuncLeaveRC(rc);
414 return rc;
415}
416
417inline bool Guest::sessionExists(uint32_t uSessionID)
418{
419 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
420 return (itSessions == mData.mGuestSessions.end()) ? false : true;
421}
422
423// implementation of public methods
424/////////////////////////////////////////////////////////////////////////////
425
426STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain, IN_BSTR aSessionName, IGuestSession **aGuestSession)
427{
428#ifndef VBOX_WITH_GUEST_CONTROL
429 ReturnComNotImplemented();
430#else /* VBOX_WITH_GUEST_CONTROL */
431
432 LogFlowFuncEnter();
433
434 /* Do not allow anonymous sessions (with system rights) with public API. */
435 if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
436 return setError(E_INVALIDARG, tr("No user name specified"));
437 CheckComArgOutPointerValid(aGuestSession);
438 /* Rest is optional. */
439
440 AutoCaller autoCaller(this);
441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
442
443 GuestSessionStartupInfo startupInfo;
444 startupInfo.mName = aSessionName;
445
446 GuestCredentials guestCreds;
447 guestCreds.mUser = aUser;
448 guestCreds.mPassword = aPassword;
449 guestCreds.mDomain = aDomain;
450
451 ComObjPtr<GuestSession> pSession;
452 int rc = sessionCreate(startupInfo, guestCreds, pSession);
453 if (RT_SUCCESS(rc))
454 {
455 /* Return guest session to the caller. */
456 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
457 if (FAILED(hr2))
458 rc = VERR_COM_OBJECT_NOT_FOUND;
459 }
460
461 int guestRc;
462 if (RT_SUCCESS(rc))
463 {
464 /** @todo Do we need to use openSessioAsync() here? Otherwise
465 * there might be a problem on webservice calls (= timeouts)
466 * if opening the guest session takes too long -> Use
467 * the new session.getStatus() API call! */
468
469 /* Start (fork) the session on the guest. */
470 rc = pSession->startSessionIntenal(&guestRc);
471 if (RT_SUCCESS(rc))
472 {
473 LogFlowFunc(("Created new guest session (pSession=%p), now %zu sessions total\n",
474 (GuestSession *)pSession, mData.mGuestSessions.size()));
475 }
476 }
477
478 HRESULT hr = S_OK;
479
480 if (RT_FAILURE(rc))
481 {
482 switch (rc)
483 {
484 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
485 hr = GuestSession::setErrorExternal(this, guestRc);
486 break;
487
488 case VERR_MAX_PROCS_REACHED:
489 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
490 VBOX_GUESTCTRL_MAX_SESSIONS);
491 break;
492
493 /** @todo Add more errors here. */
494
495 default:
496 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
497 break;
498 }
499 }
500
501 LogFlowFuncLeaveRC(rc);
502 return hr;
503#endif /* VBOX_WITH_GUEST_CONTROL */
504}
505
506STDMETHODIMP Guest::FindSession(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions))
507{
508#ifndef VBOX_WITH_GUEST_CONTROL
509 ReturnComNotImplemented();
510#else /* VBOX_WITH_GUEST_CONTROL */
511
512 CheckComArgOutSafeArrayPointerValid(aSessions);
513
514 LogFlowFuncEnter();
515
516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
517
518 Utf8Str strName(aSessionName);
519 std::list < ComObjPtr<GuestSession> > listSessions;
520
521 GuestSessions::const_iterator itSessions = mData.mGuestSessions.begin();
522 while (itSessions != mData.mGuestSessions.end())
523 {
524 if (strName.contains(itSessions->second->getName())) /** @todo Use a (simple) pattern match (IPRT?). */
525 listSessions.push_back(itSessions->second);
526 itSessions++;
527 }
528
529 LogFlowFunc(("Sessions with \"%ls\" = %RU32\n",
530 aSessionName, listSessions.size()));
531
532 if (listSessions.size())
533 {
534 SafeIfaceArray<IGuestSession> sessionIfacs(listSessions);
535 sessionIfacs.detachTo(ComSafeArrayOutArg(aSessions));
536
537 return S_OK;
538 }
539
540 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
541 tr("Could not find sessions with name '%ls'"),
542 aSessionName);
543#endif /* VBOX_WITH_GUEST_CONTROL */
544}
545
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