VirtualBox

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

Last change on this file since 42892 was 42864, checked in by vboxsync, 12 years ago

Main/Guest control: Removed old API.

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