VirtualBox

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

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

Main: Made it compile without VBOX_WITH_GUEST_CONTROL.

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