VirtualBox

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

Last change on this file since 53517 was 52085, checked in by vboxsync, 10 years ago

6813 - GuestImpl using COM Wrappers - bug fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 52085 2014-07-17 19:26:24Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2014 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::i_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->i_dispatchToSession(&ctxCb, pSvcCb);
110
111 LogFlowFunc(("Returning rc=%Rrc\n", rc));
112 return rc;
113}
114#endif /* VBOX_WITH_GUEST_CONTROL */
115
116HRESULT Guest::updateGuestAdditions(const com::Utf8Str &aSource, const std::vector<com::Utf8Str> &aArguments,
117 const std::vector<AdditionsUpdateFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
118{
119#ifndef VBOX_WITH_GUEST_CONTROL
120 ReturnComNotImplemented();
121#else /* VBOX_WITH_GUEST_CONTROL */
122
123 /* Validate flags. */
124 uint32_t fFlags = AdditionsUpdateFlag_None;
125 if (aFlags.size())
126 for (size_t i = 0; i < aFlags.size(); ++i)
127 fFlags |= aFlags[i];
128
129 if (fFlags && !(fFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
130 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
131
132 int rc = VINF_SUCCESS;
133
134 ProcessArguments aArgs;
135 aArgs.resize(0);
136
137 if (aArguments.size())
138 {
139 try
140 {
141 for (size_t i = 0; i < aArguments.size(); ++i)
142 aArgs.push_back(aArguments[i]);
143 }
144 catch(std::bad_alloc &)
145 {
146 rc = VERR_NO_MEMORY;
147 }
148 }
149
150 HRESULT hr = S_OK;
151
152 /*
153 * Create an anonymous session. This is required to run the Guest Additions
154 * update process with administrative rights.
155 */
156 GuestSessionStartupInfo startupInfo;
157 startupInfo.mName = "Updating Guest Additions";
158
159 GuestCredentials guestCreds;
160 RT_ZERO(guestCreds);
161
162 ComObjPtr<GuestSession> pSession;
163 if (RT_SUCCESS(rc))
164 rc = i_sessionCreate(startupInfo, guestCreds, pSession);
165 if (RT_FAILURE(rc))
166 {
167 switch (rc)
168 {
169 case VERR_MAX_PROCS_REACHED:
170 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
171 VBOX_GUESTCTRL_MAX_SESSIONS);
172 break;
173
174 /** @todo Add more errors here. */
175
176 default:
177 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
178 break;
179 }
180 }
181 else
182 {
183 Assert(!pSession.isNull());
184 int guestRc;
185 rc = pSession->i_startSessionInternal(&guestRc);
186 if (RT_FAILURE(rc))
187 {
188 /** @todo Handle guestRc! */
189
190 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not open guest session: %Rrc"), rc);
191 }
192 else
193 {
194 try
195 {
196 ComObjPtr<Progress> pProgress;
197 SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
198 aSource, aArgs, fFlags);
199 rc = pSession->i_startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
200 if (RT_SUCCESS(rc))
201 {
202 /* Return progress to the caller. */
203 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
204 }
205 else
206 hr = setError(VBOX_E_IPRT_ERROR,
207 tr("Starting task for updating Guest Additions on the guest failed: %Rrc"), rc);
208 }
209 catch(std::bad_alloc &)
210 {
211 hr = E_OUTOFMEMORY;
212 }
213 }
214 }
215 return hr;
216#endif /* VBOX_WITH_GUEST_CONTROL */
217}
218
219// private methods
220/////////////////////////////////////////////////////////////////////////////
221
222int Guest::i_dispatchToSession(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
223{
224 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
225
226 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
227 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
228
229 LogFlowFunc(("uFunction=%RU32, uContextID=%RU32, uProtocol=%RU32\n",
230 pCtxCb->uFunction, pCtxCb->uContextID, pCtxCb->uProtocol));
231
232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
233
234 uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtxCb->uContextID);
235#ifdef DEBUG
236 LogFlowFunc(("uSessionID=%RU32 (%zu total)\n",
237 uSessionID, mData.mGuestSessions.size()));
238#endif
239 GuestSessions::const_iterator itSession
240 = mData.mGuestSessions.find(uSessionID);
241
242 int rc;
243 if (itSession != mData.mGuestSessions.end())
244 {
245 ComObjPtr<GuestSession> pSession(itSession->second);
246 Assert(!pSession.isNull());
247
248 alock.release();
249
250 bool fDispatch = true;
251#ifdef DEBUG
252 /*
253 * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
254 * it means that that guest could not handle the entire message
255 * because of its exceeding size. This should not happen on daily
256 * use but testcases might try this. It then makes no sense to dispatch
257 * this further because we don't have a valid context ID.
258 */
259 if ( pCtxCb->uFunction == GUEST_EXEC_STATUS
260 && pSvcCb->mParms >= 5)
261 {
262 CALLBACKDATA_PROC_STATUS dataCb;
263 /* pSvcCb->mpaParms[0] always contains the context ID. */
264 pSvcCb->mpaParms[1].getUInt32(&dataCb.uPID);
265 pSvcCb->mpaParms[2].getUInt32(&dataCb.uStatus);
266 pSvcCb->mpaParms[3].getUInt32(&dataCb.uFlags);
267 pSvcCb->mpaParms[4].getPointer(&dataCb.pvData, &dataCb.cbData);
268
269 if ( ( dataCb.uStatus == PROC_STS_ERROR)
270 /** @todo Note: Due to legacy reasons we cannot change uFlags to
271 * int32_t, so just cast it for now. */
272 && ((int32_t)dataCb.uFlags == VERR_TOO_MUCH_DATA))
273 {
274 LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
275
276 Assert(dataCb.uPID == 0);
277 fDispatch = false;
278 }
279 }
280#endif
281 if (fDispatch)
282 {
283 switch (pCtxCb->uFunction)
284 {
285 case GUEST_DISCONNECTED:
286 rc = pSession->i_dispatchToThis(pCtxCb, pSvcCb);
287 break;
288
289 case GUEST_EXEC_STATUS:
290 case GUEST_EXEC_OUTPUT:
291 case GUEST_EXEC_INPUT_STATUS:
292 case GUEST_EXEC_IO_NOTIFY:
293 rc = pSession->i_dispatchToProcess(pCtxCb, pSvcCb);
294 break;
295
296 case GUEST_FILE_NOTIFY:
297 rc = pSession->i_dispatchToFile(pCtxCb, pSvcCb);
298 break;
299
300 case GUEST_SESSION_NOTIFY:
301 rc = pSession->i_dispatchToThis(pCtxCb, pSvcCb);
302 break;
303
304 default:
305 /*
306 * Try processing generic messages which might
307 * (or might not) supported by certain objects.
308 * If the message either is not found or supported
309 * by the approprirate object, try handling it
310 * in this session object.
311 */
312 rc = pSession->i_dispatchToObject(pCtxCb, pSvcCb);
313 if ( rc == VERR_NOT_FOUND
314 || rc == VERR_NOT_SUPPORTED)
315 {
316 alock.acquire();
317
318 rc = pSession->dispatchGeneric(pCtxCb, pSvcCb);
319 }
320#ifndef DEBUG_andy
321 if (rc == VERR_NOT_IMPLEMENTED)
322 AssertMsgFailed(("Received not handled function %RU32\n", pCtxCb->uFunction));
323#endif
324 break;
325 }
326 }
327 else
328 rc = VERR_NOT_FOUND;
329 }
330 else
331 rc = VERR_NOT_FOUND;
332
333 LogFlowFuncLeaveRC(rc);
334 return rc;
335}
336
337int Guest::i_sessionRemove(GuestSession *pSession)
338{
339 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
340
341 LogFlowThisFuncEnter();
342
343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
344
345 int rc = VERR_NOT_FOUND;
346
347 LogFlowThisFunc(("Removing session (ID=%RU32) ...\n", pSession->i_getId()));
348
349 GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
350 while (itSessions != mData.mGuestSessions.end())
351 {
352 if (pSession == itSessions->second)
353 {
354#ifdef DEBUG_andy
355 ULONG cRefs = pSession->AddRef();
356 Assert(cRefs >= 2);
357 LogFlowThisFunc(("pCurSession=%p, cRefs=%RU32\n", pSession, cRefs - 2));
358 pSession->Release();
359#endif
360 /* Make sure to consume the pointer before the one of the
361 * iterator gets released. */
362 ComObjPtr<GuestSession> pCurSession = pSession;
363
364 LogFlowThisFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
365 pSession, pSession->i_getId(), mData.mGuestSessions.size() - 1));
366
367 rc = pSession->i_onRemove();
368 mData.mGuestSessions.erase(itSessions);
369
370 alock.release(); /* Release lock before firing off event. */
371
372 fireGuestSessionRegisteredEvent(mEventSource, pCurSession,
373 false /* Unregistered */);
374 pCurSession.setNull();
375 break;
376 }
377
378 itSessions++;
379 }
380
381 LogFlowFuncLeaveRC(rc);
382 return rc;
383}
384
385int Guest::i_sessionCreate(const GuestSessionStartupInfo &ssInfo,
386 const GuestCredentials &guestCreds, ComObjPtr<GuestSession> &pGuestSession)
387{
388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
389
390 int rc = VERR_MAX_PROCS_REACHED;
391 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
392 return rc;
393
394 try
395 {
396 /* Create a new session ID and assign it. */
397 uint32_t uNewSessionID = VBOX_GUESTCTRL_SESSION_ID_BASE;
398 uint32_t uTries = 0;
399
400 for (;;)
401 {
402 /* Is the context ID already used? */
403 if (!i_sessionExists(uNewSessionID))
404 {
405 rc = VINF_SUCCESS;
406 break;
407 }
408 uNewSessionID++;
409 if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
410 uNewSessionID = VBOX_GUESTCTRL_SESSION_ID_BASE;
411
412 if (++uTries == VBOX_GUESTCTRL_MAX_SESSIONS)
413 break; /* Don't try too hard. */
414 }
415 if (RT_FAILURE(rc)) throw rc;
416
417 /* Create the session object. */
418 HRESULT hr = pGuestSession.createObject();
419 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
420
421 /** @todo Use an overloaded copy operator. Later. */
422 GuestSessionStartupInfo startupInfo;
423 startupInfo.mID = uNewSessionID; /* Assign new session ID. */
424 startupInfo.mName = ssInfo.mName;
425 startupInfo.mOpenFlags = ssInfo.mOpenFlags;
426 startupInfo.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
427
428 GuestCredentials guestCredentials;
429 if (!guestCreds.mUser.isEmpty())
430 {
431 /** @todo Use an overloaded copy operator. Later. */
432 guestCredentials.mUser = guestCreds.mUser;
433 guestCredentials.mPassword = guestCreds.mPassword;
434 guestCredentials.mDomain = guestCreds.mDomain;
435 }
436 else
437 {
438 /* Internal (annonymous) session. */
439 startupInfo.mIsInternal = true;
440 }
441
442 rc = pGuestSession->init(this, startupInfo, guestCredentials);
443 if (RT_FAILURE(rc)) throw rc;
444
445 /*
446 * Add session object to our session map. This is necessary
447 * before calling openSession because the guest calls back
448 * with the creation result of this session.
449 */
450 mData.mGuestSessions[uNewSessionID] = pGuestSession;
451
452 alock.release(); /* Release lock before firing off event. */
453
454 fireGuestSessionRegisteredEvent(mEventSource, pGuestSession,
455 true /* Registered */);
456 }
457 catch (int rc2)
458 {
459 rc = rc2;
460 }
461
462 LogFlowFuncLeaveRC(rc);
463 return rc;
464}
465
466inline bool Guest::i_sessionExists(uint32_t uSessionID)
467{
468 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
469 return (itSessions == mData.mGuestSessions.end()) ? false : true;
470}
471
472// implementation of public methods
473/////////////////////////////////////////////////////////////////////////////
474HRESULT Guest::createSession(const com::Utf8Str &aUser, const com::Utf8Str &aPassword, const com::Utf8Str &aDomain,
475 const com::Utf8Str &aSessionName, ComPtr<IGuestSession> &aGuestSession)
476
477{
478#ifndef VBOX_WITH_GUEST_CONTROL
479 ReturnComNotImplemented();
480#else /* VBOX_WITH_GUEST_CONTROL */
481
482 LogFlowFuncEnter();
483
484 /* Do not allow anonymous sessions (with system rights) with public API. */
485 if (RT_UNLIKELY(!aUser.length()))
486 return setError(E_INVALIDARG, tr("No user name specified"));
487
488 GuestSessionStartupInfo startupInfo;
489 startupInfo.mName = aSessionName;
490
491 GuestCredentials guestCreds;
492 guestCreds.mUser = aUser;
493 guestCreds.mPassword = aPassword;
494 guestCreds.mDomain = aDomain;
495
496 ComObjPtr<GuestSession> pSession;
497 int rc = i_sessionCreate(startupInfo, guestCreds, pSession);
498 if (RT_SUCCESS(rc))
499 {
500 /* Return guest session to the caller. */
501 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession.asOutParam());
502 if (FAILED(hr2))
503 rc = VERR_COM_OBJECT_NOT_FOUND;
504 }
505
506 if (RT_SUCCESS(rc))
507 /* Start (fork) the session asynchronously
508 * on the guest. */
509 rc = pSession->i_startSessionAsync();
510
511 HRESULT hr = S_OK;
512
513 if (RT_FAILURE(rc))
514 {
515 switch (rc)
516 {
517 case VERR_MAX_PROCS_REACHED:
518 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
519 VBOX_GUESTCTRL_MAX_SESSIONS);
520 break;
521
522 /** @todo Add more errors here. */
523
524 default:
525 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
526 break;
527 }
528 }
529
530 LogFlowThisFunc(("Returning rc=%Rhrc\n", hr));
531 return hr;
532#endif /* VBOX_WITH_GUEST_CONTROL */
533}
534
535HRESULT Guest::findSession(const com::Utf8Str &aSessionName, std::vector<ComPtr<IGuestSession> > &aSessions)
536{
537#ifndef VBOX_WITH_GUEST_CONTROL
538 ReturnComNotImplemented();
539#else /* VBOX_WITH_GUEST_CONTROL */
540
541 LogFlowFuncEnter();
542
543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
544
545 Utf8Str strName(aSessionName);
546 std::list < ComObjPtr<GuestSession> > listSessions;
547
548 GuestSessions::const_iterator itSessions = mData.mGuestSessions.begin();
549 while (itSessions != mData.mGuestSessions.end())
550 {
551 if (strName.contains(itSessions->second->i_getName())) /** @todo Use a (simple) pattern match (IPRT?). */
552 listSessions.push_back(itSessions->second);
553 itSessions++;
554 }
555
556 LogFlowFunc(("Sessions with \"%s\" = %RU32\n",
557 aSessionName.c_str(), listSessions.size()));
558
559 aSessions.resize(listSessions.size());
560 if (listSessions.size())
561 {
562 size_t i = 0;
563 for (std::list < ComObjPtr<GuestSession> >::const_iterator it = listSessions.begin(); it != listSessions.end(); ++it, ++i)
564 (*it).queryInterfaceTo(aSessions[i].asOutParam());
565
566 return S_OK;
567
568 }
569
570 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
571 tr("Could not find sessions with name '%s'"),
572 aSessionName.c_str());
573#endif /* VBOX_WITH_GUEST_CONTROL */
574}
575
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