VirtualBox

source: vbox/trunk/src/VBox/Main/GuestImpl.cpp@ 27996

Last change on this file since 27996 was 27996, checked in by vboxsync, 15 years ago

Guest Control: Update (Main: argument as array, SDK docs).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.0 KB
Line 
1/* $Id: GuestImpl.cpp 27996 2010-04-06 09:48:28Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "GuestImpl.h"
25
26#include "Global.h"
27#include "ConsoleImpl.h"
28#include "VMMDev.h"
29
30#include "AutoCaller.h"
31#include "Logging.h"
32
33#include <VBox/VMMDev.h>
34#ifdef VBOX_WITH_GUEST_CONTROL
35# include <VBox/HostServices/GuestControlSvc.h>
36# include <VBox/com/array.h>
37#endif
38#include <iprt/cpp/utils.h>
39#include <iprt/getopt.h>
40
41// defines
42/////////////////////////////////////////////////////////////////////////////
43
44// constructor / destructor
45/////////////////////////////////////////////////////////////////////////////
46
47DEFINE_EMPTY_CTOR_DTOR (Guest)
48
49HRESULT Guest::FinalConstruct()
50{
51 return S_OK;
52}
53
54void Guest::FinalRelease()
55{
56 uninit ();
57}
58
59// public methods only for internal purposes
60/////////////////////////////////////////////////////////////////////////////
61
62/**
63 * Initializes the guest object.
64 */
65HRESULT Guest::init (Console *aParent)
66{
67 LogFlowThisFunc(("aParent=%p\n", aParent));
68
69 ComAssertRet(aParent, E_INVALIDARG);
70
71 /* Enclose the state transition NotReady->InInit->Ready */
72 AutoInitSpan autoInitSpan(this);
73 AssertReturn(autoInitSpan.isOk(), E_FAIL);
74
75 unconst(mParent) = aParent;
76
77 /* mData.mAdditionsActive is FALSE */
78
79 /* Confirm a successful initialization when it's the case */
80 autoInitSpan.setSucceeded();
81
82 ULONG aMemoryBalloonSize;
83 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
84 if (ret == S_OK)
85 mMemoryBalloonSize = aMemoryBalloonSize;
86 else
87 mMemoryBalloonSize = 0; /* Default is no ballooning */
88
89 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
90
91 /* Clear statistics. */
92 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
93 mCurrentGuestStat[i] = 0;
94
95 return S_OK;
96}
97
98/**
99 * Uninitializes the instance and sets the ready flag to FALSE.
100 * Called either from FinalRelease() or by the parent when it gets destroyed.
101 */
102void Guest::uninit()
103{
104 LogFlowThisFunc(("\n"));
105
106 /* Enclose the state transition Ready->InUninit->NotReady */
107 AutoUninitSpan autoUninitSpan(this);
108 if (autoUninitSpan.uninitDone())
109 return;
110
111 unconst(mParent) = NULL;
112}
113
114// IGuest properties
115/////////////////////////////////////////////////////////////////////////////
116
117STDMETHODIMP Guest::COMGETTER(OSTypeId) (BSTR *aOSTypeId)
118{
119 CheckComArgOutPointerValid(aOSTypeId);
120
121 AutoCaller autoCaller(this);
122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
123
124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
125
126 // redirect the call to IMachine if no additions are installed
127 if (mData.mAdditionsVersion.isEmpty())
128 return mParent->machine()->COMGETTER(OSTypeId)(aOSTypeId);
129
130 mData.mOSTypeId.cloneTo(aOSTypeId);
131
132 return S_OK;
133}
134
135STDMETHODIMP Guest::COMGETTER(AdditionsActive) (BOOL *aAdditionsActive)
136{
137 CheckComArgOutPointerValid(aAdditionsActive);
138
139 AutoCaller autoCaller(this);
140 if (FAILED(autoCaller.rc())) return autoCaller.rc();
141
142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
143
144 *aAdditionsActive = mData.mAdditionsActive;
145
146 return S_OK;
147}
148
149STDMETHODIMP Guest::COMGETTER(AdditionsVersion) (BSTR *aAdditionsVersion)
150{
151 CheckComArgOutPointerValid(aAdditionsVersion);
152
153 AutoCaller autoCaller(this);
154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
155
156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
157
158 mData.mAdditionsVersion.cloneTo(aAdditionsVersion);
159
160 return S_OK;
161}
162
163STDMETHODIMP Guest::COMGETTER(SupportsSeamless) (BOOL *aSupportsSeamless)
164{
165 CheckComArgOutPointerValid(aSupportsSeamless);
166
167 AutoCaller autoCaller(this);
168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
169
170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
171
172 *aSupportsSeamless = mData.mSupportsSeamless;
173
174 return S_OK;
175}
176
177STDMETHODIMP Guest::COMGETTER(SupportsGraphics) (BOOL *aSupportsGraphics)
178{
179 CheckComArgOutPointerValid(aSupportsGraphics);
180
181 AutoCaller autoCaller(this);
182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
183
184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
185
186 *aSupportsGraphics = mData.mSupportsGraphics;
187
188 return S_OK;
189}
190
191STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize) (ULONG *aMemoryBalloonSize)
192{
193 CheckComArgOutPointerValid(aMemoryBalloonSize);
194
195 AutoCaller autoCaller(this);
196 if (FAILED(autoCaller.rc())) return autoCaller.rc();
197
198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
199
200 *aMemoryBalloonSize = mMemoryBalloonSize;
201
202 return S_OK;
203}
204
205STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize) (ULONG aMemoryBalloonSize)
206{
207 AutoCaller autoCaller(this);
208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
209
210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
211
212 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
213 if (ret == S_OK)
214 {
215 mMemoryBalloonSize = aMemoryBalloonSize;
216 /* forward the information to the VMM device */
217 VMMDev *vmmDev = mParent->getVMMDev();
218 if (vmmDev)
219 vmmDev->getVMMDevPort()->pfnSetMemoryBalloon(vmmDev->getVMMDevPort(), aMemoryBalloonSize);
220 }
221
222 return ret;
223}
224
225STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)
226{
227 CheckComArgOutPointerValid(aUpdateInterval);
228
229 AutoCaller autoCaller(this);
230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
231
232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
233
234 *aUpdateInterval = mStatUpdateInterval;
235 return S_OK;
236}
237
238STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)
239{
240 AutoCaller autoCaller(this);
241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
242
243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
244
245 mStatUpdateInterval = aUpdateInterval;
246 /* forward the information to the VMM device */
247 VMMDev *vmmDev = mParent->getVMMDev();
248 if (vmmDev)
249 vmmDev->getVMMDevPort()->pfnSetStatisticsInterval(vmmDev->getVMMDevPort(), aUpdateInterval);
250
251 return S_OK;
252}
253
254STDMETHODIMP Guest::InternalGetStatistics(ULONG aCpuId, ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
255 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemCache,
256 ULONG *aPageTotal, ULONG *aPageFree)
257{
258 CheckComArgOutPointerValid(aCpuUser);
259 CheckComArgOutPointerValid(aCpuKernel);
260 CheckComArgOutPointerValid(aCpuIdle);
261 CheckComArgOutPointerValid(aMemTotal);
262 CheckComArgOutPointerValid(aMemFree);
263 CheckComArgOutPointerValid(aMemBalloon);
264 CheckComArgOutPointerValid(aMemCache);
265 CheckComArgOutPointerValid(aPageTotal);
266 CheckComArgOutPointerValid(aPageFree);
267
268 AutoCaller autoCaller(this);
269 if (FAILED(autoCaller.rc())) return autoCaller.rc();
270
271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
272
273 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
274 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
275 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
276 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL];
277 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE];
278 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON];
279 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE];
280 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL];
281 *aPageFree = mCurrentGuestStat[GUESTSTATTYPE_PAGEFREE];
282
283 return S_OK;
284}
285
286HRESULT Guest::SetStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
287{
288 AutoCaller autoCaller(this);
289 if (FAILED(autoCaller.rc())) return autoCaller.rc();
290
291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
292
293 if (enmType >= GUESTSTATTYPE_MAX)
294 return E_INVALIDARG;
295
296 mCurrentGuestStat[enmType] = aVal;
297 return S_OK;
298}
299
300STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,
301 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)
302{
303 AutoCaller autoCaller(this);
304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
305
306 /* forward the information to the VMM device */
307 VMMDev *vmmDev = mParent->getVMMDev();
308 if (vmmDev)
309 {
310 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
311 if (!aAllowInteractiveLogon)
312 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
313
314 vmmDev->getVMMDevPort()->pfnSetCredentials(vmmDev->getVMMDevPort(),
315 Utf8Str(aUserName).raw(), Utf8Str(aPassword).raw(),
316 Utf8Str(aDomain).raw(), u32Flags);
317 return S_OK;
318 }
319
320 return setError(VBOX_E_VM_ERROR,
321 tr("VMM device is not available (is the VM running?)"));
322}
323
324#ifdef VBOX_WITH_GUEST_CONTROL
325/**
326 * Creates the argument list as an array used for executing a program.
327 *
328 * @returns VBox status code.
329 *
330 * @todo
331 *
332 * @todo Respect spaces when quoting for arguments, e.g. "c:\\program files\\".
333 * @todo Handle empty ("") argguments.
334 */
335int Guest::prepareExecuteArgs(const char *pszArgs, void **ppvList, uint32_t *pcbList, uint32_t *pcArgs)
336{
337 char **ppaArg;
338 int iArgs;
339 int rc = RTGetOptArgvFromString(&ppaArg, &iArgs, pszArgs, NULL);
340 if (RT_SUCCESS(rc))
341 {
342 char *pszTemp = NULL;
343 *pcbList = 0;
344 for (int i=0; i<iArgs; i++)
345 {
346 if (i > 0) /* Insert space as delimiter. */
347 rc = RTStrAAppendN(&pszTemp, " ", 1);
348
349 if (RT_FAILURE(rc))
350 break;
351 else
352 {
353 rc = RTStrAAppendN(&pszTemp, ppaArg[i], strlen(ppaArg[i]));
354 if (RT_FAILURE(rc))
355 break;
356 }
357 }
358 RTGetOptArgvFree(ppaArg);
359 if (RT_SUCCESS(rc))
360 {
361 *ppvList = pszTemp;
362 *pcArgs = iArgs;
363 if (pszTemp)
364 *pcbList = strlen(pszTemp) + 1; /* Include zero termination. */
365 }
366 else
367 RTStrFree(pszTemp);
368 }
369 return rc;
370}
371
372/**
373 * Appends environment variables to the environment block. Each var=value pair is separated
374 * by NULL (\0) sequence. The whole block will be stored in one blob and disassembled on the
375 * guest side later to fit into the HGCM param structure.
376 *
377 * @returns VBox status code.
378 *
379 * @todo
380 *
381 */
382int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv)
383{
384 int rc = VINF_SUCCESS;
385 uint32_t cbLen = strlen(pszEnv);
386 if (*ppvList)
387 {
388 uint32_t cbNewLen = *pcbList + cbLen + 1; /* Include zero termination. */
389 char *pvTmp = (char*)RTMemRealloc(*ppvList, cbNewLen);
390 if (NULL == pvTmp)
391 {
392 rc = VERR_NO_MEMORY;
393 }
394 else
395 {
396 memcpy(pvTmp + *pcbList, pszEnv, cbLen);
397 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
398 *ppvList = (void**)pvTmp;
399 }
400 }
401 else
402 {
403 char *pcTmp;
404 if (RTStrAPrintf(&pcTmp, "%s", pszEnv) > 0)
405 {
406 *ppvList = (void**)pcTmp;
407 /* Reset counters. */
408 *pcEnv = 0;
409 *pcbList = 0;
410 }
411 }
412 if (RT_SUCCESS(rc))
413 {
414 *pcbList += cbLen + 1; /* Include zero termination. */
415 *pcEnv += 1; /* Increase env pairs count. */
416 }
417 return rc;
418}
419#endif /* VBOX_WITH_GUEST_CONTROL */
420
421STDMETHODIMP Guest::ExecuteProgram(IN_BSTR aCommand, ULONG aFlags,
422 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
423 IN_BSTR aStdIn, IN_BSTR aStdOut, IN_BSTR aStdErr,
424 IN_BSTR aUserName, IN_BSTR aPassword,
425 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
426{
427#ifndef VBOX_WITH_GUEST_CONTROL
428 ReturnComNotImplemented();
429#else /* VBOX_WITH_GUEST_CONTROL */
430 using namespace guestControl;
431
432 CheckComArgStrNotEmptyOrNull(aCommand);
433 CheckComArgOutPointerValid(aProgress);
434 CheckComArgOutPointerValid(aPID);
435 /* Flags are not supported at the moment. */
436 if (aFlags != 0)
437 return E_INVALIDARG;
438
439 HRESULT rc = S_OK;
440
441 try
442 {
443 AutoCaller autoCaller(this);
444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
445
446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
447
448 /* Just be on the safe side when calling another process. */
449 alock.leave();
450
451 HRESULT rc = E_UNEXPECTED;
452 using namespace guestControl;
453
454 int vrc = VINF_SUCCESS;
455 Utf8Str Utf8Command(aCommand);
456
457 /* Prepare arguments. */
458 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
459 uint32_t uNumArgs = args.size();
460 char **papszArgv = NULL;
461 if(uNumArgs > 0)
462 {
463 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
464 AssertPtr(papszArgv);
465 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
466 vrc = RTStrAPrintf(&papszArgv[i], "%s", Utf8Str(args[i]).raw());
467 papszArgv[uNumArgs] = NULL;
468 }
469
470 if (RT_SUCCESS(vrc))
471 {
472 char *pszArgs = NULL;
473 if (uNumArgs > 0)
474 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
475 if (RT_SUCCESS(vrc))
476 {
477 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
478
479 /* Prepare environment. */
480 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
481
482 void *pvEnv = NULL;
483 uint32_t uNumEnv = env.size();
484 uint32_t cbEnv = 0;
485
486 for (unsigned i = 0; i < uNumEnv; i++)
487 {
488 vrc = prepareExecuteEnv(Utf8Str(env[i]).raw(), &pvEnv, &cbEnv, &uNumEnv);
489 if (RT_FAILURE(vrc))
490 break;
491 }
492
493 if (RT_SUCCESS(vrc))
494 {
495 Utf8Str Utf8StdIn(aStdIn);
496 Utf8Str Utf8StdOut(aStdOut);
497 Utf8Str Utf8StdErr(aStdErr);
498 Utf8Str Utf8UserName(aUserName);
499 Utf8Str Utf8Password(aPassword);
500
501 VBOXHGCMSVCPARM paParms[13];
502 int i = 0;
503 paParms[i++].setPointer((void*)Utf8Command.raw(), (uint32_t)strlen(Utf8Command.raw()) + 1);
504 paParms[i++].setUInt32(aFlags);
505 paParms[i++].setUInt32(uNumArgs);
506 paParms[i++].setPointer((void*)pszArgs, cbArgs);
507 paParms[i++].setUInt32(uNumEnv);
508 paParms[i++].setPointer((void*)pvEnv, cbEnv);
509 paParms[i++].setPointer((void*)Utf8StdIn.raw(), (uint32_t)strlen(Utf8StdIn.raw()) + 1);
510 paParms[i++].setPointer((void*)Utf8StdOut.raw(), (uint32_t)strlen(Utf8StdOut.raw()) + 1);
511 paParms[i++].setPointer((void*)Utf8StdErr.raw(), (uint32_t)strlen(Utf8StdErr.raw()) + 1);
512 paParms[i++].setPointer((void*)Utf8UserName.raw(), (uint32_t)strlen(Utf8UserName.raw()) + 1);
513 paParms[i++].setPointer((void*)Utf8Password.raw(), (uint32_t)strlen(Utf8Password.raw()) + 1);
514 paParms[i++].setUInt32(aTimeoutMS);
515
516 /* Forward the information to the VMM device. */
517 AssertPtr(mParent);
518 VMMDev *vmmDev = mParent->getVMMDev();
519 if (vmmDev)
520 {
521 LogFlow(("Guest::ExecuteProgram: numParms=%d\n", i));
522 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
523 i, paParms);
524 /** @todo Get the PID. */
525 }
526 RTMemFree(pvEnv);
527 }
528 RTStrFree(pszArgs);
529 }
530 if (RT_SUCCESS(vrc))
531 rc = S_OK;
532 else
533 rc = setError(E_UNEXPECTED,
534 tr("The service call failed with the error %Rrc"),
535 vrc);
536
537 for (unsigned i = 0; i < uNumArgs; i++)
538 RTMemFree(papszArgv[i]);
539 RTMemFree(papszArgv);
540 }
541 }
542 catch (std::bad_alloc &)
543 {
544 rc = E_OUTOFMEMORY;
545 };
546
547 return rc;
548#endif /* VBOX_WITH_GUEST_CONTROL */
549}
550
551// public methods only for internal purposes
552/////////////////////////////////////////////////////////////////////////////
553
554void Guest::setAdditionsVersion(Bstr aVersion, VBOXOSTYPE aOsType)
555{
556 AutoCaller autoCaller(this);
557 AssertComRCReturnVoid (autoCaller.rc());
558
559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
560
561 mData.mAdditionsVersion = aVersion;
562 mData.mAdditionsActive = !aVersion.isEmpty();
563 /* Older Additions didn't have this finer grained capability bit,
564 * so enable it by default. Newer Additions will disable it immediately
565 * if relevant. */
566 mData.mSupportsGraphics = mData.mAdditionsActive;
567
568 mData.mOSTypeId = Global::OSTypeId (aOsType);
569}
570
571void Guest::setSupportsSeamless (BOOL aSupportsSeamless)
572{
573 AutoCaller autoCaller(this);
574 AssertComRCReturnVoid (autoCaller.rc());
575
576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
577
578 mData.mSupportsSeamless = aSupportsSeamless;
579}
580
581void Guest::setSupportsGraphics (BOOL aSupportsGraphics)
582{
583 AutoCaller autoCaller(this);
584 AssertComRCReturnVoid (autoCaller.rc());
585
586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
587
588 mData.mSupportsGraphics = aSupportsGraphics;
589}
590/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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