VirtualBox

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

Last change on this file since 51217 was 50727, checked in by vboxsync, 11 years ago

6813 src-client/GuestSessionImpl.cpp

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 50727 2014-03-07 18:21:44Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2013 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 "VBoxEvents.h"
26#include "VMMDev.h"
27
28#include "AutoCaller.h"
29
30#include <VBox/VMMDev.h>
31#ifdef VBOX_WITH_GUEST_CONTROL
32# include <VBox/com/array.h>
33# include <VBox/com/ErrorInfo.h>
34#endif
35#include <iprt/cpp/utils.h>
36#include <iprt/file.h>
37#include <iprt/getopt.h>
38#include <iprt/isofs.h>
39#include <iprt/list.h>
40#include <iprt/path.h>
41#include <VBox/vmm/pgm.h>
42
43#include <memory>
44
45#ifdef LOG_GROUP
46 #undef LOG_GROUP
47#endif
48#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
49#include <VBox/log.h>
50
51
52// public methods only for internal purposes
53/////////////////////////////////////////////////////////////////////////////
54
55#ifdef VBOX_WITH_GUEST_CONTROL
56/**
57 * Static callback function for receiving updates on guest control commands
58 * from the guest. Acts as a dispatcher for the actual class instance.
59 *
60 * @returns VBox status code.
61 *
62 * @todo
63 *
64 */
65/* static */
66DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
67 uint32_t u32Function,
68 void *pvData,
69 uint32_t cbData)
70{
71 using namespace guestControl;
72
73 /*
74 * No locking, as this is purely a notification which does not make any
75 * changes to the object state.
76 */
77 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
78 pvExtension, u32Function, pvData, cbData));
79 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
80 Assert(!pGuest.isNull());
81
82 /*
83 * For guest control 2.0 using the legacy commands we need to do the following here:
84 * - Get the callback header to access the context ID
85 * - Get the context ID of the callback
86 * - Extract the session ID out of the context ID
87 * - Dispatch the whole stuff to the appropriate session (if still exists)
88 */
89 if (cbData != sizeof(VBOXGUESTCTRLHOSTCALLBACK))
90 return VERR_NOT_SUPPORTED;
91 PVBOXGUESTCTRLHOSTCALLBACK pSvcCb = (PVBOXGUESTCTRLHOSTCALLBACK)pvData;
92 AssertPtr(pSvcCb);
93
94 if (!pSvcCb->mParms) /* At least context ID must be present. */
95 return VERR_INVALID_PARAMETER;
96
97 uint32_t uContextID;
98 int rc = pSvcCb->mpaParms[0].getUInt32(&uContextID);
99 AssertMsgRCReturn(rc, ("Unable to extract callback context ID, pvData=%p\n", pSvcCb), rc);
100#ifdef DEBUG
101 LogFlowFunc(("CID=%RU32, uSession=%RU32, uObject=%RU32, uCount=%RU32\n",
102 uContextID,
103 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
104 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
105 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID)));
106#endif
107
108 VBOXGUESTCTRLHOSTCBCTX ctxCb = { u32Function, uContextID };
109 rc = pGuest->dispatchToSession(&ctxCb, pSvcCb);
110
111 LogFlowFunc(("Returning rc=%Rrc\n", rc));
112 return rc;
113}
114#endif /* VBOX_WITH_GUEST_CONTROL */
115
116STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(IN_BSTR, aArguments),
117 ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress)
118{
119#ifndef VBOX_WITH_GUEST_CONTROL
120 ReturnComNotImplemented();
121#else /* VBOX_WITH_GUEST_CONTROL */
122 CheckComArgStrNotEmptyOrNull(aSource);
123 CheckComArgOutPointerValid(aProgress);
124
125 AutoCaller autoCaller(this);
126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
127
128 /* Validate flags. */
129 uint32_t fFlags = AdditionsUpdateFlag_None;
130 if (aFlags)
131 {
132 com::SafeArray<AdditionsUpdateFlag_T> flags(ComSafeArrayInArg(aFlags));
133 for (size_t i = 0; i < flags.size(); i++)
134 fFlags |= flags[i];
135 }
136
137 if (fFlags)
138 {
139 if (!(fFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
140 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
141 }
142
143 int rc = VINF_SUCCESS;
144
145 ProcessArguments aArgs;
146 if (aArguments)
147 {
148 try
149 {
150 com::SafeArray<IN_BSTR> arguments(ComSafeArrayInArg(aArguments));
151 for (size_t i = 0; i < arguments.size(); i++)
152 aArgs.push_back(Utf8Str(arguments[i]));
153 }
154 catch(std::bad_alloc &)
155 {
156 rc = VERR_NO_MEMORY;
157 }
158 }
159
160 HRESULT hr = S_OK;
161
162 /*
163 * Create an anonymous session. This is required to run the Guest Additions
164 * update process with administrative rights.
165 */
166 GuestSessionStartupInfo startupInfo;
167 startupInfo.mName = "Updating Guest Additions";
168
169 GuestCredentials guestCreds;
170 RT_ZERO(guestCreds);
171
172 ComObjPtr<GuestSession> pSession;
173 if (RT_SUCCESS(rc))
174 rc = sessionCreate(startupInfo, guestCreds, pSession);
175 if (RT_FAILURE(rc))
176 {
177 switch (rc)
178 {
179 case VERR_MAX_PROCS_REACHED:
180 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
181 VBOX_GUESTCTRL_MAX_SESSIONS);
182 break;
183
184 /** @todo Add more errors here. */
185
186 default:
187 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
188 break;
189 }
190 }
191 else
192 {
193 Assert(!pSession.isNull());
194 int guestRc;
195 rc = pSession->i_startSessionInternal(&guestRc);
196 if (RT_FAILURE(rc))
197 {
198 /** @todo Handle guestRc! */
199
200 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not open guest session: %Rrc"), rc);
201 }
202 else
203 {
204 try
205 {
206 ComObjPtr<Progress> pProgress;
207 SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
208 Utf8Str(aSource), aArgs, fFlags);
209 rc = pSession->i_startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
210 if (RT_SUCCESS(rc))
211 {
212 /* Return progress to the caller. */
213 hr = pProgress.queryInterfaceTo(aProgress);
214 }
215 else
216 hr = setError(VBOX_E_IPRT_ERROR,
217 tr("Starting task for updating Guest Additions on the guest failed: %Rrc"), rc);
218 }
219 catch(std::bad_alloc &)
220 {
221 hr = E_OUTOFMEMORY;
222 }
223 }
224 }
225 return hr;
226#endif /* VBOX_WITH_GUEST_CONTROL */
227}
228
229// private methods
230/////////////////////////////////////////////////////////////////////////////
231
232int Guest::dispatchToSession(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
233{
234 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
235
236 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
237 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
238
239 LogFlowFunc(("uFunction=%RU32, uContextID=%RU32, uProtocol=%RU32\n",
240 pCtxCb->uFunction, pCtxCb->uContextID, pCtxCb->uProtocol));
241
242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
243
244 uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtxCb->uContextID);
245#ifdef DEBUG
246 LogFlowFunc(("uSessionID=%RU32 (%zu total)\n",
247 uSessionID, mData.mGuestSessions.size()));
248#endif
249 GuestSessions::const_iterator itSession
250 = mData.mGuestSessions.find(uSessionID);
251
252 int rc;
253 if (itSession != mData.mGuestSessions.end())
254 {
255 ComObjPtr<GuestSession> pSession(itSession->second);
256 Assert(!pSession.isNull());
257
258 alock.release();
259
260 bool fDispatch = true;
261#ifdef DEBUG
262 /*
263 * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
264 * it means that that guest could not handle the entire message
265 * because of its exceeding size. This should not happen on daily
266 * use but testcases might try this. It then makes no sense to dispatch
267 * this further because we don't have a valid context ID.
268 */
269 if ( pCtxCb->uFunction == GUEST_EXEC_STATUS
270 && pSvcCb->mParms >= 5)
271 {
272 CALLBACKDATA_PROC_STATUS dataCb;
273 /* pSvcCb->mpaParms[0] always contains the context ID. */
274 pSvcCb->mpaParms[1].getUInt32(&dataCb.uPID);
275 pSvcCb->mpaParms[2].getUInt32(&dataCb.uStatus);
276 pSvcCb->mpaParms[3].getUInt32(&dataCb.uFlags);
277 pSvcCb->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
278
279 if ( ( dataCb.uStatus == PROC_STS_ERROR)
280 /** @todo Note: Due to legacy reasons we cannot change uFlags to
281 * int32_t, so just cast it for now. */
282 && ((int32_t)dataCb.uFlags == VERR_TOO_MUCH_DATA))
283 {
284 LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
285
286 Assert(dataCb.uPID == 0);
287 fDispatch = false;
288 }
289 }
290#endif
291 if (fDispatch)
292 {
293 switch (pCtxCb->uFunction)
294 {
295 case GUEST_DISCONNECTED:
296 rc = pSession->i_dispatchToThis(pCtxCb, pSvcCb);
297 break;
298
299 case GUEST_EXEC_STATUS:
300 case GUEST_EXEC_OUTPUT:
301 case GUEST_EXEC_INPUT_STATUS:
302 case GUEST_EXEC_IO_NOTIFY:
303 rc = pSession->i_dispatchToProcess(pCtxCb, pSvcCb);
304 break;
305
306 case GUEST_FILE_NOTIFY:
307 rc = pSession->i_dispatchToFile(pCtxCb, pSvcCb);
308 break;
309
310 case GUEST_SESSION_NOTIFY:
311 rc = pSession->i_dispatchToThis(pCtxCb, pSvcCb);
312 break;
313
314 default:
315 /*
316 * Try processing generic messages which might
317 * (or might not) supported by certain objects.
318 * If the message either is not found or supported
319 * by the approprirate object, try handling it
320 * in this session object.
321 */
322 rc = pSession->i_dispatchToObject(pCtxCb, pSvcCb);
323 if ( rc == VERR_NOT_FOUND
324 || rc == VERR_NOT_SUPPORTED)
325 {
326 alock.acquire();
327
328 rc = pSession->dispatchGeneric(pCtxCb, pSvcCb);
329 }
330#ifndef DEBUG_andy
331 if (rc == VERR_NOT_IMPLEMENTED)
332 AssertMsgFailed(("Received not handled function %RU32\n", pCtxCb->uFunction));
333#endif
334 break;
335 }
336 }
337 else
338 rc = VERR_NOT_FOUND;
339 }
340 else
341 rc = VERR_NOT_FOUND;
342
343 LogFlowFuncLeaveRC(rc);
344 return rc;
345}
346
347int Guest::sessionRemove(GuestSession *pSession)
348{
349 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
350
351 LogFlowThisFuncEnter();
352
353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
354
355 int rc = VERR_NOT_FOUND;
356
357 LogFlowThisFunc(("Removing session (ID=%RU32) ...\n", pSession->i_getId()));
358
359 GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
360 while (itSessions != mData.mGuestSessions.end())
361 {
362 if (pSession == itSessions->second)
363 {
364#ifdef DEBUG_andy
365 ULONG cRefs = pSession->AddRef();
366 Assert(cRefs >= 2);
367 LogFlowThisFunc(("pCurSession=%p, cRefs=%RU32\n", pSession, cRefs - 2));
368 pSession->Release();
369#endif
370 /* Make sure to consume the pointer before the one of the
371 * iterator gets released. */
372 ComObjPtr<GuestSession> pCurSession = pSession;
373
374 LogFlowThisFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
375 pSession, pSession->i_getId(), mData.mGuestSessions.size() - 1));
376
377 rc = pSession->i_onRemove();
378 mData.mGuestSessions.erase(itSessions);
379
380 alock.release(); /* Release lock before firing off event. */
381
382 fireGuestSessionRegisteredEvent(mEventSource, pCurSession,
383 false /* Unregistered */);
384 pCurSession.setNull();
385 break;
386 }
387
388 itSessions++;
389 }
390
391 LogFlowFuncLeaveRC(rc);
392 return rc;
393}
394
395int Guest::sessionCreate(const GuestSessionStartupInfo &ssInfo,
396 const GuestCredentials &guestCreds, ComObjPtr<GuestSession> &pGuestSession)
397{
398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
399
400 int rc = VERR_MAX_PROCS_REACHED;
401 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
402 return rc;
403
404 try
405 {
406 /* Create a new session ID and assign it. */
407 uint32_t uNewSessionID = VBOX_GUESTCTRL_SESSION_ID_BASE;
408 uint32_t uTries = 0;
409
410 for (;;)
411 {
412 /* Is the context ID already used? */
413 if (!sessionExists(uNewSessionID))
414 {
415 rc = VINF_SUCCESS;
416 break;
417 }
418 uNewSessionID++;
419 if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
420 uNewSessionID = VBOX_GUESTCTRL_SESSION_ID_BASE;
421
422 if (++uTries == VBOX_GUESTCTRL_MAX_SESSIONS)
423 break; /* Don't try too hard. */
424 }
425 if (RT_FAILURE(rc)) throw rc;
426
427 /* Create the session object. */
428 HRESULT hr = pGuestSession.createObject();
429 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
430
431 /** @todo Use an overloaded copy operator. Later. */
432 GuestSessionStartupInfo startupInfo;
433 startupInfo.mID = uNewSessionID; /* Assign new session ID. */
434 startupInfo.mName = ssInfo.mName;
435 startupInfo.mOpenFlags = ssInfo.mOpenFlags;
436 startupInfo.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
437
438 GuestCredentials guestCredentials;
439 if (!guestCreds.mUser.isEmpty())
440 {
441 /** @todo Use an overloaded copy operator. Later. */
442 guestCredentials.mUser = guestCreds.mUser;
443 guestCredentials.mPassword = guestCreds.mPassword;
444 guestCredentials.mDomain = guestCreds.mDomain;
445 }
446 else
447 {
448 /* Internal (annonymous) session. */
449 startupInfo.mIsInternal = true;
450 }
451
452 rc = pGuestSession->init(this, startupInfo, guestCredentials);
453 if (RT_FAILURE(rc)) throw rc;
454
455 /*
456 * Add session object to our session map. This is necessary
457 * before calling openSession because the guest calls back
458 * with the creation result of this session.
459 */
460 mData.mGuestSessions[uNewSessionID] = pGuestSession;
461
462 alock.release(); /* Release lock before firing off event. */
463
464 fireGuestSessionRegisteredEvent(mEventSource, pGuestSession,
465 true /* Registered */);
466 }
467 catch (int rc2)
468 {
469 rc = rc2;
470 }
471
472 LogFlowFuncLeaveRC(rc);
473 return rc;
474}
475
476inline bool Guest::sessionExists(uint32_t uSessionID)
477{
478 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
479 return (itSessions == mData.mGuestSessions.end()) ? false : true;
480}
481
482// implementation of public methods
483/////////////////////////////////////////////////////////////////////////////
484
485STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain,
486 IN_BSTR aSessionName, IGuestSession **aGuestSession)
487{
488#ifndef VBOX_WITH_GUEST_CONTROL
489 ReturnComNotImplemented();
490#else /* VBOX_WITH_GUEST_CONTROL */
491
492 LogFlowFuncEnter();
493
494 /* Do not allow anonymous sessions (with system rights) with public API. */
495 if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
496 return setError(E_INVALIDARG, tr("No user name specified"));
497 if (RT_UNLIKELY((aPassword) == NULL)) /* Allow empty passwords. */
498 return setError(E_INVALIDARG, tr("No password specified"));
499 CheckComArgOutPointerValid(aGuestSession);
500 /* Rest is optional. */
501
502 AutoCaller autoCaller(this);
503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
504
505 GuestSessionStartupInfo startupInfo;
506 startupInfo.mName = aSessionName;
507
508 GuestCredentials guestCreds;
509 guestCreds.mUser = aUser;
510 guestCreds.mPassword = aPassword;
511 guestCreds.mDomain = aDomain;
512
513 ComObjPtr<GuestSession> pSession;
514 int rc = sessionCreate(startupInfo, guestCreds, pSession);
515 if (RT_SUCCESS(rc))
516 {
517 /* Return guest session to the caller. */
518 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
519 if (FAILED(hr2))
520 rc = VERR_COM_OBJECT_NOT_FOUND;
521 }
522
523 if (RT_SUCCESS(rc))
524 {
525 /* Start (fork) the session asynchronously
526 * on the guest. */
527 rc = pSession->i_startSessionAsync();
528 }
529
530 HRESULT hr = S_OK;
531
532 if (RT_FAILURE(rc))
533 {
534 switch (rc)
535 {
536 case VERR_MAX_PROCS_REACHED:
537 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
538 VBOX_GUESTCTRL_MAX_SESSIONS);
539 break;
540
541 /** @todo Add more errors here. */
542
543 default:
544 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
545 break;
546 }
547 }
548
549 LogFlowThisFunc(("Returning rc=%Rhrc\n", hr));
550 return hr;
551#endif /* VBOX_WITH_GUEST_CONTROL */
552}
553
554STDMETHODIMP Guest::FindSession(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions))
555{
556#ifndef VBOX_WITH_GUEST_CONTROL
557 ReturnComNotImplemented();
558#else /* VBOX_WITH_GUEST_CONTROL */
559
560 CheckComArgOutSafeArrayPointerValid(aSessions);
561
562 LogFlowFuncEnter();
563
564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
565
566 Utf8Str strName(aSessionName);
567 std::list < ComObjPtr<GuestSession> > listSessions;
568
569 GuestSessions::const_iterator itSessions = mData.mGuestSessions.begin();
570 while (itSessions != mData.mGuestSessions.end())
571 {
572 if (strName.contains(itSessions->second->i_getName())) /** @todo Use a (simple) pattern match (IPRT?). */
573 listSessions.push_back(itSessions->second);
574 itSessions++;
575 }
576
577 LogFlowFunc(("Sessions with \"%ls\" = %RU32\n",
578 aSessionName, listSessions.size()));
579
580 if (listSessions.size())
581 {
582 SafeIfaceArray<IGuestSession> sessionIfacs(listSessions);
583 sessionIfacs.detachTo(ComSafeArrayOutArg(aSessions));
584
585 return S_OK;
586 }
587
588 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
589 tr("Could not find sessions with name '%ls'"),
590 aSessionName);
591#endif /* VBOX_WITH_GUEST_CONTROL */
592}
593
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