VirtualBox

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

Last change on this file since 70772 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use