VirtualBox

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

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

Guest Additions update/Main: Retrieve installer status after run.

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