VirtualBox

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

Last change on this file since 34181 was 34172, checked in by vboxsync, 14 years ago

Main/GuestImpl: Improved error/cancel handling for automatic Guest Additions updates.

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