VirtualBox

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

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

GuestCtrlImpl.cpp: Warning, cast.

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