VirtualBox

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

Last change on this file since 45772 was 45415, checked in by vboxsync, 12 years ago

GuestCtrl: Implemented using (public) VirtualBox events instead of own callback mechanisms. Bugfixes for testcases (still work in progress).

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