VirtualBox

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

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

Guest Copy/CreateDirectory: Bugfixes, improved error handling.

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