VirtualBox

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

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

Main/GuestImpl: Post Guest Additions install status on automatic update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 104.0 KB
Line 
1/* $Id: GuestImpl.cpp 34155 2010-11-18 08:48:28Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.215389.xyz. 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
20#include "GuestImpl.h"
21
22#include "Global.h"
23#include "ConsoleImpl.h"
24#include "ProgressImpl.h"
25#include "VMMDev.h"
26
27#include "AutoCaller.h"
28#include "Logging.h"
29
30#include <VBox/VMMDev.h>
31#ifdef VBOX_WITH_GUEST_CONTROL
32# include <VBox/com/array.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/pgm.h>
41
42#include <memory>
43
44// defines
45/////////////////////////////////////////////////////////////////////////////
46
47// constructor / destructor
48/////////////////////////////////////////////////////////////////////////////
49
50DEFINE_EMPTY_CTOR_DTOR (Guest)
51
52struct Guest::TaskGuest
53{
54 enum TaskType
55 {
56 /** Update Guest Additions by directly copying the required installer
57 * off the .ISO file, transfer it to the guest and execute the installer
58 * with system priviledges. */
59 UpdateGuestAdditions = 100
60 };
61
62 TaskGuest(TaskType aTaskType, Guest *aThat, Progress *aProgress)
63 : taskType(aTaskType),
64 pGuest(aThat),
65 progress(aProgress),
66 rc(S_OK)
67 {}
68 ~TaskGuest() {}
69
70 int startThread();
71 static int taskThread(RTTHREAD aThread, void *pvUser);
72 static int uploadProgress(unsigned uPercent, void *pvUser);
73
74 TaskType taskType;
75 Guest *pGuest;
76 ComObjPtr<Progress> progress;
77 HRESULT rc;
78
79 /* Task data. */
80 Utf8Str strSource;
81};
82
83int Guest::TaskGuest::startThread()
84{
85 int vrc = RTThreadCreate(NULL, Guest::TaskGuest::taskThread, this,
86 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
87 "Guest::Task");
88
89 if (RT_FAILURE(vrc))
90 return Guest::setErrorStatic(E_FAIL, Utf8StrFmt("Could not create taskThreadGuest (%Rrc)\n", vrc));
91
92 return vrc;
93}
94
95/* static */
96DECLCALLBACK(int) Guest::TaskGuest::taskThread(RTTHREAD /* aThread */, void *pvUser)
97{
98 std::auto_ptr<TaskGuest> task(static_cast<TaskGuest*>(pvUser));
99 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
100
101 Guest *pGuest = task->pGuest;
102
103 LogFlowFuncEnter();
104 LogFlowFunc(("Guest %p\n", pGuest));
105
106 HRESULT rc = S_OK;
107
108 switch (task->taskType)
109 {
110#ifdef VBOX_WITH_GUEST_CONTROL
111 case TaskGuest::UpdateGuestAdditions:
112 {
113 rc = pGuest->taskUpdateGuestAdditions(task.get());
114 break;
115 }
116#endif
117 default:
118 AssertMsgFailed(("Invalid task type %u specified!\n", task->taskType));
119 break;
120 }
121
122 LogFlowFunc(("rc=%Rhrc\n", rc));
123 LogFlowFuncLeave();
124
125 return VINF_SUCCESS;
126}
127
128/* static */
129int Guest::TaskGuest::uploadProgress(unsigned uPercent, void *pvUser)
130{
131 Guest::TaskGuest *pTask = *(Guest::TaskGuest**)pvUser;
132
133 if (pTask &&
134 !pTask->progress.isNull())
135 {
136 BOOL fCanceled;
137 pTask->progress->COMGETTER(Canceled)(&fCanceled);
138 if (fCanceled)
139 return -1;
140 pTask->progress->SetCurrentOperationProgress(uPercent);
141 }
142 return VINF_SUCCESS;
143}
144
145#ifdef VBOX_WITH_GUEST_CONTROL
146HRESULT Guest::taskUpdateGuestAdditions(TaskGuest *aTask)
147{
148 LogFlowFuncEnter();
149
150 AutoCaller autoCaller(this);
151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
152
153 /*
154 * Do *not* take a write lock here since we don't (and won't)
155 * touch any class-specific data (of IGuest) here - only the member functions
156 * which get called here can do that.
157 */
158
159 HRESULT rc = S_OK;
160
161 try
162 {
163 Guest *pGuest = aTask->pGuest;
164 AssertPtr(pGuest);
165
166 if (aTask->progress)
167 aTask->progress->SetCurrentOperationProgress(10);
168
169 bool fIsWindows = false;
170 Utf8Str osType = pGuest->mData.mOSTypeId;
171 if ( osType.contains("Microsoft", Utf8Str::CaseInsensitive)
172 || osType.contains("Windows", Utf8Str::CaseInsensitive))
173 {
174 fIsWindows = true; /* We have a Windows guest. */
175 }
176 else /* Everything else is not supported (yet). */
177 throw setError(VBOX_E_NOT_SUPPORTED, tr("Guest OS not supported for automatic Guest Additions updating"));
178
179 /*
180 * Determine guest type to know which installer stuff
181 * we need. At the moment only Windows guests are supported.
182 */
183 Utf8Str installerImage;
184 if (fIsWindows)
185 {
186 if (osType.contains("64", Utf8Str::CaseInsensitive))
187 installerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
188 else
189 installerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
190 /* Since the installers are located in the root directory,
191 * no further path processing needs to be done (yet). */
192 }
193 Assert(!installerImage.isEmpty());
194
195 /*
196 * Try to open the .ISO file and locate the specified installer.
197 */
198 RTISOFSFILE iso;
199 int vrc = RTIsoFsOpen(&iso, aTask->strSource.c_str());
200 if (RT_FAILURE(vrc))
201 {
202 rc = setError(VBOX_E_FILE_ERROR, tr("Invalid installation medium detected"));
203 }
204 else
205 {
206 uint32_t cbOffset;
207 size_t cbLength;
208 vrc = RTIsoFsGetFileInfo(&iso, installerImage.c_str(), &cbOffset, &cbLength);
209 if ( RT_SUCCESS(vrc)
210 && cbOffset
211 && cbLength)
212 {
213 vrc = RTFileSeek(iso.file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
214 }
215
216 /* Specify the ouput path on the guest side. */
217 Utf8Str strInstallerPath = "%TEMP%\\VBoxWindowsAdditions.exe";
218
219 if (RT_FAILURE(vrc))
220 {
221 switch (vrc)
222 {
223 case VERR_FILE_NOT_FOUND:
224 rc = setError(VBOX_E_IPRT_ERROR, tr("Setup file was not found on installation medium"));
225 break;
226
227 default:
228 rc = setError(VBOX_E_IPRT_ERROR, tr("An unknown error occured (%Rrc)"), vrc);
229 break;
230 }
231 }
232 else
233 {
234 /* Okay, we're ready to start our copy routine on the guest! */
235 if (aTask->progress)
236 aTask->progress->SetCurrentOperationProgress(15);
237
238 LogRel(("Automatic update of Guest Additions started\n"));
239
240 /* Prepare command line args. */
241 com::SafeArray<IN_BSTR> args;
242 com::SafeArray<IN_BSTR> env;
243
244 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */
245 args.push_back(Bstr("--output").raw()); /* We want to write a file ... */
246 args.push_back(Bstr(strInstallerPath.c_str()).raw()); /* ... with this path. */
247
248 if (SUCCEEDED(rc))
249 {
250 ComPtr<IProgress> progressCopy;
251 ULONG uPID;
252
253 /*
254 * Start built-in "vbox_cat" tool (inside VBoxService) to
255 * copy over/pipe the data into a file on the guest (with
256 * system rights, no username/password specified).
257 */
258 rc = pGuest->executeProcessInternal(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
259 ExecuteProcessFlag_WaitForProcessStartOnly,
260 ComSafeArrayAsInParam(args),
261 ComSafeArrayAsInParam(env),
262 Bstr("").raw() /* Username. */,
263 Bstr("").raw() /* Password */,
264 5 * 1000 /* Wait 5s for getting the process started. */,
265 &uPID, progressCopy.asOutParam(), &vrc);
266 if (RT_FAILURE(vrc))
267 {
268 switch (vrc)
269 {
270 /* If we got back VERR_INVALID_PARAMETER we're running an old(er) Guest Additions version
271 * (< 4.0) which does not support automatic updating and/or has not the internal tool "vbox_cat". */
272 case VERR_INVALID_PARAMETER:
273 rc = setError(VBOX_E_NOT_SUPPORTED,
274 tr("Currently installed Guest Additions don't support automatic updating, please update them manually"));
275 break;
276 /* Getting back a VERR_TIMEOUT basically means that either VBoxService on the guest does not run (anymore) or that
277 * no Guest Additions (on a supported automatic updating OS) are installed at all. */
278 case VERR_TIMEOUT:
279 rc = setError(VBOX_E_NOT_SUPPORTED,
280 tr("Guest Additions seem not to be installed on the guest or are not responding, please update them manually"));
281 break;
282
283 default:
284 break;
285 }
286 }
287 else
288 {
289 LogRel(("Copying Guest Additions installer to guest ...\n"));
290
291 if (aTask->progress)
292 aTask->progress->SetCurrentOperationProgress(20);
293
294 /* Wait for process to exit ... */
295 BOOL fCompleted = FALSE;
296 BOOL fCanceled = FALSE;
297
298 size_t cbRead;
299 size_t cbToRead;
300 SafeArray<BYTE> aInputData(_64K);
301 while ( SUCCEEDED(progressCopy->COMGETTER(Completed(&fCompleted)))
302 && !fCompleted)
303 {
304 /* cbLength contains remaining bytes of our installer file
305 * opened above to read. */
306 cbToRead = RT_MIN(cbLength, _64K);
307 if (cbToRead)
308 {
309 vrc = RTFileRead(iso.file, (uint8_t*)aInputData.raw(), cbToRead, &cbRead);
310 if ( cbRead
311 && RT_SUCCESS(vrc))
312 {
313 /* Did we reach the end of the content
314 * we want to transfer (last chunk)? */
315 ULONG uFlags = ProcessInputFlag_None;
316 if (cbRead < _64K)
317 {
318 uFlags |= ProcessInputFlag_EndOfFile;
319 if (cbRead > 0) /* Don't allow an empty array! */
320 aInputData.resize(cbRead); /* Adjust array size (less than 64K read). */
321 }
322
323 /* Transfer the current chunk ... */
324 ULONG uBytesWritten;
325 rc = SetProcessInput(uPID, uFlags,
326 ComSafeArrayAsInParam(aInputData), &uBytesWritten);
327 if (FAILED(rc))
328 break;
329
330 Assert(cbLength >= uBytesWritten);
331 cbLength -= uBytesWritten;
332
333 /* Progress canceled by Main API? */
334 if ( SUCCEEDED(progressCopy->COMGETTER(Canceled(&fCanceled)))
335 && fCanceled)
336 {
337 break;
338 }
339 if (aTask->progress)
340 {
341 if (SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
342 && fCanceled)
343 break;
344 }
345 }
346 }
347 }
348 }
349 }
350 }
351 RTIsoFsClose(&iso);
352
353 if (SUCCEEDED(rc))
354 {
355 /*
356 * Installer was transferred successfully, so let's start it
357 * (with system rights).
358 */
359 if (aTask->progress)
360 aTask->progress->SetCurrentOperationProgress(66);
361
362 /* Prepare command line args for installer. */
363 com::SafeArray<IN_BSTR> installerArgs;
364 com::SafeArray<IN_BSTR> installerEnv;
365
366 /** @todo Only Windows! */
367 installerArgs.push_back(Bstr(strInstallerPath).raw()); /* The actual (internal) installer image (as argv[0]). */
368 /* Note that starting at Windows Vista the lovely session 0 separation applies:
369 * This means that if we run an application with the profile/security context
370 * of VBoxService (system rights!) we're not able to show any UI. */
371 installerArgs.push_back(Bstr("/S").raw()); /* We want to install in silent mode. */
372 installerArgs.push_back(Bstr("/l").raw()); /* ... and logging enabled. */
373 /* Don't quit VBoxService during upgrade because it still is used for this
374 * piece of code we're in right now (that is, here!) ... */
375 installerArgs.push_back(Bstr("/no_vboxservice_exit").raw());
376 /* Tell the installer to report its current installation status
377 * using a running VBoxTray instance via balloon messages in the
378 * Windows taskbar. */
379 installerArgs.push_back(Bstr("/post_installstatus").raw());
380
381 /*
382 * Start the just copied over installer with system rights
383 * in silent mode on the guest.
384 */
385 ComPtr<IProgress> progressInstaller;
386 ULONG uPID;
387 rc = pGuest->executeProcessInternal(Bstr(strInstallerPath).raw(),
388 ExecuteProcessFlag_WaitForProcessStartOnly,
389 ComSafeArrayAsInParam(installerArgs),
390 ComSafeArrayAsInParam(installerEnv),
391 Bstr("").raw() /* Username */,
392 Bstr("").raw() /* Password */,
393 5 * 1000 /* Wait 5s for getting the process started */,
394 &uPID, progressInstaller.asOutParam(), &vrc);
395 if (SUCCEEDED(rc))
396 {
397 LogRel(("Executing Guest Additions update ...\n"));
398 }
399 }
400 }
401 }
402 catch(HRESULT aRC)
403 {
404 rc = aRC;
405 }
406
407 /* Clean up */
408 if (aTask->progress)
409 aTask->progress->SetCurrentOperationProgress(99);
410
411 /* Assign data. */
412 if (rc == S_OK)
413 {
414 }
415
416 aTask->rc = rc;
417
418 if (!aTask->progress.isNull())
419 aTask->progress->notifyComplete(rc);
420
421 LogFlowFunc(("rc=%Rhrc\n", rc));
422 LogFlowFuncLeave();
423
424 return VINF_SUCCESS;
425}
426#endif
427
428HRESULT Guest::FinalConstruct()
429{
430 return S_OK;
431}
432
433void Guest::FinalRelease()
434{
435 uninit ();
436}
437
438// public methods only for internal purposes
439/////////////////////////////////////////////////////////////////////////////
440
441/**
442 * Initializes the guest object.
443 */
444HRESULT Guest::init(Console *aParent)
445{
446 LogFlowThisFunc(("aParent=%p\n", aParent));
447
448 ComAssertRet(aParent, E_INVALIDARG);
449
450 /* Enclose the state transition NotReady->InInit->Ready */
451 AutoInitSpan autoInitSpan(this);
452 AssertReturn(autoInitSpan.isOk(), E_FAIL);
453
454 unconst(mParent) = aParent;
455
456 /* Confirm a successful initialization when it's the case */
457 autoInitSpan.setSucceeded();
458
459 ULONG aMemoryBalloonSize;
460 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
461 if (ret == S_OK)
462 mMemoryBalloonSize = aMemoryBalloonSize;
463 else
464 mMemoryBalloonSize = 0; /* Default is no ballooning */
465
466 BOOL fPageFusionEnabled;
467 ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
468 if (ret == S_OK)
469 mfPageFusionEnabled = fPageFusionEnabled;
470 else
471 mfPageFusionEnabled = false; /* Default is no page fusion*/
472
473 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
474
475 /* Clear statistics. */
476 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
477 mCurrentGuestStat[i] = 0;
478
479#ifdef VBOX_WITH_GUEST_CONTROL
480 /* Init the context ID counter at 1000. */
481 mNextContextID = 1000;
482#endif
483
484 return S_OK;
485}
486
487/**
488 * Uninitializes the instance and sets the ready flag to FALSE.
489 * Called either from FinalRelease() or by the parent when it gets destroyed.
490 */
491void Guest::uninit()
492{
493 LogFlowThisFunc(("\n"));
494
495#ifdef VBOX_WITH_GUEST_CONTROL
496 /* Scope write lock as much as possible. */
497 {
498 /*
499 * Cleanup must be done *before* AutoUninitSpan to cancel all
500 * all outstanding waits in API functions (which hold AutoCaller
501 * ref counts).
502 */
503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
504
505 /* Clean up callback data. */
506 CallbackMapIter it;
507 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
508 destroyCtrlCallbackContext(it);
509
510 /* Clear process map. */
511 mGuestProcessMap.clear();
512 }
513#endif
514
515 /* Enclose the state transition Ready->InUninit->NotReady */
516 AutoUninitSpan autoUninitSpan(this);
517 if (autoUninitSpan.uninitDone())
518 return;
519
520 unconst(mParent) = NULL;
521}
522
523// IGuest properties
524/////////////////////////////////////////////////////////////////////////////
525
526STDMETHODIMP Guest::COMGETTER(OSTypeId) (BSTR *aOSTypeId)
527{
528 CheckComArgOutPointerValid(aOSTypeId);
529
530 AutoCaller autoCaller(this);
531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
532
533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
534
535 // redirect the call to IMachine if no additions are installed
536 if (mData.mAdditionsVersion.isEmpty())
537 return mParent->machine()->COMGETTER(OSTypeId)(aOSTypeId);
538
539 mData.mOSTypeId.cloneTo(aOSTypeId);
540
541 return S_OK;
542}
543
544STDMETHODIMP Guest::COMGETTER(AdditionsRunLevel) (AdditionsRunLevelType_T *aRunLevel)
545{
546 AutoCaller autoCaller(this);
547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
548
549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
550
551 *aRunLevel = mData.mAdditionsRunLevel;
552
553 return S_OK;
554}
555
556STDMETHODIMP Guest::COMGETTER(AdditionsVersion) (BSTR *aAdditionsVersion)
557{
558 CheckComArgOutPointerValid(aAdditionsVersion);
559
560 AutoCaller autoCaller(this);
561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
562
563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
564
565 HRESULT hr = S_OK;
566 if ( mData.mAdditionsVersion.isEmpty()
567 /* Only try alternative way if GA are active! */
568 && mData.mAdditionsRunLevel > AdditionsRunLevelType_None)
569 {
570 /*
571 * If we got back an empty string from GetAdditionsVersion() we either
572 * really don't have the Guest Additions version yet or the guest is running
573 * older Guest Additions (< 3.2.0) which don't provide VMMDevReq_ReportGuestInfo2,
574 * so get the version + revision from the (hopefully) provided guest properties
575 * instead.
576 */
577 Bstr addVersion;
578 LONG64 u64Timestamp;
579 Bstr flags;
580 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Version").raw(),
581 addVersion.asOutParam(), &u64Timestamp, flags.asOutParam());
582 if (hr == S_OK)
583 {
584 Bstr addRevision;
585 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Revision").raw(),
586 addRevision.asOutParam(), &u64Timestamp, flags.asOutParam());
587 if ( hr == S_OK
588 && !addVersion.isEmpty()
589 && !addRevision.isEmpty())
590 {
591 /* Some Guest Additions versions had interchanged version + revision values,
592 * so check if the version value at least has a dot to identify it and change
593 * both values to reflect the right content. */
594 if (!Utf8Str(addVersion).contains("."))
595 {
596 Bstr addTemp = addVersion;
597 addVersion = addRevision;
598 addRevision = addTemp;
599 }
600
601 Bstr additionsVersion = BstrFmt("%ls r%ls",
602 addVersion.raw(), addRevision.raw());
603 additionsVersion.cloneTo(aAdditionsVersion);
604 }
605 /** @todo r=bird: else: Should not return failure! */
606 }
607 else
608 {
609 /* If getting the version + revision above fails or they simply aren't there
610 * because of *really* old Guest Additions we only can report the interface
611 * version to at least have something. */
612 mData.mInterfaceVersion.cloneTo(aAdditionsVersion);
613 /** @todo r=bird: hr is still indicating failure! */
614 }
615 }
616 else
617 mData.mAdditionsVersion.cloneTo(aAdditionsVersion);
618
619 return hr;
620}
621
622STDMETHODIMP Guest::COMGETTER(SupportsSeamless) (BOOL *aSupportsSeamless)
623{
624 CheckComArgOutPointerValid(aSupportsSeamless);
625
626 AutoCaller autoCaller(this);
627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
628
629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
630
631 *aSupportsSeamless = mData.mSupportsSeamless;
632
633 return S_OK;
634}
635
636STDMETHODIMP Guest::COMGETTER(SupportsGraphics) (BOOL *aSupportsGraphics)
637{
638 CheckComArgOutPointerValid(aSupportsGraphics);
639
640 AutoCaller autoCaller(this);
641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
642
643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
644
645 *aSupportsGraphics = mData.mSupportsGraphics;
646
647 return S_OK;
648}
649
650BOOL Guest::isPageFusionEnabled()
651{
652 AutoCaller autoCaller(this);
653 if (FAILED(autoCaller.rc())) return false;
654
655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
656
657 return mfPageFusionEnabled;
658}
659
660STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize) (ULONG *aMemoryBalloonSize)
661{
662 CheckComArgOutPointerValid(aMemoryBalloonSize);
663
664 AutoCaller autoCaller(this);
665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
666
667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
668
669 *aMemoryBalloonSize = mMemoryBalloonSize;
670
671 return S_OK;
672}
673
674STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize) (ULONG aMemoryBalloonSize)
675{
676 AutoCaller autoCaller(this);
677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
678
679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
680
681 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)
682 * does not call us back in any way! */
683 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
684 if (ret == S_OK)
685 {
686 mMemoryBalloonSize = aMemoryBalloonSize;
687 /* forward the information to the VMM device */
688 VMMDev *pVMMDev = mParent->getVMMDev();
689 /* MUST release all locks before calling VMM device as its critsect
690 * has higher lock order than anything in Main. */
691 alock.release();
692 if (pVMMDev)
693 {
694 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
695 if (pVMMDevPort)
696 pVMMDevPort->pfnSetMemoryBalloon(pVMMDevPort, aMemoryBalloonSize);
697 }
698 }
699
700 return ret;
701}
702
703STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)
704{
705 CheckComArgOutPointerValid(aUpdateInterval);
706
707 AutoCaller autoCaller(this);
708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
709
710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
711
712 *aUpdateInterval = mStatUpdateInterval;
713 return S_OK;
714}
715
716STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)
717{
718 AutoCaller autoCaller(this);
719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
720
721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
722
723 mStatUpdateInterval = aUpdateInterval;
724 /* forward the information to the VMM device */
725 VMMDev *pVMMDev = mParent->getVMMDev();
726 /* MUST release all locks before calling VMM device as its critsect
727 * has higher lock order than anything in Main. */
728 alock.release();
729 if (pVMMDev)
730 {
731 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
732 if (pVMMDevPort)
733 pVMMDevPort->pfnSetStatisticsInterval(pVMMDevPort, aUpdateInterval);
734 }
735
736 return S_OK;
737}
738
739STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
740 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemShared,
741 ULONG *aMemCache, ULONG *aPageTotal,
742 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal, ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)
743{
744 CheckComArgOutPointerValid(aCpuUser);
745 CheckComArgOutPointerValid(aCpuKernel);
746 CheckComArgOutPointerValid(aCpuIdle);
747 CheckComArgOutPointerValid(aMemTotal);
748 CheckComArgOutPointerValid(aMemFree);
749 CheckComArgOutPointerValid(aMemBalloon);
750 CheckComArgOutPointerValid(aMemShared);
751 CheckComArgOutPointerValid(aMemCache);
752 CheckComArgOutPointerValid(aPageTotal);
753 CheckComArgOutPointerValid(aMemAllocTotal);
754 CheckComArgOutPointerValid(aMemFreeTotal);
755 CheckComArgOutPointerValid(aMemBalloonTotal);
756 CheckComArgOutPointerValid(aMemSharedTotal);
757
758 AutoCaller autoCaller(this);
759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
760
761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
762
763 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
764 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
765 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
766 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
767 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */
768 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */
769 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
770 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
771
772 /* MUST release all locks before calling any PGM statistics queries,
773 * as they are executed by EMT and that might deadlock us by VMM device
774 * activity which waits for the Guest object lock. */
775 alock.release();
776 Console::SafeVMPtr pVM (mParent);
777 if (pVM.isOk())
778 {
779 uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;
780 *aMemFreeTotal = 0;
781 int rc = PGMR3QueryVMMMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);
782 AssertRC(rc);
783 if (rc == VINF_SUCCESS)
784 {
785 *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */
786 *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);
787 *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);
788 *aMemSharedTotal = (ULONG)(uSharedTotal / _1K);
789 }
790
791 /* Query the missing per-VM memory statistics. */
792 *aMemShared = 0;
793 uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;
794 rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);
795 if (rc == VINF_SUCCESS)
796 {
797 *aMemShared = (ULONG)(uSharedMem / _1K);
798 }
799 }
800 else
801 {
802 *aMemFreeTotal = 0;
803 *aMemShared = 0;
804 }
805
806 return S_OK;
807}
808
809HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
810{
811 AutoCaller autoCaller(this);
812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
813
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 if (enmType >= GUESTSTATTYPE_MAX)
817 return E_INVALIDARG;
818
819 mCurrentGuestStat[enmType] = aVal;
820 return S_OK;
821}
822
823STDMETHODIMP Guest::GetAdditionsStatus(AdditionsRunLevelType_T aLevel, BOOL *aActive)
824{
825 AutoCaller autoCaller(this);
826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
827
828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
829
830 HRESULT rc = S_OK;
831 switch (aLevel)
832 {
833 case AdditionsRunLevelType_System:
834 *aActive = (mData.mAdditionsRunLevel > AdditionsRunLevelType_None);
835 break;
836
837 case AdditionsRunLevelType_Userland:
838 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Userland);
839 break;
840
841 case AdditionsRunLevelType_Desktop:
842 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Desktop);
843 break;
844
845 default:
846 rc = setError(VBOX_E_NOT_SUPPORTED,
847 tr("Invalid status level defined: %u"), aLevel);
848 break;
849 }
850
851 return rc;
852}
853
854STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,
855 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)
856{
857 AutoCaller autoCaller(this);
858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
859
860 /* forward the information to the VMM device */
861 VMMDev *pVMMDev = mParent->getVMMDev();
862 if (pVMMDev)
863 {
864 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
865 if (pVMMDevPort)
866 {
867 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
868 if (!aAllowInteractiveLogon)
869 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
870
871 pVMMDevPort->pfnSetCredentials(pVMMDevPort,
872 Utf8Str(aUserName).c_str(),
873 Utf8Str(aPassword).c_str(),
874 Utf8Str(aDomain).c_str(),
875 u32Flags);
876 return S_OK;
877 }
878 }
879
880 return setError(VBOX_E_VM_ERROR,
881 tr("VMM device is not available (is the VM running?)"));
882}
883
884#ifdef VBOX_WITH_GUEST_CONTROL
885/**
886 * Appends environment variables to the environment block.
887 *
888 * Each var=value pair is separated by the null character ('\\0'). The whole
889 * block will be stored in one blob and disassembled on the guest side later to
890 * fit into the HGCM param structure.
891 *
892 * @returns VBox status code.
893 *
894 * @param pszEnvVar The environment variable=value to append to the
895 * environment block.
896 * @param ppvList This is actually a pointer to a char pointer
897 * variable which keeps track of the environment block
898 * that we're constructing.
899 * @param pcbList Pointer to the variable holding the current size of
900 * the environment block. (List is a misnomer, go
901 * ahead a be confused.)
902 * @param pcEnvVars Pointer to the variable holding count of variables
903 * stored in the environment block.
904 */
905int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)
906{
907 int rc = VINF_SUCCESS;
908 uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
909 if (*ppvList)
910 {
911 uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
912 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
913 if (pvTmp == NULL)
914 rc = VERR_NO_MEMORY;
915 else
916 {
917 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
918 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
919 *ppvList = (void **)pvTmp;
920 }
921 }
922 else
923 {
924 char *pszTmp;
925 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
926 {
927 *ppvList = (void **)pszTmp;
928 /* Reset counters. */
929 *pcEnvVars = 0;
930 *pcbList = 0;
931 }
932 }
933 if (RT_SUCCESS(rc))
934 {
935 *pcbList += cchEnv + 1; /* Include zero termination. */
936 *pcEnvVars += 1; /* Increase env variable count. */
937 }
938 return rc;
939}
940
941/**
942 * Static callback function for receiving updates on guest control commands
943 * from the guest. Acts as a dispatcher for the actual class instance.
944 *
945 * @returns VBox status code.
946 *
947 * @todo
948 *
949 */
950DECLCALLBACK(int) Guest::doGuestCtrlNotification(void *pvExtension,
951 uint32_t u32Function,
952 void *pvParms,
953 uint32_t cbParms)
954{
955 using namespace guestControl;
956
957 /*
958 * No locking, as this is purely a notification which does not make any
959 * changes to the object state.
960 */
961#ifdef DEBUG_andy
962 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
963 pvExtension, u32Function, pvParms, cbParms));
964#endif
965 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
966
967 int rc = VINF_SUCCESS;
968 switch (u32Function)
969 {
970 case GUEST_DISCONNECTED:
971 {
972 //LogFlowFunc(("GUEST_DISCONNECTED\n"));
973
974 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
975 AssertPtr(pCBData);
976 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);
977 AssertReturn(CALLBACKDATAMAGICCLIENTDISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
978
979 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);
980 break;
981 }
982
983 case GUEST_EXEC_SEND_STATUS:
984 {
985 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
986
987 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
988 AssertPtr(pCBData);
989 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
990 AssertReturn(CALLBACKDATAMAGICEXECSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
991
992 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);
993 break;
994 }
995
996 case GUEST_EXEC_SEND_OUTPUT:
997 {
998 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
999
1000 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);
1001 AssertPtr(pCBData);
1002 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);
1003 AssertReturn(CALLBACKDATAMAGICEXECOUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1004
1005 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
1006 break;
1007 }
1008
1009 case GUEST_EXEC_SEND_INPUT_STATUS:
1010 {
1011 //LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));
1012
1013 PCALLBACKDATAEXECINSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvParms);
1014 AssertPtr(pCBData);
1015 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbParms, VERR_INVALID_PARAMETER);
1016 AssertReturn(CALLBACKDATAMAGICEXECINSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1017
1018 rc = pGuest->notifyCtrlExecInStatus(u32Function, pCBData);
1019 break;
1020 }
1021
1022 default:
1023 AssertMsgFailed(("Unknown guest control notification received, u32Function=%u", u32Function));
1024 rc = VERR_INVALID_PARAMETER;
1025 break;
1026 }
1027 return rc;
1028}
1029
1030/* Function for handling the execution start/termination notification. */
1031int Guest::notifyCtrlExecStatus(uint32_t u32Function,
1032 PCALLBACKDATAEXECSTATUS pData)
1033{
1034 int vrc = VINF_SUCCESS;
1035
1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 AssertPtr(pData);
1039 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1040
1041 /* Callback can be called several times. */
1042 if (it != mCallbackMap.end())
1043 {
1044 PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1045 AssertPtr(pCBData);
1046
1047 pCBData->u32PID = pData->u32PID;
1048 pCBData->u32Status = pData->u32Status;
1049 pCBData->u32Flags = pData->u32Flags;
1050 /** @todo Copy void* buffer contents! */
1051
1052 Utf8Str errMsg;
1053
1054 /* Was progress canceled before? */
1055 BOOL fCanceled;
1056 ComAssert(!it->second.pProgress.isNull());
1057 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))
1058 && !fCanceled)
1059 {
1060 /* Do progress handling. */
1061 HRESULT hr;
1062 switch (pData->u32Status)
1063 {
1064 case PROC_STS_STARTED:
1065 LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */
1066 hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);
1067 AssertComRC(hr);
1068 break;
1069
1070 case PROC_STS_TEN: /* Terminated normally. */
1071 LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */
1072 if (!it->second.pProgress->getCompleted())
1073 {
1074 hr = it->second.pProgress->notifyComplete(S_OK);
1075 AssertComRC(hr);
1076
1077 LogFlowFunc(("Process (CID=%u, status=%u) terminated successfully\n",
1078 pData->hdr.u32ContextID, pData->u32Status));
1079 }
1080 break;
1081
1082 case PROC_STS_TEA: /* Terminated abnormally. */
1083 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
1084 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
1085 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
1086 pCBData->u32Flags);
1087 break;
1088
1089 case PROC_STS_TES: /* Terminated through signal. */
1090 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
1091 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
1092 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
1093 pCBData->u32Flags);
1094 break;
1095
1096 case PROC_STS_TOK:
1097 LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */
1098 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
1099 break;
1100
1101 case PROC_STS_TOA:
1102 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */
1103 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
1104 break;
1105
1106 case PROC_STS_DWN:
1107 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */
1108 /*
1109 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
1110 * our progress object. This is helpful for waiters which rely on the success of our progress object
1111 * even if the executed process was killed because the system/VBoxService is shutting down.
1112 *
1113 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
1114 */
1115 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
1116 {
1117 if (!it->second.pProgress->getCompleted())
1118 {
1119 hr = it->second.pProgress->notifyComplete(S_OK);
1120 AssertComRC(hr);
1121 }
1122 }
1123 else
1124 {
1125 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
1126 }
1127 break;
1128
1129 case PROC_STS_ERROR:
1130 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
1131 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
1132 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);
1133 break;
1134
1135 default:
1136 vrc = VERR_INVALID_PARAMETER;
1137 break;
1138 }
1139
1140 /* Handle process map. */
1141 /** @todo What happens on/deal with PID reuse? */
1142 /** @todo How to deal with multiple updates at once? */
1143 if (pCBData->u32PID > 0)
1144 {
1145 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);
1146 if (it_proc == mGuestProcessMap.end())
1147 {
1148 /* Not found, add to map. */
1149 GuestProcess newProcess;
1150 newProcess.mStatus = pCBData->u32Status;
1151 newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */
1152 newProcess.mFlags = 0;
1153
1154 mGuestProcessMap[pCBData->u32PID] = newProcess;
1155 }
1156 else /* Update map. */
1157 {
1158 it_proc->second.mStatus = pCBData->u32Status;
1159 it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */
1160 it_proc->second.mFlags = 0;
1161 }
1162 }
1163 }
1164 else
1165 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
1166
1167 if (!it->second.pProgress->getCompleted())
1168 {
1169 if ( errMsg.length()
1170 || fCanceled) /* If canceled we have to report E_FAIL! */
1171 {
1172 /* Destroy all callbacks which are still waiting on something
1173 * which is related to the current PID. */
1174 CallbackMapIter it2;
1175 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)
1176 {
1177 switch (it2->second.mType)
1178 {
1179 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
1180 break;
1181
1182 /* When waiting for process output while the process is destroyed,
1183 * make sure we also destroy the actual waiting operation (internal progress object)
1184 * in order to not block the caller. */
1185 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
1186 {
1187 PCALLBACKDATAEXECOUT pItData = (CALLBACKDATAEXECOUT*)it2->second.pvData;
1188 AssertPtr(pItData);
1189 if (pItData->u32PID == pCBData->u32PID)
1190 destroyCtrlCallbackContext(it2);
1191 break;
1192 }
1193
1194 default:
1195 AssertMsgFailed(("Unknown callback type %d\n", it2->second.mType));
1196 break;
1197 }
1198 }
1199
1200 HRESULT hr2 = it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1201 COM_IIDOF(IGuest),
1202 Guest::getStaticComponentName(),
1203 "%s", errMsg.c_str());
1204 AssertComRC(hr2);
1205 LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
1206 pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));
1207 }
1208 }
1209 }
1210 else
1211 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
1212 LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
1213 return vrc;
1214}
1215
1216/* Function for handling the execution output notification. */
1217int Guest::notifyCtrlExecOut(uint32_t u32Function,
1218 PCALLBACKDATAEXECOUT pData)
1219{
1220 int rc = VINF_SUCCESS;
1221
1222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 AssertPtr(pData);
1225 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1226 if (it != mCallbackMap.end())
1227 {
1228 PCALLBACKDATAEXECOUT pCBData = (PCALLBACKDATAEXECOUT)it->second.pvData;
1229 AssertPtr(pCBData);
1230
1231 pCBData->u32PID = pData->u32PID;
1232 pCBData->u32HandleId = pData->u32HandleId;
1233 pCBData->u32Flags = pData->u32Flags;
1234
1235 /* Make sure we really got something! */
1236 if ( pData->cbData
1237 && pData->pvData)
1238 {
1239 /* Allocate data buffer and copy it */
1240 pCBData->pvData = RTMemAlloc(pData->cbData);
1241 pCBData->cbData = pData->cbData;
1242
1243 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
1244 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
1245 }
1246 else
1247 {
1248 pCBData->pvData = NULL;
1249 pCBData->cbData = 0;
1250 }
1251
1252 /* Was progress canceled before? */
1253 BOOL fCanceled;
1254 ComAssert(!it->second.pProgress.isNull());
1255 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
1256 {
1257 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1258 COM_IIDOF(IGuest),
1259 Guest::getStaticComponentName(),
1260 Guest::tr("The output operation was canceled"));
1261 }
1262 else
1263 {
1264 BOOL fCompleted;
1265 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1266 && !fCompleted)
1267 {
1268 /* If we previously got completed notification, don't trigger again. */
1269 it->second.pProgress->notifyComplete(S_OK);
1270 }
1271 }
1272 }
1273 else
1274 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
1275 return rc;
1276}
1277
1278/* Function for handling the execution input status notification. */
1279int Guest::notifyCtrlExecInStatus(uint32_t u32Function,
1280 PCALLBACKDATAEXECINSTATUS pData)
1281{
1282 int rc = VINF_SUCCESS;
1283
1284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1285
1286 AssertPtr(pData);
1287 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1288 if (it != mCallbackMap.end())
1289 {
1290 PCALLBACKDATAEXECINSTATUS pCBData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
1291 AssertPtr(pCBData);
1292
1293 /* Save bytes processed. */
1294 pCBData->cbProcessed = pData->cbProcessed;
1295
1296 /* Was progress canceled before? */
1297 BOOL fCanceled;
1298 ComAssert(!it->second.pProgress.isNull());
1299 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
1300 {
1301 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1302 COM_IIDOF(IGuest),
1303 Guest::getStaticComponentName(),
1304 Guest::tr("The input operation was canceled"));
1305 }
1306 else
1307 {
1308 BOOL fCompleted;
1309 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1310 && !fCompleted)
1311 {
1312 /* If we previously got completed notification, don't trigger again. */
1313 it->second.pProgress->notifyComplete(S_OK);
1314 }
1315 }
1316 }
1317 else
1318 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
1319 return rc;
1320}
1321
1322int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
1323 PCALLBACKDATACLIENTDISCONNECTED pData)
1324{
1325 int rc = VINF_SUCCESS;
1326
1327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1328 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1329 if (it != mCallbackMap.end())
1330 {
1331 LogFlowFunc(("Client with CID=%u disconnected\n", it->first));
1332 destroyCtrlCallbackContext(it);
1333 }
1334 return rc;
1335}
1336
1337Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
1338{
1339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1340 return mCallbackMap.find(u32ContextID);
1341}
1342
1343Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)
1344{
1345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1346 return mGuestProcessMap.find(u32PID);
1347}
1348
1349/* No locking here; */
1350void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)
1351{
1352 LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));
1353
1354 if (it->second.pvData)
1355 {
1356 RTMemFree(it->second.pvData);
1357 it->second.pvData = NULL;
1358 it->second.cbData = 0;
1359 }
1360
1361 /* Notify outstanding waits for progress ... */
1362 if ( it->second.pProgress
1363 && !it->second.pProgress.isNull())
1364 {
1365 LogFlowFunc(("Handling progress for CID=%u ...\n", it->first));
1366
1367 /*
1368 * Assume we didn't complete to make sure we clean up even if the
1369 * following call fails.
1370 */
1371 BOOL fCompleted = FALSE;
1372 it->second.pProgress->COMGETTER(Completed)(&fCompleted);
1373 if (!fCompleted)
1374 {
1375 LogFlowFunc(("Progress of CID=%u *not* completed, cancelling ...\n", it->first));
1376
1377 /* Only cancel if not canceled before! */
1378 BOOL fCanceled;
1379 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && !fCanceled)
1380 it->second.pProgress->Cancel();
1381
1382 /*
1383 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
1384 * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
1385 * is disconnecting without having the chance to sending a status message before, so we
1386 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
1387 * progress object to become signalled.
1388 */
1389 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1390 COM_IIDOF(IGuest),
1391 Guest::getStaticComponentName(),
1392 Guest::tr("The operation was canceled because client is shutting down"));
1393 }
1394 /*
1395 * Do *not* NULL pProgress here, because waiting function like executeProcess()
1396 * will still rely on this object for checking whether they have to give up!
1397 */
1398 }
1399}
1400
1401/* Adds a callback with a user provided data block and an optional progress object
1402 * to the callback map. A callback is identified by a unique context ID which is used
1403 * to identify a callback from the guest side. */
1404uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
1405{
1406 AssertPtr(pProgress);
1407
1408 /** @todo Put this stuff into a constructor! */
1409 CallbackContext context;
1410 context.mType = enmType;
1411 context.pvData = pvData;
1412 context.cbData = cbData;
1413 context.pProgress = pProgress;
1414
1415 /* Create a new context ID and assign it. */
1416 CallbackMapIter it;
1417 uint32_t uNewContext = 0;
1418 do
1419 {
1420 /* Create a new context ID ... */
1421 uNewContext = ASMAtomicIncU32(&mNextContextID);
1422 if (uNewContext == UINT32_MAX)
1423 ASMAtomicUoWriteU32(&mNextContextID, 1000);
1424 /* Is the context ID already used? */
1425 it = getCtrlCallbackContextByID(uNewContext);
1426 } while(it != mCallbackMap.end());
1427
1428 uint32_t nCallbacks = 0;
1429 if ( it == mCallbackMap.end()
1430 && uNewContext > 0)
1431 {
1432 /* We apparently got an unused context ID, let's use it! */
1433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1434 mCallbackMap[uNewContext] = context;
1435 nCallbacks = mCallbackMap.size();
1436 }
1437 else
1438 {
1439 /* Should never happen ... */
1440 {
1441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1442 nCallbacks = mCallbackMap.size();
1443 }
1444 AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));
1445 }
1446
1447#if 0
1448 if (nCallbacks > 256) /* Don't let the container size get too big! */
1449 {
1450 Guest::CallbackListIter it = mCallbackList.begin();
1451 destroyCtrlCallbackContext(it);
1452 {
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454 mCallbackList.erase(it);
1455 }
1456 }
1457#endif
1458 return uNewContext;
1459}
1460#endif /* VBOX_WITH_GUEST_CONTROL */
1461
1462STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
1463 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1464 IN_BSTR aUserName, IN_BSTR aPassword,
1465 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
1466{
1467/** @todo r=bird: Eventually we should clean up all the timeout parameters
1468 * in the API and have the same way of specifying infinite waits! */
1469#ifndef VBOX_WITH_GUEST_CONTROL
1470 ReturnComNotImplemented();
1471#else /* VBOX_WITH_GUEST_CONTROL */
1472 using namespace guestControl;
1473
1474 CheckComArgStrNotEmptyOrNull(aCommand);
1475 CheckComArgOutPointerValid(aPID);
1476 CheckComArgOutPointerValid(aProgress);
1477
1478 /* Do not allow anonymous executions (with system rights). */
1479 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
1480 return setError(E_INVALIDARG, tr("No user name specified"));
1481
1482 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1483 Utf8Str(aCommand).c_str(), Utf8Str(aUserName).c_str()));
1484
1485 return executeProcessInternal(aCommand, aFlags, ComSafeArrayInArg(aArguments),
1486 ComSafeArrayInArg(aEnvironment),
1487 aUserName, aPassword, aTimeoutMS, aPID, aProgress, NULL /* rc */);
1488#endif
1489}
1490
1491HRESULT Guest::executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
1492 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1493 IN_BSTR aUserName, IN_BSTR aPassword,
1494 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC)
1495{
1496/** @todo r=bird: Eventually we should clean up all the timeout parameters
1497 * in the API and have the same way of specifying infinite waits! */
1498#ifndef VBOX_WITH_GUEST_CONTROL
1499 ReturnComNotImplemented();
1500#else /* VBOX_WITH_GUEST_CONTROL */
1501 using namespace guestControl;
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 /* Validate flags. */
1507 if (aFlags)
1508 {
1509 if ( !(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
1510 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly))
1511 {
1512 if (pRC)
1513 *pRC = VERR_INVALID_PARAMETER;
1514 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1515 }
1516 }
1517
1518 HRESULT rc = S_OK;
1519
1520 try
1521 {
1522 /*
1523 * Create progress object. Note that this is a multi operation
1524 * object to perform the following steps:
1525 * - Operation 1 (0): Create/start process.
1526 * - Operation 2 (1): Wait for process to exit.
1527 * If this progress completed successfully (S_OK), the process
1528 * started and exited normally. In any other case an error/exception
1529 * occurred.
1530 */
1531 ComObjPtr <Progress> progress;
1532 rc = progress.createObject();
1533 if (SUCCEEDED(rc))
1534 {
1535 rc = progress->init(static_cast<IGuest*>(this),
1536 Bstr(tr("Executing process")).raw(),
1537 TRUE,
1538 2, /* Number of operations. */
1539 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1540 }
1541 ComAssertComRC(rc);
1542
1543 /*
1544 * Prepare process execution.
1545 */
1546 int vrc = VINF_SUCCESS;
1547 Utf8Str Utf8Command(aCommand);
1548
1549 /* Adjust timeout */
1550 if (aTimeoutMS == 0)
1551 aTimeoutMS = UINT32_MAX;
1552
1553 /* Prepare arguments. */
1554 char **papszArgv = NULL;
1555 uint32_t uNumArgs = 0;
1556 if (aArguments > 0)
1557 {
1558 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1559 uNumArgs = args.size();
1560 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1561 AssertReturn(papszArgv, E_OUTOFMEMORY);
1562 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1563 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1564 papszArgv[uNumArgs] = NULL;
1565 }
1566
1567 Utf8Str Utf8UserName(aUserName);
1568 Utf8Str Utf8Password(aPassword);
1569 if (RT_SUCCESS(vrc))
1570 {
1571 uint32_t uContextID = 0;
1572
1573 char *pszArgs = NULL;
1574 if (uNumArgs > 0)
1575 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
1576 if (RT_SUCCESS(vrc))
1577 {
1578 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1579
1580 /* Prepare environment. */
1581 void *pvEnv = NULL;
1582 uint32_t uNumEnv = 0;
1583 uint32_t cbEnv = 0;
1584 if (aEnvironment > 0)
1585 {
1586 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1587
1588 for (unsigned i = 0; i < env.size(); i++)
1589 {
1590 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1591 if (RT_FAILURE(vrc))
1592 break;
1593 }
1594 }
1595
1596 if (RT_SUCCESS(vrc))
1597 {
1598 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
1599 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1600 RT_ZERO(*pData);
1601 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
1602 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
1603 Assert(uContextID > 0);
1604
1605 VBOXHGCMSVCPARM paParms[15];
1606 int i = 0;
1607 paParms[i++].setUInt32(uContextID);
1608 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1609 paParms[i++].setUInt32(aFlags);
1610 paParms[i++].setUInt32(uNumArgs);
1611 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1612 paParms[i++].setUInt32(uNumEnv);
1613 paParms[i++].setUInt32(cbEnv);
1614 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1615 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1616 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1617
1618 /*
1619 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1620 * until the process was started - the process itself then gets an infinit timeout for execution.
1621 * This is handy when we want to start a process inside a worker thread within a certain timeout
1622 * but let the started process perform lengthly operations then.
1623 */
1624 if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1625 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1626 else
1627 paParms[i++].setUInt32(aTimeoutMS);
1628
1629 VMMDev *vmmDev;
1630 {
1631 /* Make sure mParent is valid, so set the read lock while using.
1632 * Do not keep this lock while doing the actual call, because in the meanwhile
1633 * another thread could request a write lock which would be a bad idea ... */
1634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 /* Forward the information to the VMM device. */
1637 AssertPtr(mParent);
1638 vmmDev = mParent->getVMMDev();
1639 }
1640
1641 if (vmmDev)
1642 {
1643 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1644 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1645 i, paParms);
1646 }
1647 else
1648 vrc = VERR_INVALID_VM_HANDLE;
1649 RTMemFree(pvEnv);
1650 }
1651 RTStrFree(pszArgs);
1652 }
1653 if (RT_SUCCESS(vrc))
1654 {
1655 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1656
1657 /*
1658 * Wait for the HGCM low level callback until the process
1659 * has been started (or something went wrong). This is necessary to
1660 * get the PID.
1661 */
1662 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1663 BOOL fCanceled = FALSE;
1664 if (it != mCallbackMap.end())
1665 {
1666 ComAssert(!it->second.pProgress.isNull());
1667
1668 /*
1669 * Wait for the first stage (=0) to complete (that is starting the process).
1670 */
1671 PCALLBACKDATAEXECSTATUS pData = NULL;
1672 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
1673 if (SUCCEEDED(rc))
1674 {
1675 /* Was the operation canceled by one of the parties? */
1676 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1677 if (FAILED(rc)) throw rc;
1678
1679 if (!fCanceled)
1680 {
1681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1684 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
1685 AssertPtr(pData);
1686
1687 /* Did we get some status? */
1688 switch (pData->u32Status)
1689 {
1690 case PROC_STS_STARTED:
1691 /* Process is (still) running; get PID. */
1692 *aPID = pData->u32PID;
1693 break;
1694
1695 /* In any other case the process either already
1696 * terminated or something else went wrong, so no PID ... */
1697 case PROC_STS_TEN: /* Terminated normally. */
1698 case PROC_STS_TEA: /* Terminated abnormally. */
1699 case PROC_STS_TES: /* Terminated through signal. */
1700 case PROC_STS_TOK:
1701 case PROC_STS_TOA:
1702 case PROC_STS_DWN:
1703 /*
1704 * Process (already) ended, but we want to get the
1705 * PID anyway to retrieve the output in a later call.
1706 */
1707 *aPID = pData->u32PID;
1708 break;
1709
1710 case PROC_STS_ERROR:
1711 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
1712 break;
1713
1714 case PROC_STS_UNDEFINED:
1715 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */
1716 break;
1717
1718 default:
1719 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
1720 break;
1721 }
1722 }
1723 else /* Operation was canceled. */
1724 vrc = VERR_CANCELLED;
1725 }
1726 else /* Operation did not complete within time. */
1727 vrc = VERR_TIMEOUT;
1728
1729 /*
1730 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1731 * else (like end of process) ...
1732 */
1733 if (RT_FAILURE(vrc))
1734 {
1735 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1736 rc = setError(VBOX_E_IPRT_ERROR,
1737 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1738 else if (vrc == VERR_PATH_NOT_FOUND)
1739 rc = setError(VBOX_E_IPRT_ERROR,
1740 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1741 else if (vrc == VERR_BAD_EXE_FORMAT)
1742 rc = setError(VBOX_E_IPRT_ERROR,
1743 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1744 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1745 rc = setError(VBOX_E_IPRT_ERROR,
1746 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1747 else if (vrc == VERR_TIMEOUT)
1748 rc = setError(VBOX_E_IPRT_ERROR,
1749 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1750 else if (vrc == VERR_CANCELLED)
1751 rc = setError(VBOX_E_IPRT_ERROR,
1752 tr("The execution operation was canceled"));
1753 else if (vrc == VERR_PERMISSION_DENIED)
1754 rc = setError(VBOX_E_IPRT_ERROR,
1755 tr("Invalid user/password credentials"));
1756 else
1757 {
1758 if (pData && pData->u32Status == PROC_STS_ERROR)
1759 rc = setError(VBOX_E_IPRT_ERROR,
1760 tr("Process could not be started: %Rrc"), pData->u32Flags);
1761 else
1762 rc = setError(E_UNEXPECTED,
1763 tr("The service call failed with error %Rrc"), vrc);
1764 }
1765 }
1766 else /* Execution went fine. */
1767 {
1768 /* Return the progress to the caller. */
1769 progress.queryInterfaceTo(aProgress);
1770 }
1771 }
1772 else /* Callback context not found; should never happen! */
1773 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1774 }
1775 else /* HGCM related error codes .*/
1776 {
1777 if (vrc == VERR_INVALID_VM_HANDLE)
1778 rc = setError(VBOX_E_VM_ERROR,
1779 tr("VMM device is not available (is the VM running?)"));
1780 else if (vrc == VERR_TIMEOUT)
1781 rc = setError(VBOX_E_VM_ERROR,
1782 tr("The guest execution service is not ready"));
1783 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
1784 rc = setError(VBOX_E_VM_ERROR,
1785 tr("The guest execution service is not available"));
1786 else /* HGCM call went wrong. */
1787 rc = setError(E_UNEXPECTED,
1788 tr("The HGCM call failed with error %Rrc"), vrc);
1789 }
1790
1791 for (unsigned i = 0; i < uNumArgs; i++)
1792 RTMemFree(papszArgv[i]);
1793 RTMemFree(papszArgv);
1794 }
1795
1796 if (RT_FAILURE(vrc))
1797 {
1798 if (!pRC) /* Skip logging internal calls. */
1799 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1800 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1801 }
1802
1803 if (pRC)
1804 *pRC = vrc;
1805 }
1806 catch (std::bad_alloc &)
1807 {
1808 rc = E_OUTOFMEMORY;
1809 }
1810 return rc;
1811#endif /* VBOX_WITH_GUEST_CONTROL */
1812}
1813
1814STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)
1815{
1816#ifndef VBOX_WITH_GUEST_CONTROL
1817 ReturnComNotImplemented();
1818#else /* VBOX_WITH_GUEST_CONTROL */
1819 using namespace guestControl;
1820
1821 CheckComArgExpr(aPID, aPID > 0);
1822 CheckComArgOutPointerValid(aBytesWritten);
1823
1824 /* Validate flags. */
1825 if (aFlags)
1826 {
1827 if (!(aFlags & ProcessInputFlag_EndOfFile))
1828 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1829 }
1830
1831 AutoCaller autoCaller(this);
1832 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1833
1834 HRESULT rc = S_OK;
1835
1836 try
1837 {
1838 /* Init. */
1839 *aBytesWritten = 0;
1840
1841 /* Search for existing PID. */
1842 GuestProcessMapIterConst itProc = getProcessByPID(aPID);
1843 if (itProc != mGuestProcessMap.end())
1844 {
1845 /* PID exists; check if process is still running. */
1846 if (itProc->second.mStatus != PROC_STS_STARTED)
1847 {
1848 rc = setError(VBOX_E_IPRT_ERROR,
1849 tr("Process (PID %u) does not run anymore! Status: %ld, Flags: %u, Exit Code: %u"),
1850 aPID, itProc->second.mStatus, itProc->second.mFlags, itProc->second.mExitCode);
1851 }
1852 }
1853 else
1854 rc = setError(VBOX_E_IPRT_ERROR,
1855 tr("Process (PID %u) not found!"), aPID);
1856
1857 if (SUCCEEDED(rc))
1858 {
1859 /*
1860 * Create progress object.
1861 * This progress object, compared to the one in executeProgress() above
1862 * is only local and is used to determine whether the operation finished
1863 * or got canceled.
1864 */
1865 ComObjPtr <Progress> progress;
1866 rc = progress.createObject();
1867 if (SUCCEEDED(rc))
1868 {
1869 rc = progress->init(static_cast<IGuest*>(this),
1870 Bstr(tr("Setting input for process")).raw(),
1871 TRUE /* Cancelable */);
1872 }
1873 if (FAILED(rc)) return rc;
1874
1875 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
1876 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1877 RT_ZERO(*pData);
1878 /* Save PID + output flags for later use. */
1879 pData->u32PID = aPID;
1880 pData->u32Flags = aFlags;
1881 /* Add job to callback contexts. */
1882 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS,
1883 pData, sizeof(CALLBACKDATAEXECINSTATUS), progress);
1884 Assert(uContextID > 0);
1885
1886 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
1887 uint32_t cbSize = sfaData.size();
1888
1889 VBOXHGCMSVCPARM paParms[6];
1890 int i = 0;
1891 paParms[i++].setUInt32(uContextID);
1892 paParms[i++].setUInt32(aPID);
1893 paParms[i++].setUInt32(aFlags);
1894 paParms[i++].setPointer(sfaData.raw(), cbSize);
1895 paParms[i++].setUInt32(cbSize);
1896
1897 int vrc = VINF_SUCCESS;
1898
1899 {
1900 VMMDev *vmmDev;
1901 {
1902 /* Make sure mParent is valid, so set the read lock while using.
1903 * Do not keep this lock while doing the actual call, because in the meanwhile
1904 * another thread could request a write lock which would be a bad idea ... */
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 /* Forward the information to the VMM device. */
1908 AssertPtr(mParent);
1909 vmmDev = mParent->getVMMDev();
1910 }
1911
1912 if (vmmDev)
1913 {
1914 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1915 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
1916 i, paParms);
1917 }
1918 }
1919
1920 if (RT_SUCCESS(vrc))
1921 {
1922 LogFlowFunc(("Waiting for HGCM callback ...\n"));
1923
1924 /*
1925 * Wait for the HGCM low level callback until the process
1926 * has been started (or something went wrong). This is necessary to
1927 * get the PID.
1928 */
1929 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1930 BOOL fCanceled = FALSE;
1931 if (it != mCallbackMap.end())
1932 {
1933 ComAssert(!it->second.pProgress.isNull());
1934
1935 /* Wait until operation completed. */
1936 rc = it->second.pProgress->WaitForCompletion(UINT32_MAX /* Wait forever */);
1937 if (FAILED(rc)) throw rc;
1938
1939 /* Was the operation canceled by one of the parties? */
1940 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1941 if (FAILED(rc)) throw rc;
1942
1943 if (!fCanceled)
1944 {
1945 BOOL fCompleted;
1946 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1947 && fCompleted)
1948 {
1949 PCALLBACKDATAEXECINSTATUS pStatusData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
1950 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECINSTATUS));
1951 AssertPtr(pStatusData);
1952
1953 *aBytesWritten = pStatusData->cbProcessed;
1954 }
1955 }
1956 else /* Operation was canceled. */
1957 vrc = VERR_CANCELLED;
1958
1959 if (RT_FAILURE(vrc))
1960 {
1961 if (vrc == VERR_CANCELLED)
1962 {
1963 rc = setError(VBOX_E_IPRT_ERROR,
1964 tr("The input operation was canceled"));
1965 }
1966 else
1967 {
1968 rc = setError(E_UNEXPECTED,
1969 tr("The service call failed with error %Rrc"), vrc);
1970 }
1971 }
1972
1973 {
1974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1975 /*
1976 * Destroy locally used progress object.
1977 */
1978 destroyCtrlCallbackContext(it);
1979 }
1980
1981 /* Remove callback context (not used anymore). */
1982 mCallbackMap.erase(it);
1983 }
1984 else /* PID lookup failed. */
1985 rc = setError(VBOX_E_IPRT_ERROR,
1986 tr("Process (PID %u) not found!"), aPID);
1987 }
1988 else /* HGCM operation failed. */
1989 rc = setError(E_UNEXPECTED,
1990 tr("The HGCM call failed with error %Rrc"), vrc);
1991
1992 /* Cleanup. */
1993 progress->uninit();
1994 progress.setNull();
1995 }
1996 }
1997 catch (std::bad_alloc &)
1998 {
1999 rc = E_OUTOFMEMORY;
2000 }
2001 return rc;
2002#endif
2003}
2004
2005STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
2006{
2007/** @todo r=bird: Eventually we should clean up all the timeout parameters
2008 * in the API and have the same way of specifying infinite waits! */
2009#ifndef VBOX_WITH_GUEST_CONTROL
2010 ReturnComNotImplemented();
2011#else /* VBOX_WITH_GUEST_CONTROL */
2012 using namespace guestControl;
2013
2014 CheckComArgExpr(aPID, aPID > 0);
2015 if (aSize < 0)
2016 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
2017 if (aFlags != 0) /* Flags are not supported at the moment. */
2018 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2019
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2022
2023 HRESULT rc = S_OK;
2024
2025 try
2026 {
2027 /*
2028 * Create progress object.
2029 * This progress object, compared to the one in executeProgress() above
2030 * is only local and is used to determine whether the operation finished
2031 * or got canceled.
2032 */
2033 ComObjPtr <Progress> progress;
2034 rc = progress.createObject();
2035 if (SUCCEEDED(rc))
2036 {
2037 rc = progress->init(static_cast<IGuest*>(this),
2038 Bstr(tr("Getting output of process")).raw(),
2039 TRUE);
2040 }
2041 if (FAILED(rc)) return rc;
2042
2043 /* Adjust timeout */
2044 if (aTimeoutMS == 0)
2045 aTimeoutMS = UINT32_MAX;
2046
2047 /* Search for existing PID. */
2048 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
2049 AssertReturn(pData, VBOX_E_IPRT_ERROR);
2050 RT_ZERO(*pData);
2051 /* Save PID + output flags for later use. */
2052 pData->u32PID = aPID;
2053 pData->u32Flags = aFlags;
2054 /* Add job to callback contexts. */
2055 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
2056 pData, sizeof(CALLBACKDATAEXECOUT), progress);
2057 Assert(uContextID > 0);
2058
2059 size_t cbData = (size_t)RT_MIN(aSize, _64K);
2060 com::SafeArray<BYTE> outputData(cbData);
2061
2062 VBOXHGCMSVCPARM paParms[5];
2063 int i = 0;
2064 paParms[i++].setUInt32(uContextID);
2065 paParms[i++].setUInt32(aPID);
2066 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
2067
2068 int vrc = VINF_SUCCESS;
2069
2070 {
2071 VMMDev *vmmDev;
2072 {
2073 /* Make sure mParent is valid, so set the read lock while using.
2074 * Do not keep this lock while doing the actual call, because in the meanwhile
2075 * another thread could request a write lock which would be a bad idea ... */
2076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2077
2078 /* Forward the information to the VMM device. */
2079 AssertPtr(mParent);
2080 vmmDev = mParent->getVMMDev();
2081 }
2082
2083 if (vmmDev)
2084 {
2085 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
2086 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
2087 i, paParms);
2088 }
2089 }
2090
2091 if (RT_SUCCESS(vrc))
2092 {
2093 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
2094
2095 /*
2096 * Wait for the HGCM low level callback until the process
2097 * has been started (or something went wrong). This is necessary to
2098 * get the PID.
2099 */
2100 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
2101 BOOL fCanceled = FALSE;
2102 if (it != mCallbackMap.end())
2103 {
2104 ComAssert(!it->second.pProgress.isNull());
2105
2106 /* Wait until operation completed. */
2107 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
2108 if (FAILED(rc)) throw rc;
2109
2110 /* Was the operation canceled by one of the parties? */
2111 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
2112 if (FAILED(rc)) throw rc;
2113
2114 if (!fCanceled)
2115 {
2116 BOOL fCompleted;
2117 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
2118 && fCompleted)
2119 {
2120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2121
2122 /* Did we get some output? */
2123 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
2124 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
2125 AssertPtr(pData);
2126
2127 if (pData->cbData)
2128 {
2129 /* Do we need to resize the array? */
2130 if (pData->cbData > cbData)
2131 outputData.resize(pData->cbData);
2132
2133 /* Fill output in supplied out buffer. */
2134 memcpy(outputData.raw(), pData->pvData, pData->cbData);
2135 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
2136 }
2137 else
2138 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
2139 }
2140 else /* If callback not called within time ... well, that's a timeout! */
2141 vrc = VERR_TIMEOUT;
2142 }
2143 else /* Operation was canceled. */
2144 {
2145 vrc = VERR_CANCELLED;
2146 }
2147
2148 if (RT_FAILURE(vrc))
2149 {
2150 if (vrc == VERR_NO_DATA)
2151 {
2152 /* This is not an error we want to report to COM. */
2153 rc = S_OK;
2154 }
2155 else if (vrc == VERR_TIMEOUT)
2156 {
2157 rc = setError(VBOX_E_IPRT_ERROR,
2158 tr("The guest did not output within time (%ums)"), aTimeoutMS);
2159 }
2160 else if (vrc == VERR_CANCELLED)
2161 {
2162 rc = setError(VBOX_E_IPRT_ERROR,
2163 tr("The output operation was canceled"));
2164 }
2165 else
2166 {
2167 rc = setError(E_UNEXPECTED,
2168 tr("The service call failed with error %Rrc"), vrc);
2169 }
2170 }
2171
2172 {
2173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2174 /*
2175 * Destroy locally used progress object.
2176 */
2177 destroyCtrlCallbackContext(it);
2178 }
2179
2180 /* Remove callback context (not used anymore). */
2181 mCallbackMap.erase(it);
2182 }
2183 else /* PID lookup failed. */
2184 rc = setError(VBOX_E_IPRT_ERROR,
2185 tr("Process (PID %u) not found!"), aPID);
2186 }
2187 else /* HGCM operation failed. */
2188 rc = setError(E_UNEXPECTED,
2189 tr("The HGCM call failed with error %Rrc"), vrc);
2190
2191 /* Cleanup. */
2192 progress->uninit();
2193 progress.setNull();
2194
2195 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
2196 * we return an empty array so that the frontend knows when to give up. */
2197 if (RT_FAILURE(vrc) || FAILED(rc))
2198 outputData.resize(0);
2199 outputData.detachTo(ComSafeArrayOutArg(aData));
2200 }
2201 catch (std::bad_alloc &)
2202 {
2203 rc = E_OUTOFMEMORY;
2204 }
2205 return rc;
2206#endif
2207}
2208
2209STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
2210{
2211#ifndef VBOX_WITH_GUEST_CONTROL
2212 ReturnComNotImplemented();
2213#else /* VBOX_WITH_GUEST_CONTROL */
2214 using namespace guestControl;
2215
2216 AutoCaller autoCaller(this);
2217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2218
2219 HRESULT rc = S_OK;
2220
2221 try
2222 {
2223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 GuestProcessMapIterConst it = getProcessByPID(aPID);
2226 if (it != mGuestProcessMap.end())
2227 {
2228 *aExitCode = it->second.mExitCode;
2229 *aFlags = it->second.mFlags;
2230 *aStatus = it->second.mStatus;
2231 }
2232 else
2233 rc = setError(VBOX_E_IPRT_ERROR,
2234 tr("Process (PID %u) not found!"), aPID);
2235 }
2236 catch (std::bad_alloc &)
2237 {
2238 rc = E_OUTOFMEMORY;
2239 }
2240 return rc;
2241#endif
2242}
2243
2244/** @todo For having a progress object which actually reports something,
2245 * the actual copy loop (see below) needs to go to some worker thread
2246 * so that this routine can return to the caller (and the caller then
2247 * can do display a progress). */
2248STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
2249 IN_BSTR aUserName, IN_BSTR aPassword,
2250 ULONG aFlags, IProgress **aProgress)
2251{
2252#ifndef VBOX_WITH_GUEST_CONTROL
2253 ReturnComNotImplemented();
2254#else /* VBOX_WITH_GUEST_CONTROL */
2255 using namespace guestControl;
2256
2257 CheckComArgStrNotEmptyOrNull(aSource);
2258 CheckComArgStrNotEmptyOrNull(aDest);
2259 CheckComArgOutPointerValid(aProgress);
2260
2261 AutoCaller autoCaller(this);
2262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2263
2264 /* Validate flags. */
2265 if (aFlags != CopyFileFlag_None)
2266 {
2267 if ( !(aFlags & CopyFileFlag_Recursive)
2268 && !(aFlags & CopyFileFlag_Update)
2269 && !(aFlags & CopyFileFlag_FollowLinks))
2270 {
2271 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2272 }
2273 }
2274
2275 HRESULT rc = S_OK;
2276
2277 try
2278 {
2279 Utf8Str Utf8Source(aSource);
2280 Utf8Str Utf8Dest(aDest);
2281 Utf8Str Utf8UserName(aUserName);
2282 Utf8Str Utf8Password(aPassword);
2283
2284 /* Does our source file exist? */
2285 if (!RTFileExists(Utf8Source.c_str()))
2286 {
2287 rc = setError(VBOX_E_FILE_ERROR,
2288 tr("Source file \"%s\" does not exist"), Utf8Source.c_str());
2289 }
2290 else
2291 {
2292 RTFILE fileSource;
2293 int vrc = RTFileOpen(&fileSource, Utf8Source.c_str(),
2294 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
2295 if (RT_FAILURE(vrc))
2296 {
2297 rc = setError(VBOX_E_IPRT_ERROR,
2298 tr("Could not open source file \"%s\" for reading, rc=%Rrc"),
2299 Utf8Source.c_str(), vrc);
2300 }
2301 else
2302 {
2303 uint64_t cbSize;
2304 vrc = RTFileGetSize(fileSource, &cbSize);
2305 if (RT_FAILURE(vrc))
2306 {
2307 rc = setError(VBOX_E_IPRT_ERROR,
2308 tr("Could not query file size of \"%s\", rc=%Rrc"),
2309 Utf8Source.c_str(), vrc);
2310 }
2311 else
2312 {
2313 com::SafeArray<IN_BSTR> args;
2314 com::SafeArray<IN_BSTR> env;
2315
2316 /*
2317 * Prepare tool command line.
2318 */
2319 char szOutput[RTPATH_MAX];
2320 if (RTStrPrintf(szOutput, sizeof(szOutput), "--output=%s", Utf8Dest.c_str()))
2321 {
2322 /*
2323 * Normalize path slashes, based on the detected guest.
2324 */
2325 Utf8Str osType = mData.mOSTypeId;
2326 if ( osType.contains("Microsoft", Utf8Str::CaseInsensitive)
2327 || osType.contains("Windows", Utf8Str::CaseInsensitive))
2328 {
2329 /* We have a Windows guest. */
2330 RTPathChangeToDosSlashes(szOutput, true /* Force conversion. */);
2331 }
2332 else /* ... or something which isn't from Redmond ... */
2333 {
2334 RTPathChangeToUnixSlashes(szOutput, true /* Force conversion. */);
2335 }
2336
2337 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */
2338 args.push_back(Bstr(szOutput).raw()); /* We want to write a file ... */
2339 }
2340 else
2341 rc = setError(VBOX_E_IPRT_ERROR, tr("Error preparing command line"));
2342
2343 ComPtr<IProgress> execProgress;
2344 ULONG uPID;
2345 if (SUCCEEDED(rc))
2346 {
2347 LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",
2348 Utf8Source.c_str(), Utf8Dest.c_str(), cbSize));
2349 /*
2350 * Okay, since we gathered all stuff we need until now to start the
2351 * actual copying, start the guest part now.
2352 */
2353 rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
2354 ExecuteProcessFlag_WaitForProcessStartOnly,
2355 ComSafeArrayAsInParam(args),
2356 ComSafeArrayAsInParam(env),
2357 Bstr(Utf8UserName).raw(),
2358 Bstr(Utf8Password).raw(),
2359 5 * 1000 /* Wait 5s for getting the process started. */,
2360 &uPID, execProgress.asOutParam());
2361 }
2362
2363 if (SUCCEEDED(rc))
2364 {
2365 /* Wait for process to exit ... */
2366 BOOL fCompleted = FALSE;
2367 BOOL fCanceled = FALSE;
2368
2369 size_t cbRead;
2370 SafeArray<BYTE> aInputData(_64K);
2371 while ( SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
2372 && !fCompleted)
2373 {
2374 vrc = RTFileRead(fileSource, (uint8_t*)aInputData.raw(), _64K, &cbRead);
2375 if ( cbRead == 0
2376 || vrc == VERR_EOF)
2377 break;
2378
2379 aInputData.resize(cbRead);
2380
2381 /* Did we reach the end of the content
2382 * we want to transfer (last chunk)? */
2383 ULONG uFlags = ProcessInputFlag_None;
2384 if (cbRead < _64K)
2385 uFlags |= ProcessInputFlag_EndOfFile;
2386
2387 /* Transfer the current chunk ... */
2388 ULONG uBytesWritten;
2389 rc = SetProcessInput(uPID, uFlags,
2390 ComSafeArrayAsInParam(aInputData), &uBytesWritten);
2391 if (FAILED(rc))
2392 break;
2393
2394 /* Progress canceled by Main API? */
2395 if ( SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))
2396 && fCanceled)
2397 {
2398 break;
2399 }
2400 }
2401
2402 if (SUCCEEDED(rc))
2403 {
2404 /* Return the progress to the caller. */
2405 execProgress.queryInterfaceTo(aProgress);
2406 }
2407 }
2408 }
2409 RTFileClose(fileSource);
2410 }
2411 }
2412 }
2413 catch (std::bad_alloc &)
2414 {
2415 rc = E_OUTOFMEMORY;
2416 }
2417 return rc;
2418#endif /* VBOX_WITH_GUEST_CONTROL */
2419}
2420
2421STDMETHODIMP Guest::CreateDirectory(IN_BSTR aDirectory,
2422 IN_BSTR aUserName, IN_BSTR aPassword,
2423 ULONG aMode, ULONG aFlags,
2424 IProgress **aProgress)
2425{
2426#ifndef VBOX_WITH_GUEST_CONTROL
2427 ReturnComNotImplemented();
2428#else /* VBOX_WITH_GUEST_CONTROL */
2429 using namespace guestControl;
2430
2431 CheckComArgStrNotEmptyOrNull(aDirectory);
2432
2433 /* Do not allow anonymous executions (with system rights). */
2434 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
2435 return setError(E_INVALIDARG, tr("No user name specified"));
2436
2437 LogRel(("Creating guest directory \"%s\" as user \"%s\" ...\n",
2438 Utf8Str(aDirectory).c_str(), Utf8Str(aUserName).c_str()));
2439
2440 return createDirectoryInternal(aDirectory,
2441 aUserName, aPassword,
2442 aMode, aFlags, aProgress, NULL /* rc */);
2443#endif
2444}
2445
2446HRESULT Guest::createDirectoryInternal(IN_BSTR aDirectory,
2447 IN_BSTR aUserName, IN_BSTR aPassword,
2448 ULONG aMode, ULONG aFlags,
2449 IProgress **aProgress, int *pRC)
2450{
2451#ifndef VBOX_WITH_GUEST_CONTROL
2452 ReturnComNotImplemented();
2453#else /* VBOX_WITH_GUEST_CONTROL */
2454 using namespace guestControl;
2455
2456 CheckComArgStrNotEmptyOrNull(aDirectory);
2457 CheckComArgOutPointerValid(aProgress);
2458
2459 AutoCaller autoCaller(this);
2460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2461
2462 /* Validate flags. */
2463 if (aFlags != CreateDirectoryFlag_None)
2464 {
2465 if (!(aFlags & CreateDirectoryFlag_Parents))
2466 {
2467 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2468 }
2469 }
2470
2471 HRESULT rc = S_OK;
2472
2473 try
2474 {
2475 Utf8Str Utf8Directory(aDirectory);
2476 Utf8Str Utf8UserName(aUserName);
2477 Utf8Str Utf8Password(aPassword);
2478
2479 com::SafeArray<IN_BSTR> args;
2480 com::SafeArray<IN_BSTR> env;
2481
2482 /*
2483 * Prepare tool command line.
2484 */
2485 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */
2486 if (aFlags & CreateDirectoryFlag_Parents)
2487 args.push_back(Bstr("--parents").raw()); /* We also want to create the parent directories. */
2488 if (aMode > 0)
2489 {
2490 args.push_back(Bstr("--mode").raw()); /* Set the creation mode. */
2491
2492 char szMode[16];
2493 if (RTStrPrintf(szMode, sizeof(szMode), "%o", aMode))
2494 args.push_back(Bstr(szMode).raw());
2495 else
2496 rc = setError(VBOX_E_IPRT_ERROR, tr("Error preparing command line"));
2497 }
2498 args.push_back(Bstr(Utf8Directory).raw()); /* The directory we want to create. */
2499
2500 /*
2501 * Execute guest process.
2502 */
2503 ComPtr<IProgress> execProgress;
2504 ULONG uPID;
2505 if (SUCCEEDED(rc))
2506 {
2507 rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
2508 ExecuteProcessFlag_None,
2509 ComSafeArrayAsInParam(args),
2510 ComSafeArrayAsInParam(env),
2511 Bstr(Utf8UserName).raw(),
2512 Bstr(Utf8Password).raw(),
2513 5 * 1000 /* Wait 5s for getting the process started. */,
2514 &uPID, execProgress.asOutParam());
2515 }
2516
2517 if (SUCCEEDED(rc))
2518 {
2519 /* Wait for process to exit ... */
2520 BOOL fCompleted = FALSE;
2521 BOOL fCanceled = FALSE;
2522
2523 while ( SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
2524 && !fCompleted)
2525 {
2526 /* Progress canceled by Main API? */
2527 if ( SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))
2528 && fCanceled)
2529 {
2530 break;
2531 }
2532 }
2533
2534 if (SUCCEEDED(rc))
2535 {
2536 /* Return the progress to the caller. */
2537 execProgress.queryInterfaceTo(aProgress);
2538 }
2539 }
2540 }
2541 catch (std::bad_alloc &)
2542 {
2543 rc = E_OUTOFMEMORY;
2544 }
2545 return rc;
2546#endif /* VBOX_WITH_GUEST_CONTROL */
2547}
2548
2549STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ULONG aFlags, IProgress **aProgress)
2550{
2551#ifndef VBOX_WITH_GUEST_CONTROL
2552 ReturnComNotImplemented();
2553#else /* VBOX_WITH_GUEST_CONTROL */
2554 CheckComArgStrNotEmptyOrNull(aSource);
2555 CheckComArgOutPointerValid(aProgress);
2556
2557 if (aFlags != 0) /* Flags are not supported at the moment. */
2558 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2559
2560 AutoCaller autoCaller(this);
2561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2562
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 HRESULT rc = S_OK;
2566
2567 ComObjPtr<Progress> progress;
2568 try
2569 {
2570 /* Create the progress object. */
2571 progress.createObject();
2572
2573 rc = progress->init(static_cast<IGuest*>(this),
2574 Bstr(tr("Updating Guest Additions")).raw(),
2575 TRUE /* aCancelable */);
2576 if (FAILED(rc)) throw rc;
2577
2578 /* Initialize our worker task. */
2579 std::auto_ptr<TaskGuest> task(new TaskGuest(TaskGuest::UpdateGuestAdditions, this, progress));
2580
2581 /* Assign data - in that case aSource is the full path
2582 * to the Guest Additions .ISO we want to mount. */
2583 task->strSource = (Utf8Str(aSource));
2584
2585 rc = task->startThread();
2586 if (FAILED(rc)) throw rc;
2587
2588 /* Don't destruct on success. */
2589 task.release();
2590 }
2591 catch (HRESULT aRC)
2592 {
2593 rc = aRC;
2594 }
2595
2596 if (SUCCEEDED(rc))
2597 {
2598 /* Return progress to the caller. */
2599 progress.queryInterfaceTo(aProgress);
2600 }
2601 return rc;
2602#endif /* VBOX_WITH_GUEST_CONTROL */
2603}
2604
2605// public methods only for internal purposes
2606/////////////////////////////////////////////////////////////////////////////
2607
2608/**
2609 * Sets the general Guest Additions information like
2610 * API (interface) version and OS type. Gets called by
2611 * vmmdevUpdateGuestInfo.
2612 *
2613 * @param aInterfaceVersion
2614 * @param aOsType
2615 */
2616void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)
2617{
2618 AutoCaller autoCaller(this);
2619 AssertComRCReturnVoid(autoCaller.rc());
2620
2621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 /*
2624 * Note: The Guest Additions API (interface) version is deprecated
2625 * and will not be used anymore! We might need it to at least report
2626 * something as version number if *really* ancient Guest Additions are
2627 * installed (without the guest version + revision properties having set).
2628 */
2629 mData.mInterfaceVersion = aInterfaceVersion;
2630
2631 /*
2632 * Older Additions rely on the Additions API version whether they
2633 * are assumed to be active or not. Since newer Additions do report
2634 * the Additions version *before* calling this function (by calling
2635 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
2636 * in that order) we can tell apart old and new Additions here. Old
2637 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
2638 * so they just rely on the aInterfaceVersion string (which gets set by
2639 * VMMDevReportGuestInfo).
2640 *
2641 * So only mark the Additions as being active (run level = system) when we
2642 * don't have the Additions version set.
2643 */
2644 if (mData.mAdditionsVersion.isEmpty())
2645 {
2646 if (aInterfaceVersion.isEmpty())
2647 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
2648 else
2649 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
2650 }
2651
2652 /*
2653 * Older Additions didn't have this finer grained capability bit,
2654 * so enable it by default. Newer Additions will not enable this here
2655 * and use the setSupportedFeatures function instead.
2656 */
2657 mData.mSupportsGraphics = mData.mAdditionsRunLevel > AdditionsRunLevelType_None;
2658
2659 /*
2660 * Note! There is a race going on between setting mAdditionsRunLevel and
2661 * mSupportsGraphics here and disabling/enabling it later according to
2662 * its real status when using new(er) Guest Additions.
2663 */
2664 mData.mOSTypeId = Global::OSTypeId (aOsType);
2665}
2666
2667/**
2668 * Sets the Guest Additions version information details.
2669 * Gets called by vmmdevUpdateGuestInfo2.
2670 *
2671 * @param aAdditionsVersion
2672 * @param aVersionName
2673 */
2674void Guest::setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision)
2675{
2676 AutoCaller autoCaller(this);
2677 AssertComRCReturnVoid(autoCaller.rc());
2678
2679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 if (!aVersionName.isEmpty())
2682 /*
2683 * aVersionName could be "x.y.z_BETA1_FOOBAR", so append revision manually to
2684 * become "x.y.z_BETA1_FOOBARr12345".
2685 */
2686 mData.mAdditionsVersion = BstrFmt("%ls r%ls", aVersionName.raw(), aRevision.raw());
2687 else /* aAdditionsVersion is in x.y.zr12345 format. */
2688 mData.mAdditionsVersion = aAdditionsVersion;
2689}
2690
2691/**
2692 * Sets the status of a certain Guest Additions facility.
2693 * Gets called by vmmdevUpdateGuestStatus.
2694 *
2695 * @param Facility
2696 * @param Status
2697 * @param ulFlags
2698 */
2699void Guest::setAdditionsStatus(VBoxGuestStatusFacility Facility, VBoxGuestStatusCurrent Status, ULONG ulFlags)
2700{
2701 AutoCaller autoCaller(this);
2702 AssertComRCReturnVoid(autoCaller.rc());
2703
2704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 uint32_t uCurFacility = Facility + (Status == VBoxGuestStatusCurrent_Active ? 0 : -1);
2707
2708 /* First check for disabled status. */
2709 if ( Facility < VBoxGuestStatusFacility_VBoxGuestDriver
2710 || ( Facility == VBoxGuestStatusFacility_All
2711 && ( Status == VBoxGuestStatusCurrent_Inactive
2712 || Status == VBoxGuestStatusCurrent_Disabled
2713 )
2714 )
2715 )
2716 {
2717 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
2718 }
2719 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxTray)
2720 {
2721 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;
2722 }
2723 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxService)
2724 {
2725 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;
2726 }
2727 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxGuestDriver)
2728 {
2729 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
2730 }
2731 else /* Should never happen! */
2732 AssertMsgFailed(("Invalid facility status/run level detected! uCurFacility=%ld\n", uCurFacility));
2733}
2734
2735/**
2736 * Sets the supported features (and whether they are active or not).
2737 *
2738 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).
2739 * @param fActive No idea what this is supposed to be, it's always 0 and
2740 * not references by this method.
2741 */
2742void Guest::setSupportedFeatures(uint32_t fCaps, uint32_t fActive)
2743{
2744 AutoCaller autoCaller(this);
2745 AssertComRCReturnVoid(autoCaller.rc());
2746
2747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
2749 mData.mSupportsSeamless = (fCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS);
2750 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
2751 mData.mSupportsGraphics = (fCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS);
2752}
2753/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette