VirtualBox

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

Last change on this file since 47469 was 47469, checked in by vboxsync, 11 years ago

Guest Control: Adjustments for supporting < 4.3 Guest Additions in conjunction with the new guest session, extended testcase (now passing using latest 4.2 Guest Additions with latest trunk).

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