VirtualBox

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

Last change on this file since 34331 was 34254, checked in by vboxsync, 14 years ago

Guest Additions Update/Main+Fe/Qt4: Don't show errors (also don't in log) when old Guest Additions can't be updated automatically or when Guest Additions are not installed on guest - just use the "old" .ISO mounting approach.

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

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