VirtualBox

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

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

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • 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 73003 2018-07-09 11:09:32Z 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 = pSvcCb->mpaParms[0].getUInt32(&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 pSvcCb->mpaParms[1].getUInt32(&dataCb.uPID);
169 pSvcCb->mpaParms[2].getUInt32(&dataCb.uStatus);
170 pSvcCb->mpaParms[3].getUInt32(&dataCb.uFlags);
171 pSvcCb->mpaParms[4].getPointer(&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