VirtualBox

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

Last change on this file since 75737 was 75737, checked in by vboxsync, 6 years ago

HGCM: Replace C++-style inline members on VBOXHGCMSVCPARM with simple functions.
bugref:9172: Shared folder performance tuning
Changes in bugref:9172 caused a build regression on Ubuntu 18.10 due to the
use of RT_ZERO on a structure containing an embedded VBOXHGCMSVCPARM, as
VBOXHGCMSVCPARM had member functions and was therefore not a simple plain-
old-data structure. Rather than just doing the sensible thing and zeroing
it in a different way, I converted the inline member getters and setters to
standard inline C functions, including fixing callers. Actually I had planned
this for some time, as the member function use seemed a bit gratuitous in
hindsight.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use