VirtualBox

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

Last change on this file since 42913 was 42897, checked in by vboxsync, 12 years ago

Guest Control 2.0: Bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 42897 2012-08-21 10:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 "VMMDev.h"
26
27#include "AutoCaller.h"
28
29#include <VBox/VMMDev.h>
30#ifdef VBOX_WITH_GUEST_CONTROL
31# include <VBox/com/array.h>
32# include <VBox/com/ErrorInfo.h>
33#endif
34#include <iprt/cpp/utils.h>
35#include <iprt/file.h>
36#include <iprt/getopt.h>
37#include <iprt/isofs.h>
38#include <iprt/list.h>
39#include <iprt/path.h>
40#include <VBox/vmm/pgm.h>
41
42#include <memory>
43
44#ifdef LOG_GROUP
45 #undef LOG_GROUP
46#endif
47#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
48#include <VBox/log.h>
49
50
51// public methods only for internal purposes
52/////////////////////////////////////////////////////////////////////////////
53
54#ifdef VBOX_WITH_GUEST_CONTROL
55/**
56 * Static callback function for receiving updates on guest control commands
57 * from the guest. Acts as a dispatcher for the actual class instance.
58 *
59 * @returns VBox status code.
60 *
61 * @todo
62 *
63 */
64/* static */
65DECLCALLBACK(int) Guest::notifyCtrlDispatcher(void *pvExtension,
66 uint32_t u32Function,
67 void *pvParms,
68 uint32_t cbParms)
69{
70 using namespace guestControl;
71
72 /*
73 * No locking, as this is purely a notification which does not make any
74 * changes to the object state.
75 */
76 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
77 pvExtension, u32Function, pvParms, cbParms));
78 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
79 Assert(!pGuest.isNull());
80
81 /*
82 * For guest control 2.0 using the legacy commands we need to do the following here:
83 * - Get the callback header to access the context ID
84 * - Get the context ID of the callback
85 * - Extract the session ID out of the context ID
86 * - Dispatch the whole stuff to the appropriate session (if still exists)
87 */
88
89 PCALLBACKHEADER pHeader = (PCALLBACKHEADER)pvParms;
90 AssertPtr(pHeader);
91
92#ifdef DEBUG
93 LogFlowFunc(("CID=%RU32, uSession=%RU32, uObject=%RU32, uCount=%RU32\n",
94 pHeader->u32ContextID,
95 VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHeader->u32ContextID),
96 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHeader->u32ContextID),
97 VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pHeader->u32ContextID)));
98#endif
99
100 bool fDispatch = true;
101#ifdef DEBUG
102 /*
103 * Pre-check: If we got a status message with an error and VERR_TOO_MUCH_DATA
104 * it means that that guest could not handle the entire message
105 * because of its exceeding size. This should not happen on daily
106 * use but testcases might try this. It then makes no sense to dispatch
107 * this further because we don't have a valid context ID.
108 */
109 if (u32Function == GUEST_EXEC_SEND_STATUS)
110 {
111 PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
112 AssertPtr(pCallbackData);
113 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
114 AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
115
116 if ( pCallbackData->u32Status == PROC_STS_ERROR
117 && ((int)pCallbackData->u32Flags) == VERR_TOO_MUCH_DATA)
118 {
119 LogFlowFunc(("Requested command with too much data, skipping dispatching ...\n"));
120
121 Assert(pCallbackData->u32PID == 0);
122 fDispatch = false;
123 }
124 }
125#endif
126 int rc = VINF_SUCCESS;
127 if (fDispatch)
128 {
129 rc = pGuest->dispatchToSession(pHeader->u32ContextID, u32Function, pvParms, cbParms);
130 if (RT_SUCCESS(rc))
131 return rc;
132 }
133
134 LogFlowFuncLeaveRC(rc);
135 return rc;
136}
137#endif /* VBOX_WITH_GUEST_CONTROL */
138
139STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ComSafeArrayIn(AdditionsUpdateFlag_T, aFlags), IProgress **aProgress)
140{
141#ifndef VBOX_WITH_GUEST_CONTROL
142 ReturnComNotImplemented();
143#else /* VBOX_WITH_GUEST_CONTROL */
144 CheckComArgStrNotEmptyOrNull(aSource);
145 CheckComArgOutPointerValid(aProgress);
146
147 AutoCaller autoCaller(this);
148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
149
150 /* Validate flags. */
151 uint32_t fFlags = AdditionsUpdateFlag_None;
152 if (aFlags)
153 {
154 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
155 for (size_t i = 0; i < flags.size(); i++)
156 fFlags |= flags[i];
157 }
158
159 if (fFlags)
160 {
161 if (!(fFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
162 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
163 }
164
165 HRESULT hr = S_OK;
166
167 /* Create an anonymous session. This is required to run the Guest Additions
168 * update process with administrative rights. */
169 ComObjPtr<GuestSession> pSession;
170 int rc = sessionCreate("" /* User */, "" /* Password */, "" /* Domain */,
171 "Updating Guest Additions" /* Name */, pSession);
172 if (RT_FAILURE(rc))
173 {
174 switch (rc)
175 {
176 case VERR_MAX_PROCS_REACHED:
177 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
178 VBOX_GUESTCTRL_MAX_SESSIONS);
179 break;
180
181 /** @todo Add more errors here. */
182
183 default:
184 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session: %Rrc"), rc);
185 break;
186 }
187 }
188 else
189 {
190 Assert(!pSession.isNull());
191 rc = pSession->queryInfo();
192 if (RT_FAILURE(rc))
193 {
194 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not query guest session information: %Rrc"), rc);
195 }
196 else
197 {
198 ComObjPtr<Progress> pProgress;
199 SessionTaskUpdateAdditions *pTask = new SessionTaskUpdateAdditions(pSession /* GuestSession */,
200 Utf8Str(aSource), fFlags);
201 AssertPtrReturn(pTask, VERR_NO_MEMORY);
202 rc = pSession->startTaskAsync(tr("Updating Guest Additions"), pTask, pProgress);
203 if (RT_SUCCESS(rc))
204 {
205 /* Return progress to the caller. */
206 hr = pProgress.queryInterfaceTo(aProgress);
207 }
208 else
209 hr = setError(VBOX_E_IPRT_ERROR,
210 tr("Starting task for updating Guest Additions on the guest failed: %Rrc"), rc);
211 }
212 }
213 return hr;
214#endif /* VBOX_WITH_GUEST_CONTROL */
215}
216
217// private methods
218/////////////////////////////////////////////////////////////////////////////
219
220int Guest::dispatchToSession(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
221{
222 LogFlowFuncEnter();
223
224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
225
226 uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID);
227#ifdef DEBUG
228 LogFlowFunc(("uSessionID=%RU32 (%RU32 total)\n",
229 uSessionID, mData.mGuestSessions.size()));
230#endif
231 int rc;
232 GuestSessions::const_iterator itSession
233 = mData.mGuestSessions.find(uSessionID);
234 if (itSession != mData.mGuestSessions.end())
235 {
236 ComObjPtr<GuestSession> pSession(itSession->second);
237 Assert(!pSession.isNull());
238
239 alock.release();
240 rc = pSession->dispatchToProcess(uContextID, uFunction, pvData, cbData);
241 }
242 else
243 rc = VERR_NOT_FOUND;
244
245 LogFlowFuncLeaveRC(rc);
246 return rc;
247}
248
249int Guest::sessionRemove(GuestSession *pSession)
250{
251 LogFlowThisFuncEnter();
252
253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
254
255 int rc = VERR_NOT_FOUND;
256
257 LogFlowFunc(("Closing session (ID=%RU32) ...\n", pSession->getId()));
258
259 for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
260 itSessions != mData.mGuestSessions.end(); ++itSessions)
261 {
262 if (pSession == itSessions->second)
263 {
264 LogFlowFunc(("Removing session (pSession=%p, ID=%RU32) (now total %ld sessions)\n",
265 itSessions->second, itSessions->second->getId(), mData.mGuestSessions.size() - 1));
266
267 mData.mGuestSessions.erase(itSessions);
268
269 rc = VINF_SUCCESS;
270 break;
271 }
272 }
273
274 LogFlowFuncLeaveRC(rc);
275 return rc;
276}
277
278int Guest::sessionCreate(const Utf8Str &strUser, const Utf8Str &strPassword, const Utf8Str &strDomain,
279 const Utf8Str &strSessionName, ComObjPtr<GuestSession> &pGuestSession)
280{
281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
282
283 int rc = VERR_MAX_PROCS_REACHED;
284 if (mData.mGuestSessions.size() >= VBOX_GUESTCTRL_MAX_SESSIONS)
285 return rc;
286
287 try
288 {
289 /* Create a new session ID and assign it. */
290 uint32_t uNewSessionID = 0;
291 uint32_t uTries = 0;
292
293 for (;;)
294 {
295 /* Is the context ID already used? */
296 if (!sessionExists(uNewSessionID))
297 {
298 rc = VINF_SUCCESS;
299 break;
300 }
301 uNewSessionID++;
302 if (uNewSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS)
303 uNewSessionID = 0;
304
305 if (++uTries == UINT32_MAX)
306 break; /* Don't try too hard. */
307 }
308 if (RT_FAILURE(rc)) throw rc;
309
310 /* Create the session object. */
311 HRESULT hr = pGuestSession.createObject();
312 if (FAILED(hr)) throw VERR_COM_UNEXPECTED;
313
314 rc = pGuestSession->init(this, uNewSessionID,
315 strUser, strPassword, strDomain, strSessionName);
316 if (RT_FAILURE(rc)) throw rc;
317
318 mData.mGuestSessions[uNewSessionID] = pGuestSession;
319
320 LogFlowFunc(("Added new session (pSession=%p, ID=%RU32), now %ld sessions total\n",
321 pGuestSession, uNewSessionID, mData.mGuestSessions.size()));
322 }
323 catch (int rc2)
324 {
325 rc = rc2;
326 }
327
328 return rc;
329}
330
331inline bool Guest::sessionExists(uint32_t uSessionID)
332{
333 GuestSessions::const_iterator itSessions = mData.mGuestSessions.find(uSessionID);
334 return (itSessions == mData.mGuestSessions.end()) ? false : true;
335}
336
337// implementation of public methods
338/////////////////////////////////////////////////////////////////////////////
339
340STDMETHODIMP Guest::CreateSession(IN_BSTR aUser, IN_BSTR aPassword, IN_BSTR aDomain, IN_BSTR aSessionName, IGuestSession **aGuestSession)
341{
342#ifndef VBOX_WITH_GUEST_CONTROL
343 ReturnComNotImplemented();
344#else /* VBOX_WITH_GUEST_CONTROL */
345
346 LogFlowFuncEnter();
347
348 /* Do not allow anonymous sessions (with system rights) with official API. */
349 if (RT_UNLIKELY((aUser) == NULL || *(aUser) == '\0'))
350 return setError(E_INVALIDARG, tr("No user name specified"));
351 CheckComArgOutPointerValid(aGuestSession);
352 /* Rest is optional. */
353
354 AutoCaller autoCaller(this);
355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
356
357 HRESULT hr = S_OK;
358
359 ComObjPtr<GuestSession> pSession;
360 int rc = sessionCreate(aUser, aPassword, aDomain, aSessionName, pSession);
361 if (RT_SUCCESS(rc))
362 {
363 /* Return guest session to the caller. */
364 HRESULT hr2 = pSession.queryInterfaceTo(aGuestSession);
365 if (FAILED(hr2))
366 rc = VERR_COM_OBJECT_NOT_FOUND;
367
368 if (RT_SUCCESS(rc))
369 rc = pSession->queryInfo();
370 }
371
372 if (RT_FAILURE(rc))
373 {
374 switch (rc)
375 {
376 case VERR_MAX_PROCS_REACHED:
377 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
378 VBOX_GUESTCTRL_MAX_SESSIONS);
379 break;
380
381 /** @todo Add more errors here. */
382
383 default:
384 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest session, rc=%Rrc"), rc);
385 break;
386 }
387 }
388
389 LogFlowFuncLeaveRC(rc);
390 return hr;
391#endif /* VBOX_WITH_GUEST_CONTROL */
392}
393
394STDMETHODIMP Guest::FindSession(IN_BSTR aSessionName, ComSafeArrayOut(IGuestSession *, aSessions))
395{
396#ifndef VBOX_WITH_GUEST_CONTROL
397 ReturnComNotImplemented();
398#else /* VBOX_WITH_GUEST_CONTROL */
399
400 CheckComArgOutSafeArrayPointerValid(aSessions);
401
402 LogFlowFuncEnter();
403
404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
405
406 Utf8Str strName(aSessionName);
407 std::list < ComObjPtr<GuestSession> > listSessions;
408
409 GuestSessions::const_iterator itSessions = mData.mGuestSessions.begin();
410 while (itSessions != mData.mGuestSessions.end())
411 {
412 if (strName.contains(itSessions->second->getName())) /** @todo Use a (simple) pattern match (IPRT?). */
413 listSessions.push_back(itSessions->second);
414 itSessions++;
415 }
416
417 LogFlowFunc(("Sessions with \"%ls\" = %RU32\n",
418 aSessionName, listSessions.size()));
419
420 if (listSessions.size())
421 {
422 SafeIfaceArray<IGuestSession> sessionIfacs(listSessions);
423 sessionIfacs.detachTo(ComSafeArrayOutArg(aSessions));
424
425 return S_OK;
426 }
427
428 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
429 tr("Could not find sessions with name '%ls'"),
430 aSessionName);
431#endif /* VBOX_WITH_GUEST_CONTROL */
432}
433
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