VirtualBox

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

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

Automatic Guest Additions update/Main: One more process step.

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