VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImpl.cpp@ 36344

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

Runtime/log: implement log rotation, adapt all code creating log files and make use of it in the webservice

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 297.2 KB
Line 
1/* $Id: ConsoleImpl.cpp 36344 2011-03-22 14:29:37Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @todo Move the TAP mess back into the driver! */
19#if defined(RT_OS_WINDOWS)
20#elif defined(RT_OS_LINUX)
21# include <errno.h>
22# include <sys/ioctl.h>
23# include <sys/poll.h>
24# include <sys/fcntl.h>
25# include <sys/types.h>
26# include <sys/wait.h>
27# include <net/if.h>
28# include <linux/if_tun.h>
29# include <stdio.h>
30# include <stdlib.h>
31# include <string.h>
32#elif defined(RT_OS_FREEBSD)
33# include <errno.h>
34# include <sys/ioctl.h>
35# include <sys/poll.h>
36# include <sys/fcntl.h>
37# include <sys/types.h>
38# include <sys/wait.h>
39# include <stdio.h>
40# include <stdlib.h>
41# include <string.h>
42#elif defined(RT_OS_SOLARIS)
43# include <iprt/coredumper.h>
44#endif
45
46#include "ConsoleImpl.h"
47
48#include "Global.h"
49#include "VirtualBoxErrorInfoImpl.h"
50#include "GuestImpl.h"
51#include "KeyboardImpl.h"
52#include "MouseImpl.h"
53#include "DisplayImpl.h"
54#include "MachineDebuggerImpl.h"
55#include "USBDeviceImpl.h"
56#include "RemoteUSBDeviceImpl.h"
57#include "SharedFolderImpl.h"
58#include "AudioSnifferInterface.h"
59#include "ProgressCombinedImpl.h"
60#include "ConsoleVRDPServer.h"
61#include "VMMDev.h"
62#include "package-generated.h"
63#ifdef VBOX_WITH_EXTPACK
64# include "ExtPackManagerImpl.h"
65#endif
66#include "BusAssignmentManager.h"
67
68// generated header
69#include "SchemaDefs.h"
70#include "VBoxEvents.h"
71#include "AutoCaller.h"
72#include "Logging.h"
73
74#include <VBox/com/array.h>
75#include "VBox/com/ErrorInfo.h"
76#include <VBox/com/listeners.h>
77
78#include <iprt/asm.h>
79#include <iprt/buildconfig.h>
80#include <iprt/cpp/utils.h>
81#include <iprt/dir.h>
82#include <iprt/file.h>
83#include <iprt/ldr.h>
84#include <iprt/path.h>
85#include <iprt/process.h>
86#include <iprt/string.h>
87#include <iprt/system.h>
88
89#include <VBox/vmm/vmapi.h>
90#include <VBox/vmm/vmm.h>
91#include <VBox/vmm/pdmapi.h>
92#include <VBox/vmm/pdmasynccompletion.h>
93#include <VBox/vmm/pdmnetifs.h>
94#ifdef VBOX_WITH_USB
95# include <VBox/vmm/pdmusb.h>
96#endif
97#include <VBox/vmm/mm.h>
98#include <VBox/vmm/ftm.h>
99#include <VBox/vmm/ssm.h>
100#include <VBox/err.h>
101#include <VBox/param.h>
102#include <VBox/vusb.h>
103#include <VBox/version.h>
104
105#include <VBox/VMMDev.h>
106
107#include <VBox/HostServices/VBoxClipboardSvc.h>
108#ifdef VBOX_WITH_GUEST_PROPS
109# include <VBox/HostServices/GuestPropertySvc.h>
110# include <VBox/com/array.h>
111#endif
112
113#include <set>
114#include <algorithm>
115#include <memory> // for auto_ptr
116#include <vector>
117#include <typeinfo>
118
119
120// VMTask and friends
121////////////////////////////////////////////////////////////////////////////////
122
123/**
124 * Task structure for asynchronous VM operations.
125 *
126 * Once created, the task structure adds itself as a Console caller. This means:
127 *
128 * 1. The user must check for #rc() before using the created structure
129 * (e.g. passing it as a thread function argument). If #rc() returns a
130 * failure, the Console object may not be used by the task (see
131 * Console::addCaller() for more details).
132 * 2. On successful initialization, the structure keeps the Console caller
133 * until destruction (to ensure Console remains in the Ready state and won't
134 * be accidentally uninitialized). Forgetting to delete the created task
135 * will lead to Console::uninit() stuck waiting for releasing all added
136 * callers.
137 *
138 * If \a aUsesVMPtr parameter is true, the task structure will also add itself
139 * as a Console::mpUVM caller with the same meaning as above. See
140 * Console::addVMCaller() for more info.
141 */
142struct VMTask
143{
144 VMTask(Console *aConsole,
145 Progress *aProgress,
146 const ComPtr<IProgress> &aServerProgress,
147 bool aUsesVMPtr)
148 : mConsole(aConsole),
149 mConsoleCaller(aConsole),
150 mProgress(aProgress),
151 mServerProgress(aServerProgress),
152 mpVM(NULL),
153 mRC(E_FAIL),
154 mpSafeVMPtr(NULL)
155 {
156 AssertReturnVoid(aConsole);
157 mRC = mConsoleCaller.rc();
158 if (FAILED(mRC))
159 return;
160 if (aUsesVMPtr)
161 {
162 mpSafeVMPtr = new Console::SafeVMPtr(aConsole);
163 if (mpSafeVMPtr->isOk())
164 mpVM = mpSafeVMPtr->raw();
165 else
166 mRC = mpSafeVMPtr->rc();
167 }
168 }
169
170 ~VMTask()
171 {
172 releaseVMCaller();
173 }
174
175 HRESULT rc() const { return mRC; }
176 bool isOk() const { return SUCCEEDED(rc()); }
177
178 /** Releases the VM caller before destruction. Not normally necessary. */
179 void releaseVMCaller()
180 {
181 if (mpSafeVMPtr)
182 {
183 delete mpSafeVMPtr;
184 mpSafeVMPtr = NULL;
185 }
186 }
187
188 const ComObjPtr<Console> mConsole;
189 AutoCaller mConsoleCaller;
190 const ComObjPtr<Progress> mProgress;
191 Utf8Str mErrorMsg;
192 const ComPtr<IProgress> mServerProgress;
193 PVM mpVM;
194
195private:
196 HRESULT mRC;
197 Console::SafeVMPtr *mpSafeVMPtr;
198};
199
200struct VMTakeSnapshotTask : public VMTask
201{
202 VMTakeSnapshotTask(Console *aConsole,
203 Progress *aProgress,
204 IN_BSTR aName,
205 IN_BSTR aDescription)
206 : VMTask(aConsole, aProgress, NULL /* aServerProgress */,
207 false /* aUsesVMPtr */),
208 bstrName(aName),
209 bstrDescription(aDescription),
210 lastMachineState(MachineState_Null)
211 {}
212
213 Bstr bstrName,
214 bstrDescription;
215 Bstr bstrSavedStateFile; // received from BeginTakeSnapshot()
216 MachineState_T lastMachineState;
217 bool fTakingSnapshotOnline;
218 ULONG ulMemSize;
219};
220
221struct VMPowerUpTask : public VMTask
222{
223 VMPowerUpTask(Console *aConsole,
224 Progress *aProgress)
225 : VMTask(aConsole, aProgress, NULL /* aServerProgress */,
226 false /* aUsesVMPtr */),
227 mConfigConstructor(NULL),
228 mStartPaused(false),
229 mTeleporterEnabled(FALSE),
230 mEnmFaultToleranceState(FaultToleranceState_Inactive)
231 {}
232
233 PFNCFGMCONSTRUCTOR mConfigConstructor;
234 Utf8Str mSavedStateFile;
235 Console::SharedFolderDataMap mSharedFolders;
236 bool mStartPaused;
237 BOOL mTeleporterEnabled;
238 FaultToleranceState_T mEnmFaultToleranceState;
239
240 /* array of progress objects for hard disk reset operations */
241 typedef std::list<ComPtr<IProgress> > ProgressList;
242 ProgressList hardDiskProgresses;
243};
244
245struct VMPowerDownTask : public VMTask
246{
247 VMPowerDownTask(Console *aConsole,
248 const ComPtr<IProgress> &aServerProgress)
249 : VMTask(aConsole, NULL /* aProgress */, aServerProgress,
250 true /* aUsesVMPtr */)
251 {}
252};
253
254struct VMSaveTask : public VMTask
255{
256 VMSaveTask(Console *aConsole,
257 const ComPtr<IProgress> &aServerProgress,
258 const Utf8Str &aSavedStateFile)
259 : VMTask(aConsole, NULL /* aProgress */, aServerProgress,
260 true /* aUsesVMPtr */),
261 mSavedStateFile(aSavedStateFile)
262 {}
263
264 Utf8Str mSavedStateFile;
265};
266
267// Handler for global events
268////////////////////////////////////////////////////////////////////////////////
269inline static const char *networkAdapterTypeToName(NetworkAdapterType_T adapterType);
270
271class VmEventListener {
272public:
273 VmEventListener()
274 {}
275
276
277 HRESULT init(Console *aConsole)
278 {
279 mConsole = aConsole;
280 return S_OK;
281 }
282
283 void uninit()
284 {
285 }
286
287 virtual ~VmEventListener()
288 {
289 }
290
291 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
292 {
293 switch(aType)
294 {
295 case VBoxEventType_OnNATRedirect:
296 {
297 Bstr id;
298 ComPtr<IMachine> pMachine = mConsole->machine();
299 ComPtr<INATRedirectEvent> pNREv = aEvent;
300 HRESULT rc = E_FAIL;
301 Assert(pNREv);
302
303 Bstr interestedId;
304 rc = pMachine->COMGETTER(Id)(interestedId.asOutParam());
305 AssertComRC(rc);
306 rc = pNREv->COMGETTER(MachineId)(id.asOutParam());
307 AssertComRC(rc);
308 if (id != interestedId)
309 break;
310 /* now we can operate with redirects */
311 NATProtocol_T proto;
312 pNREv->COMGETTER(Proto)(&proto);
313 BOOL fRemove;
314 pNREv->COMGETTER(Remove)(&fRemove);
315 bool fUdp = (proto == NATProtocol_UDP);
316 Bstr hostIp, guestIp;
317 LONG hostPort, guestPort;
318 pNREv->COMGETTER(HostIp)(hostIp.asOutParam());
319 pNREv->COMGETTER(HostPort)(&hostPort);
320 pNREv->COMGETTER(GuestIp)(guestIp.asOutParam());
321 pNREv->COMGETTER(GuestPort)(&guestPort);
322 ULONG ulSlot;
323 rc = pNREv->COMGETTER(Slot)(&ulSlot);
324 AssertComRC(rc);
325 if (FAILED(rc))
326 break;
327 mConsole->onNATRedirectRuleChange(ulSlot, fRemove, proto, hostIp.raw(), hostPort, guestIp.raw(), guestPort);
328 }
329 break;
330
331 case VBoxEventType_OnHostPciDevicePlug:
332 {
333 // handle if needed
334 break;
335 }
336
337 default:
338 AssertFailed();
339 }
340 return S_OK;
341 }
342private:
343 Console *mConsole;
344};
345
346typedef ListenerImpl<VmEventListener, Console*> VmEventListenerImpl;
347
348VBOX_LISTENER_DECLARE(VmEventListenerImpl)
349
350
351// constructor / destructor
352/////////////////////////////////////////////////////////////////////////////
353
354Console::Console()
355 : mSavedStateDataLoaded(false)
356 , mConsoleVRDPServer(NULL)
357 , mpUVM(NULL)
358 , mVMCallers(0)
359 , mVMZeroCallersSem(NIL_RTSEMEVENT)
360 , mVMDestroying(false)
361 , mVMPoweredOff(false)
362 , mVMIsAlreadyPoweringOff(false)
363 , mfSnapshotFolderSizeWarningShown(false)
364 , mfSnapshotFolderExt4WarningShown(false)
365 , mfSnapshotFolderDiskTypeShown(false)
366 , mpVmm2UserMethods(NULL)
367 , m_pVMMDev(NULL)
368 , mAudioSniffer(NULL)
369 , mBusMgr(NULL)
370 , mVMStateChangeCallbackDisabled(false)
371 , mMachineState(MachineState_PoweredOff)
372{
373 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; ++slot)
374 meAttachmentType[slot] = NetworkAttachmentType_Null;
375}
376
377Console::~Console()
378{}
379
380HRESULT Console::FinalConstruct()
381{
382 LogFlowThisFunc(("\n"));
383
384 memset(mapStorageLeds, 0, sizeof(mapStorageLeds));
385 memset(mapNetworkLeds, 0, sizeof(mapNetworkLeds));
386 memset(&mapUSBLed, 0, sizeof(mapUSBLed));
387 memset(&mapSharedFolderLed, 0, sizeof(mapSharedFolderLed));
388
389 for (unsigned i = 0; i < RT_ELEMENTS(maStorageDevType); ++ i)
390 maStorageDevType[i] = DeviceType_Null;
391
392 VMM2USERMETHODS *pVmm2UserMethods = (VMM2USERMETHODS *)RTMemAlloc(sizeof(*mpVmm2UserMethods) + sizeof(Console *));
393 if (!pVmm2UserMethods)
394 return E_OUTOFMEMORY;
395 pVmm2UserMethods->u32Magic = VMM2USERMETHODS_MAGIC;
396 pVmm2UserMethods->u32Version = VMM2USERMETHODS_VERSION;
397 pVmm2UserMethods->pfnSaveState = Console::vmm2User_SaveState;
398 pVmm2UserMethods->u32EndMagic = VMM2USERMETHODS_MAGIC;
399 *(Console **)(pVmm2UserMethods + 1) = this; /* lazy bird. */
400 mpVmm2UserMethods = pVmm2UserMethods;
401
402 return BaseFinalConstruct();
403}
404
405void Console::FinalRelease()
406{
407 LogFlowThisFunc(("\n"));
408
409 uninit();
410
411 BaseFinalRelease();
412}
413
414// public initializer/uninitializer for internal purposes only
415/////////////////////////////////////////////////////////////////////////////
416
417HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl)
418{
419 AssertReturn(aMachine && aControl, E_INVALIDARG);
420
421 /* Enclose the state transition NotReady->InInit->Ready */
422 AutoInitSpan autoInitSpan(this);
423 AssertReturn(autoInitSpan.isOk(), E_FAIL);
424
425 LogFlowThisFuncEnter();
426 LogFlowThisFunc(("aMachine=%p, aControl=%p\n", aMachine, aControl));
427
428 HRESULT rc = E_FAIL;
429
430 unconst(mMachine) = aMachine;
431 unconst(mControl) = aControl;
432
433 /* Cache essential properties and objects */
434
435 rc = mMachine->COMGETTER(State)(&mMachineState);
436 AssertComRCReturnRC(rc);
437
438 rc = mMachine->COMGETTER(VRDEServer)(unconst(mVRDEServer).asOutParam());
439 AssertComRCReturnRC(rc);
440
441 /* Create associated child COM objects */
442
443 // Event source may be needed by other children
444 unconst(mEventSource).createObject();
445 rc = mEventSource->init(static_cast<IConsole*>(this));
446 AssertComRCReturnRC(rc);
447
448 unconst(mGuest).createObject();
449 rc = mGuest->init(this);
450 AssertComRCReturnRC(rc);
451
452 unconst(mKeyboard).createObject();
453 rc = mKeyboard->init(this);
454 AssertComRCReturnRC(rc);
455
456 unconst(mMouse).createObject();
457 rc = mMouse->init(this);
458 AssertComRCReturnRC(rc);
459
460 unconst(mDisplay).createObject();
461 rc = mDisplay->init(this);
462 AssertComRCReturnRC(rc);
463
464 unconst(mVRDEServerInfo).createObject();
465 rc = mVRDEServerInfo->init(this);
466 AssertComRCReturnRC(rc);
467
468#ifdef VBOX_WITH_EXTPACK
469 unconst(mptrExtPackManager).createObject();
470 rc = mptrExtPackManager->initExtPackManager(NULL, VBOXEXTPACKCTX_VM_PROCESS);
471 AssertComRCReturnRC(rc);
472#endif
473
474 /* Grab global and machine shared folder lists */
475
476 rc = fetchSharedFolders(true /* aGlobal */);
477 AssertComRCReturnRC(rc);
478 rc = fetchSharedFolders(false /* aGlobal */);
479 AssertComRCReturnRC(rc);
480
481 /* Create other child objects */
482
483 unconst(mConsoleVRDPServer) = new ConsoleVRDPServer(this);
484 AssertReturn(mConsoleVRDPServer, E_FAIL);
485
486 mcAudioRefs = 0;
487 mcVRDPClients = 0;
488 mu32SingleRDPClientId = 0;
489 mcGuestCredentialsProvided = false;
490
491 // VirtualBox 4.0: We no longer initialize the VMMDev instance here,
492 // which starts the HGCM thread. Instead, this is now done in the
493 // power-up thread when a VM is actually being powered up to avoid
494 // having HGCM threads all over the place every time a session is
495 // opened, even if that session will not run a VM.
496 // unconst(m_pVMMDev) = new VMMDev(this);
497 // AssertReturn(mVMMDev, E_FAIL);
498
499 unconst(mAudioSniffer) = new AudioSniffer(this);
500 AssertReturn(mAudioSniffer, E_FAIL);
501
502 /* VirtualBox events registration. */
503 {
504 ComPtr<IVirtualBox> pVirtualBox;
505 rc = aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
506 AssertComRC(rc);
507
508 ComPtr<IEventSource> pES;
509 rc = pVirtualBox->COMGETTER(EventSource)(pES.asOutParam());
510 AssertComRC(rc);
511 ComObjPtr<VmEventListenerImpl> aVmListener;
512 aVmListener.createObject();
513 aVmListener->init(new VmEventListener(), this);
514 mVmListener = aVmListener;
515 com::SafeArray<VBoxEventType_T> eventTypes;
516 eventTypes.push_back(VBoxEventType_OnNATRedirect);
517 eventTypes.push_back(VBoxEventType_OnHostPciDevicePlug);
518 rc = pES->RegisterListener(aVmListener, ComSafeArrayAsInParam(eventTypes), true);
519 AssertComRC(rc);
520 }
521
522
523 /* Confirm a successful initialization when it's the case */
524 autoInitSpan.setSucceeded();
525
526#ifdef VBOX_WITH_EXTPACK
527 /* Let the extension packs have a go at things (hold no locks). */
528 if (SUCCEEDED(rc))
529 mptrExtPackManager->callAllConsoleReadyHooks(this);
530#endif
531
532 LogFlowThisFuncLeave();
533
534 return S_OK;
535}
536
537/**
538 * Uninitializes the Console object.
539 */
540void Console::uninit()
541{
542 LogFlowThisFuncEnter();
543
544 /* Enclose the state transition Ready->InUninit->NotReady */
545 AutoUninitSpan autoUninitSpan(this);
546 if (autoUninitSpan.uninitDone())
547 {
548 LogFlowThisFunc(("Already uninitialized.\n"));
549 LogFlowThisFuncLeave();
550 return;
551 }
552
553 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
554 if (mVmListener)
555 {
556 ComPtr<IEventSource> pES;
557 ComPtr<IVirtualBox> pVirtualBox;
558 HRESULT rc = mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
559 AssertComRC(rc);
560 if (SUCCEEDED(rc) && !pVirtualBox.isNull())
561 {
562 rc = pVirtualBox->COMGETTER(EventSource)(pES.asOutParam());
563 AssertComRC(rc);
564 if (!pES.isNull())
565 {
566 rc = pES->UnregisterListener(mVmListener);
567 AssertComRC(rc);
568 }
569 }
570 mVmListener.setNull();
571 }
572
573 /* power down the VM if necessary */
574 if (mpUVM)
575 {
576 powerDown();
577 Assert(mpUVM == NULL);
578 }
579
580 if (mVMZeroCallersSem != NIL_RTSEMEVENT)
581 {
582 RTSemEventDestroy(mVMZeroCallersSem);
583 mVMZeroCallersSem = NIL_RTSEMEVENT;
584 }
585
586 if (mpVmm2UserMethods)
587 {
588 RTMemFree((void *)mpVmm2UserMethods);
589 mpVmm2UserMethods = NULL;
590 }
591
592 if (mAudioSniffer)
593 {
594 delete mAudioSniffer;
595 unconst(mAudioSniffer) = NULL;
596 }
597
598 // if the VM had a VMMDev with an HGCM thread, then remove that here
599 if (m_pVMMDev)
600 {
601 delete m_pVMMDev;
602 unconst(m_pVMMDev) = NULL;
603 }
604
605 if (mBusMgr)
606 {
607 mBusMgr->Release();
608 mBusMgr = NULL;
609 }
610
611 m_mapGlobalSharedFolders.clear();
612 m_mapMachineSharedFolders.clear();
613 m_mapSharedFolders.clear(); // console instances
614
615 mRemoteUSBDevices.clear();
616 mUSBDevices.clear();
617
618 if (mVRDEServerInfo)
619 {
620 mVRDEServerInfo->uninit();
621 unconst(mVRDEServerInfo).setNull();;
622 }
623
624 if (mDebugger)
625 {
626 mDebugger->uninit();
627 unconst(mDebugger).setNull();
628 }
629
630 if (mDisplay)
631 {
632 mDisplay->uninit();
633 unconst(mDisplay).setNull();
634 }
635
636 if (mMouse)
637 {
638 mMouse->uninit();
639 unconst(mMouse).setNull();
640 }
641
642 if (mKeyboard)
643 {
644 mKeyboard->uninit();
645 unconst(mKeyboard).setNull();;
646 }
647
648 if (mGuest)
649 {
650 mGuest->uninit();
651 unconst(mGuest).setNull();;
652 }
653
654 if (mConsoleVRDPServer)
655 {
656 delete mConsoleVRDPServer;
657 unconst(mConsoleVRDPServer) = NULL;
658 }
659
660 unconst(mVRDEServer).setNull();
661
662 unconst(mControl).setNull();
663 unconst(mMachine).setNull();
664
665 // we don't perform uninit() as it's possible that some pending event refers to this source
666 unconst(mEventSource).setNull();
667
668 mCallbackData.clear();
669
670 LogFlowThisFuncLeave();
671}
672
673#ifdef VBOX_WITH_GUEST_PROPS
674
675/**
676 * Handles guest properties on a VM reset.
677 * At the moment we only delete properties which have the flag
678 * "TRANSRESET".
679 */
680void Console::guestPropertiesHandleVMReset(void)
681{
682 com::SafeArray<BSTR> arrNames;
683 com::SafeArray<BSTR> arrValues;
684 com::SafeArray<LONG64> arrTimestamps;
685 com::SafeArray<BSTR> arrFlags;
686 HRESULT hrc = enumerateGuestProperties(Bstr("*").raw(),
687 ComSafeArrayAsOutParam(arrNames),
688 ComSafeArrayAsOutParam(arrValues),
689 ComSafeArrayAsOutParam(arrTimestamps),
690 ComSafeArrayAsOutParam(arrFlags));
691 if (SUCCEEDED(hrc))
692 {
693 for (size_t i = 0; i < arrFlags.size(); i++)
694 {
695 /* Delete all properties which have the flag "TRANSRESET". */
696 if (Utf8Str(arrFlags[i]).contains("TRANSRESET", Utf8Str::CaseInsensitive))
697 {
698 hrc = mMachine->SetGuestProperty(arrNames[i], Bstr("").raw() /* Value */,
699 Bstr("").raw() /* Flags */);
700 if (FAILED(hrc))
701 LogRel(("RESET: Could not delete transient property \"%ls\", rc=%Rhrc\n",
702 arrNames[i], hrc));
703 }
704 }
705 }
706 else
707 LogRel(("RESET: Unable to enumerate guest properties, rc=%Rhrc\n", hrc));
708}
709
710bool Console::guestPropertiesVRDPEnabled(void)
711{
712 Bstr value;
713 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/EnableGuestPropertiesVRDP").raw(),
714 value.asOutParam());
715 if ( hrc == S_OK
716 && value == "1")
717 return true;
718 return false;
719}
720
721void Console::guestPropertiesVRDPUpdateLogon(uint32_t u32ClientId, const char *pszUser, const char *pszDomain)
722{
723 if (!guestPropertiesVRDPEnabled())
724 return;
725
726 char szPropNm[256];
727 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
728
729 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
730 Bstr clientName;
731 mVRDEServerInfo->COMGETTER(ClientName)(clientName.asOutParam());
732
733 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
734 clientName.raw(),
735 bstrReadOnlyGuest.raw());
736
737 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/User", u32ClientId);
738 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
739 Bstr(pszUser).raw(),
740 bstrReadOnlyGuest.raw());
741
742 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Domain", u32ClientId);
743 mMachine->SetGuestProperty(Bstr(szPropNm).raw(),
744 Bstr(pszDomain).raw(),
745 bstrReadOnlyGuest.raw());
746
747 char szClientId[64];
748 RTStrPrintf(szClientId, sizeof(szClientId), "%d", u32ClientId);
749 mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastConnectedClient").raw(),
750 Bstr(szClientId).raw(),
751 bstrReadOnlyGuest.raw());
752
753 return;
754}
755
756void Console::guestPropertiesVRDPUpdateDisconnect(uint32_t u32ClientId)
757{
758 if (!guestPropertiesVRDPEnabled())
759 return;
760
761 Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
762
763 char szPropNm[256];
764 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
765 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), Bstr("").raw(),
766 bstrReadOnlyGuest.raw());
767
768 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/User", u32ClientId);
769 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), Bstr("").raw(),
770 bstrReadOnlyGuest.raw());
771
772 RTStrPrintf(szPropNm, sizeof(szPropNm), "/VirtualBox/HostInfo/VRDP/Client/%u/Domain", u32ClientId);
773 mMachine->SetGuestProperty(Bstr(szPropNm).raw(), Bstr("").raw(),
774 bstrReadOnlyGuest.raw());
775
776 char szClientId[64];
777 RTStrPrintf(szClientId, sizeof(szClientId), "%d", u32ClientId);
778 mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastDisconnectedClient").raw(),
779 Bstr(szClientId).raw(),
780 bstrReadOnlyGuest.raw());
781
782 return;
783}
784
785#endif /* VBOX_WITH_GUEST_PROPS */
786
787#ifdef VBOX_WITH_EXTPACK
788/**
789 * Used by VRDEServer and others to talke to the extension pack manager.
790 *
791 * @returns The extension pack manager.
792 */
793ExtPackManager *Console::getExtPackManager()
794{
795 return mptrExtPackManager;
796}
797#endif
798
799
800int Console::VRDPClientLogon(uint32_t u32ClientId, const char *pszUser, const char *pszPassword, const char *pszDomain)
801{
802 LogFlowFuncEnter();
803 LogFlowFunc(("%d, %s, %s, %s\n", u32ClientId, pszUser, pszPassword, pszDomain));
804
805 AutoCaller autoCaller(this);
806 if (!autoCaller.isOk())
807 {
808 /* Console has been already uninitialized, deny request */
809 LogRel(("AUTH: Access denied (Console uninitialized).\n"));
810 LogFlowFuncLeave();
811 return VERR_ACCESS_DENIED;
812 }
813
814 Bstr id;
815 HRESULT hrc = mMachine->COMGETTER(Id)(id.asOutParam());
816 Guid uuid = Guid(id);
817
818 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
819
820 AuthType_T authType = AuthType_Null;
821 hrc = mVRDEServer->COMGETTER(AuthType)(&authType);
822 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
823
824 ULONG authTimeout = 0;
825 hrc = mVRDEServer->COMGETTER(AuthTimeout)(&authTimeout);
826 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
827
828 AuthResult result = AuthResultAccessDenied;
829 AuthGuestJudgement guestJudgement = AuthGuestNotAsked;
830
831 LogFlowFunc(("Auth type %d\n", authType));
832
833 LogRel(("AUTH: User: [%s]. Domain: [%s]. Authentication type: [%s]\n",
834 pszUser, pszDomain,
835 authType == AuthType_Null?
836 "Null":
837 (authType == AuthType_External?
838 "External":
839 (authType == AuthType_Guest?
840 "Guest":
841 "INVALID"
842 )
843 )
844 ));
845
846 switch (authType)
847 {
848 case AuthType_Null:
849 {
850 result = AuthResultAccessGranted;
851 break;
852 }
853
854 case AuthType_External:
855 {
856 /* Call the external library. */
857 result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
858
859 if (result != AuthResultDelegateToGuest)
860 {
861 break;
862 }
863
864 LogRel(("AUTH: Delegated to guest.\n"));
865
866 LogFlowFunc(("External auth asked for guest judgement\n"));
867 } /* pass through */
868
869 case AuthType_Guest:
870 {
871 guestJudgement = AuthGuestNotReacted;
872
873 // @todo r=dj locking required here for m_pVMMDev?
874 PPDMIVMMDEVPORT pDevPort;
875 if ( (m_pVMMDev)
876 && ((pDevPort = m_pVMMDev->getVMMDevPort()))
877 )
878 {
879 /* Issue the request to guest. Assume that the call does not require EMT. It should not. */
880
881 /* Ask the guest to judge these credentials. */
882 uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_JUDGE;
883
884 int rc = pDevPort->pfnSetCredentials(pDevPort, pszUser, pszPassword, pszDomain, u32GuestFlags);
885
886 if (RT_SUCCESS(rc))
887 {
888 /* Wait for guest. */
889 rc = m_pVMMDev->WaitCredentialsJudgement(authTimeout, &u32GuestFlags);
890
891 if (RT_SUCCESS(rc))
892 {
893 switch (u32GuestFlags & (VMMDEV_CREDENTIALS_JUDGE_OK | VMMDEV_CREDENTIALS_JUDGE_DENY | VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT))
894 {
895 case VMMDEV_CREDENTIALS_JUDGE_DENY: guestJudgement = AuthGuestAccessDenied; break;
896 case VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT: guestJudgement = AuthGuestNoJudgement; break;
897 case VMMDEV_CREDENTIALS_JUDGE_OK: guestJudgement = AuthGuestAccessGranted; break;
898 default:
899 LogFlowFunc(("Invalid guest flags %08X!!!\n", u32GuestFlags)); break;
900 }
901 }
902 else
903 {
904 LogFlowFunc(("Wait for credentials judgement rc = %Rrc!!!\n", rc));
905 }
906
907 LogFlowFunc(("Guest judgement %d\n", guestJudgement));
908 }
909 else
910 {
911 LogFlowFunc(("Could not set credentials rc = %Rrc!!!\n", rc));
912 }
913 }
914
915 if (authType == AuthType_External)
916 {
917 LogRel(("AUTH: Guest judgement %d.\n", guestJudgement));
918 LogFlowFunc(("External auth called again with guest judgement = %d\n", guestJudgement));
919 result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
920 }
921 else
922 {
923 switch (guestJudgement)
924 {
925 case AuthGuestAccessGranted:
926 result = AuthResultAccessGranted;
927 break;
928 default:
929 result = AuthResultAccessDenied;
930 break;
931 }
932 }
933 } break;
934
935 default:
936 AssertFailed();
937 }
938
939 LogFlowFunc(("Result = %d\n", result));
940 LogFlowFuncLeave();
941
942 if (result != AuthResultAccessGranted)
943 {
944 /* Reject. */
945 LogRel(("AUTH: Access denied.\n"));
946 return VERR_ACCESS_DENIED;
947 }
948
949 LogRel(("AUTH: Access granted.\n"));
950
951 /* Multiconnection check must be made after authentication, so bad clients would not interfere with a good one. */
952 BOOL allowMultiConnection = FALSE;
953 hrc = mVRDEServer->COMGETTER(AllowMultiConnection)(&allowMultiConnection);
954 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
955
956 BOOL reuseSingleConnection = FALSE;
957 hrc = mVRDEServer->COMGETTER(ReuseSingleConnection)(&reuseSingleConnection);
958 AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
959
960 LogFlowFunc(("allowMultiConnection %d, reuseSingleConnection = %d, mcVRDPClients = %d, mu32SingleRDPClientId = %d\n", allowMultiConnection, reuseSingleConnection, mcVRDPClients, mu32SingleRDPClientId));
961
962 if (allowMultiConnection == FALSE)
963 {
964 /* Note: the 'mcVRDPClients' variable is incremented in ClientConnect callback, which is called when the client
965 * is successfully connected, that is after the ClientLogon callback. Therefore the mcVRDPClients
966 * value is 0 for first client.
967 */
968 if (mcVRDPClients != 0)
969 {
970 Assert(mcVRDPClients == 1);
971 /* There is a client already.
972 * If required drop the existing client connection and let the connecting one in.
973 */
974 if (reuseSingleConnection)
975 {
976 LogRel(("AUTH: Multiple connections are not enabled. Disconnecting existing client.\n"));
977 mConsoleVRDPServer->DisconnectClient(mu32SingleRDPClientId, false);
978 }
979 else
980 {
981 /* Reject. */
982 LogRel(("AUTH: Multiple connections are not enabled. Access denied.\n"));
983 return VERR_ACCESS_DENIED;
984 }
985 }
986
987 /* Save the connected client id. From now on it will be necessary to disconnect this one. */
988 mu32SingleRDPClientId = u32ClientId;
989 }
990
991#ifdef VBOX_WITH_GUEST_PROPS
992 guestPropertiesVRDPUpdateLogon(u32ClientId, pszUser, pszDomain);
993#endif /* VBOX_WITH_GUEST_PROPS */
994
995 /* Check if the successfully verified credentials are to be sent to the guest. */
996 BOOL fProvideGuestCredentials = FALSE;
997
998 Bstr value;
999 hrc = mMachine->GetExtraData(Bstr("VRDP/ProvideGuestCredentials").raw(),
1000 value.asOutParam());
1001 if (SUCCEEDED(hrc) && value == "1")
1002 {
1003 /* Provide credentials only if there are no logged in users. */
1004 Bstr noLoggedInUsersValue;
1005 LONG64 ul64Timestamp = 0;
1006 Bstr flags;
1007
1008 hrc = getGuestProperty(Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers").raw(),
1009 noLoggedInUsersValue.asOutParam(), &ul64Timestamp, flags.asOutParam());
1010
1011 if (SUCCEEDED(hrc) && noLoggedInUsersValue != Bstr("false"))
1012 {
1013 /* And only if there are no connected clients. */
1014 if (ASMAtomicCmpXchgBool(&mcGuestCredentialsProvided, true, false))
1015 {
1016 fProvideGuestCredentials = TRUE;
1017 }
1018 }
1019 }
1020
1021 // @todo r=dj locking required here for m_pVMMDev?
1022 if ( fProvideGuestCredentials
1023 && m_pVMMDev)
1024 {
1025 uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
1026
1027 int rc = m_pVMMDev->getVMMDevPort()->pfnSetCredentials(m_pVMMDev->getVMMDevPort(),
1028 pszUser, pszPassword, pszDomain, u32GuestFlags);
1029 AssertRC(rc);
1030 }
1031
1032 return VINF_SUCCESS;
1033}
1034
1035void Console::VRDPClientConnect(uint32_t u32ClientId)
1036{
1037 LogFlowFuncEnter();
1038
1039 AutoCaller autoCaller(this);
1040 AssertComRCReturnVoid(autoCaller.rc());
1041
1042 uint32_t u32Clients = ASMAtomicIncU32(&mcVRDPClients);
1043 VMMDev *pDev;
1044 PPDMIVMMDEVPORT pPort;
1045 if ( (u32Clients == 1)
1046 && ((pDev = getVMMDev()))
1047 && ((pPort = pDev->getVMMDevPort()))
1048 )
1049 {
1050 pPort->pfnVRDPChange(pPort,
1051 true,
1052 VRDP_EXPERIENCE_LEVEL_FULL); // @todo configurable
1053 }
1054
1055 NOREF(u32ClientId);
1056 mDisplay->VideoAccelVRDP(true);
1057
1058 LogFlowFuncLeave();
1059 return;
1060}
1061
1062void Console::VRDPClientDisconnect(uint32_t u32ClientId,
1063 uint32_t fu32Intercepted)
1064{
1065 LogFlowFuncEnter();
1066
1067 AutoCaller autoCaller(this);
1068 AssertComRCReturnVoid(autoCaller.rc());
1069
1070 AssertReturnVoid(mConsoleVRDPServer);
1071
1072 uint32_t u32Clients = ASMAtomicDecU32(&mcVRDPClients);
1073 VMMDev *pDev;
1074 PPDMIVMMDEVPORT pPort;
1075
1076 if ( (u32Clients == 0)
1077 && ((pDev = getVMMDev()))
1078 && ((pPort = pDev->getVMMDevPort()))
1079 )
1080 {
1081 pPort->pfnVRDPChange(pPort,
1082 false,
1083 0);
1084 }
1085
1086 mDisplay->VideoAccelVRDP(false);
1087
1088 if (fu32Intercepted & VRDE_CLIENT_INTERCEPT_USB)
1089 {
1090 mConsoleVRDPServer->USBBackendDelete(u32ClientId);
1091 }
1092
1093 if (fu32Intercepted & VRDE_CLIENT_INTERCEPT_CLIPBOARD)
1094 {
1095 mConsoleVRDPServer->ClipboardDelete(u32ClientId);
1096 }
1097
1098 if (fu32Intercepted & VRDE_CLIENT_INTERCEPT_AUDIO)
1099 {
1100 mcAudioRefs--;
1101
1102 if (mcAudioRefs <= 0)
1103 {
1104 if (mAudioSniffer)
1105 {
1106 PPDMIAUDIOSNIFFERPORT port = mAudioSniffer->getAudioSnifferPort();
1107 if (port)
1108 {
1109 port->pfnSetup(port, false, false);
1110 }
1111 }
1112 }
1113 }
1114
1115 Bstr uuid;
1116 HRESULT hrc = mMachine->COMGETTER(Id)(uuid.asOutParam());
1117 AssertComRC(hrc);
1118
1119 AuthType_T authType = AuthType_Null;
1120 hrc = mVRDEServer->COMGETTER(AuthType)(&authType);
1121 AssertComRC(hrc);
1122
1123 if (authType == AuthType_External)
1124 mConsoleVRDPServer->AuthDisconnect(uuid, u32ClientId);
1125
1126#ifdef VBOX_WITH_GUEST_PROPS
1127 guestPropertiesVRDPUpdateDisconnect(u32ClientId);
1128#endif /* VBOX_WITH_GUEST_PROPS */
1129
1130 if (u32Clients == 0)
1131 mcGuestCredentialsProvided = false;
1132
1133 LogFlowFuncLeave();
1134 return;
1135}
1136
1137void Console::VRDPInterceptAudio(uint32_t u32ClientId)
1138{
1139 LogFlowFuncEnter();
1140
1141 AutoCaller autoCaller(this);
1142 AssertComRCReturnVoid(autoCaller.rc());
1143
1144 LogFlowFunc(("mAudioSniffer %p, u32ClientId %d.\n",
1145 mAudioSniffer, u32ClientId));
1146 NOREF(u32ClientId);
1147
1148 ++mcAudioRefs;
1149
1150 if (mcAudioRefs == 1)
1151 {
1152 if (mAudioSniffer)
1153 {
1154 PPDMIAUDIOSNIFFERPORT port = mAudioSniffer->getAudioSnifferPort();
1155 if (port)
1156 {
1157 port->pfnSetup(port, true, true);
1158 }
1159 }
1160 }
1161
1162 LogFlowFuncLeave();
1163 return;
1164}
1165
1166void Console::VRDPInterceptUSB(uint32_t u32ClientId, void **ppvIntercept)
1167{
1168 LogFlowFuncEnter();
1169
1170 AutoCaller autoCaller(this);
1171 AssertComRCReturnVoid(autoCaller.rc());
1172
1173 AssertReturnVoid(mConsoleVRDPServer);
1174
1175 mConsoleVRDPServer->USBBackendCreate(u32ClientId, ppvIntercept);
1176
1177 LogFlowFuncLeave();
1178 return;
1179}
1180
1181void Console::VRDPInterceptClipboard(uint32_t u32ClientId)
1182{
1183 LogFlowFuncEnter();
1184
1185 AutoCaller autoCaller(this);
1186 AssertComRCReturnVoid(autoCaller.rc());
1187
1188 AssertReturnVoid(mConsoleVRDPServer);
1189
1190 mConsoleVRDPServer->ClipboardCreate(u32ClientId);
1191
1192 LogFlowFuncLeave();
1193 return;
1194}
1195
1196
1197//static
1198const char *Console::sSSMConsoleUnit = "ConsoleData";
1199//static
1200uint32_t Console::sSSMConsoleVer = 0x00010001;
1201
1202inline static const char *networkAdapterTypeToName(NetworkAdapterType_T adapterType)
1203{
1204 switch (adapterType)
1205 {
1206 case NetworkAdapterType_Am79C970A:
1207 case NetworkAdapterType_Am79C973:
1208 return "pcnet";
1209#ifdef VBOX_WITH_E1000
1210 case NetworkAdapterType_I82540EM:
1211 case NetworkAdapterType_I82543GC:
1212 case NetworkAdapterType_I82545EM:
1213 return "e1000";
1214#endif
1215#ifdef VBOX_WITH_VIRTIO
1216 case NetworkAdapterType_Virtio:
1217 return "virtio-net";
1218#endif
1219 default:
1220 AssertFailed();
1221 return "unknown";
1222 }
1223 return NULL;
1224}
1225
1226/**
1227 * Loads various console data stored in the saved state file.
1228 * This method does validation of the state file and returns an error info
1229 * when appropriate.
1230 *
1231 * The method does nothing if the machine is not in the Saved file or if
1232 * console data from it has already been loaded.
1233 *
1234 * @note The caller must lock this object for writing.
1235 */
1236HRESULT Console::loadDataFromSavedState()
1237{
1238 if (mMachineState != MachineState_Saved || mSavedStateDataLoaded)
1239 return S_OK;
1240
1241 Bstr savedStateFile;
1242 HRESULT rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
1243 if (FAILED(rc))
1244 return rc;
1245
1246 PSSMHANDLE ssm;
1247 int vrc = SSMR3Open(Utf8Str(savedStateFile).c_str(), 0, &ssm);
1248 if (RT_SUCCESS(vrc))
1249 {
1250 uint32_t version = 0;
1251 vrc = SSMR3Seek(ssm, sSSMConsoleUnit, 0 /* iInstance */, &version);
1252 if (SSM_VERSION_MAJOR(version) == SSM_VERSION_MAJOR(sSSMConsoleVer))
1253 {
1254 if (RT_SUCCESS(vrc))
1255 vrc = loadStateFileExecInternal(ssm, version);
1256 else if (vrc == VERR_SSM_UNIT_NOT_FOUND)
1257 vrc = VINF_SUCCESS;
1258 }
1259 else
1260 vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1261
1262 SSMR3Close(ssm);
1263 }
1264
1265 if (RT_FAILURE(vrc))
1266 rc = setError(VBOX_E_FILE_ERROR,
1267 tr("The saved state file '%ls' is invalid (%Rrc). Delete the saved state and try again"),
1268 savedStateFile.raw(), vrc);
1269
1270 mSavedStateDataLoaded = true;
1271
1272 return rc;
1273}
1274
1275/**
1276 * Callback handler to save various console data to the state file,
1277 * called when the user saves the VM state.
1278 *
1279 * @param pvUser pointer to Console
1280 *
1281 * @note Locks the Console object for reading.
1282 */
1283//static
1284DECLCALLBACK(void)
1285Console::saveStateFileExec(PSSMHANDLE pSSM, void *pvUser)
1286{
1287 LogFlowFunc(("\n"));
1288
1289 Console *that = static_cast<Console *>(pvUser);
1290 AssertReturnVoid(that);
1291
1292 AutoCaller autoCaller(that);
1293 AssertComRCReturnVoid(autoCaller.rc());
1294
1295 AutoReadLock alock(that COMMA_LOCKVAL_SRC_POS);
1296
1297 int vrc = SSMR3PutU32(pSSM, (uint32_t)that->m_mapSharedFolders.size());
1298 AssertRC(vrc);
1299
1300 for (SharedFolderMap::const_iterator it = that->m_mapSharedFolders.begin();
1301 it != that->m_mapSharedFolders.end();
1302 ++ it)
1303 {
1304 SharedFolder *pSF = (*it).second;
1305 AutoCaller sfCaller(pSF);
1306 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
1307
1308 Utf8Str name = pSF->getName();
1309 vrc = SSMR3PutU32(pSSM, (uint32_t)name.length() + 1 /* term. 0 */);
1310 AssertRC(vrc);
1311 vrc = SSMR3PutStrZ(pSSM, name.c_str());
1312 AssertRC(vrc);
1313
1314 Utf8Str hostPath = pSF->getHostPath();
1315 vrc = SSMR3PutU32(pSSM, (uint32_t)hostPath.length() + 1 /* term. 0 */);
1316 AssertRC(vrc);
1317 vrc = SSMR3PutStrZ(pSSM, hostPath.c_str());
1318 AssertRC(vrc);
1319
1320 vrc = SSMR3PutBool(pSSM, !!pSF->isWritable());
1321 AssertRC(vrc);
1322
1323 vrc = SSMR3PutBool(pSSM, !!pSF->isAutoMounted());
1324 AssertRC(vrc);
1325 }
1326
1327 return;
1328}
1329
1330/**
1331 * Callback handler to load various console data from the state file.
1332 * Called when the VM is being restored from the saved state.
1333 *
1334 * @param pvUser pointer to Console
1335 * @param uVersion Console unit version.
1336 * Should match sSSMConsoleVer.
1337 * @param uPass The data pass.
1338 *
1339 * @note Should locks the Console object for writing, if necessary.
1340 */
1341//static
1342DECLCALLBACK(int)
1343Console::loadStateFileExec(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
1344{
1345 LogFlowFunc(("\n"));
1346
1347 if (SSM_VERSION_MAJOR_CHANGED(uVersion, sSSMConsoleVer))
1348 return VERR_VERSION_MISMATCH;
1349 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
1350
1351 Console *that = static_cast<Console *>(pvUser);
1352 AssertReturn(that, VERR_INVALID_PARAMETER);
1353
1354 /* Currently, nothing to do when we've been called from VMR3Load*. */
1355 return SSMR3SkipToEndOfUnit(pSSM);
1356}
1357
1358/**
1359 * Method to load various console data from the state file.
1360 * Called from #loadDataFromSavedState.
1361 *
1362 * @param pvUser pointer to Console
1363 * @param u32Version Console unit version.
1364 * Should match sSSMConsoleVer.
1365 *
1366 * @note Locks the Console object for writing.
1367 */
1368int
1369Console::loadStateFileExecInternal(PSSMHANDLE pSSM, uint32_t u32Version)
1370{
1371 AutoCaller autoCaller(this);
1372 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
1373
1374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1375
1376 AssertReturn(m_mapSharedFolders.size() == 0, VERR_INTERNAL_ERROR);
1377
1378 uint32_t size = 0;
1379 int vrc = SSMR3GetU32(pSSM, &size);
1380 AssertRCReturn(vrc, vrc);
1381
1382 for (uint32_t i = 0; i < size; ++ i)
1383 {
1384 Utf8Str strName;
1385 Utf8Str strHostPath;
1386 bool writable = true;
1387 bool autoMount = false;
1388
1389 uint32_t szBuf = 0;
1390 char *buf = NULL;
1391
1392 vrc = SSMR3GetU32(pSSM, &szBuf);
1393 AssertRCReturn(vrc, vrc);
1394 buf = new char[szBuf];
1395 vrc = SSMR3GetStrZ(pSSM, buf, szBuf);
1396 AssertRC(vrc);
1397 strName = buf;
1398 delete[] buf;
1399
1400 vrc = SSMR3GetU32(pSSM, &szBuf);
1401 AssertRCReturn(vrc, vrc);
1402 buf = new char[szBuf];
1403 vrc = SSMR3GetStrZ(pSSM, buf, szBuf);
1404 AssertRC(vrc);
1405 strHostPath = buf;
1406 delete[] buf;
1407
1408 if (u32Version > 0x00010000)
1409 SSMR3GetBool(pSSM, &writable);
1410
1411 if (u32Version > 0x00010000) // ???
1412 SSMR3GetBool(pSSM, &autoMount);
1413
1414 ComObjPtr<SharedFolder> pSharedFolder;
1415 pSharedFolder.createObject();
1416 HRESULT rc = pSharedFolder->init(this,
1417 strName,
1418 strHostPath,
1419 writable,
1420 autoMount,
1421 false /* fFailOnError */);
1422 AssertComRCReturn(rc, VERR_INTERNAL_ERROR);
1423
1424 m_mapSharedFolders.insert(std::make_pair(strName, pSharedFolder));
1425 }
1426
1427 return VINF_SUCCESS;
1428}
1429
1430#ifdef VBOX_WITH_GUEST_PROPS
1431
1432// static
1433DECLCALLBACK(int) Console::doGuestPropNotification(void *pvExtension,
1434 uint32_t u32Function,
1435 void *pvParms,
1436 uint32_t cbParms)
1437{
1438 using namespace guestProp;
1439
1440 Assert(u32Function == 0); NOREF(u32Function);
1441
1442 /*
1443 * No locking, as this is purely a notification which does not make any
1444 * changes to the object state.
1445 */
1446 PHOSTCALLBACKDATA pCBData = reinterpret_cast<PHOSTCALLBACKDATA>(pvParms);
1447 AssertReturn(sizeof(HOSTCALLBACKDATA) == cbParms, VERR_INVALID_PARAMETER);
1448 AssertReturn(HOSTCALLBACKMAGIC == pCBData->u32Magic, VERR_INVALID_PARAMETER);
1449 Log5(("Console::doGuestPropNotification: pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
1450 pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
1451
1452 int rc;
1453 Bstr name(pCBData->pcszName);
1454 Bstr value(pCBData->pcszValue);
1455 Bstr flags(pCBData->pcszFlags);
1456 ComObjPtr<Console> pConsole = reinterpret_cast<Console *>(pvExtension);
1457 HRESULT hrc = pConsole->mControl->PushGuestProperty(name.raw(),
1458 value.raw(),
1459 pCBData->u64Timestamp,
1460 flags.raw());
1461 if (SUCCEEDED(hrc))
1462 rc = VINF_SUCCESS;
1463 else
1464 {
1465 LogFunc(("Console::doGuestPropNotification: hrc=%Rhrc pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
1466 hrc, pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
1467 rc = Global::vboxStatusCodeFromCOM(hrc);
1468 }
1469 return rc;
1470}
1471
1472HRESULT Console::doEnumerateGuestProperties(CBSTR aPatterns,
1473 ComSafeArrayOut(BSTR, aNames),
1474 ComSafeArrayOut(BSTR, aValues),
1475 ComSafeArrayOut(LONG64, aTimestamps),
1476 ComSafeArrayOut(BSTR, aFlags))
1477{
1478 AssertReturn(m_pVMMDev, E_FAIL);
1479
1480 using namespace guestProp;
1481
1482 VBOXHGCMSVCPARM parm[3];
1483
1484 Utf8Str utf8Patterns(aPatterns);
1485 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
1486 parm[0].u.pointer.addr = (void*)utf8Patterns.c_str();
1487 parm[0].u.pointer.size = (uint32_t)utf8Patterns.length() + 1;
1488
1489 /*
1490 * Now things get slightly complicated. Due to a race with the guest adding
1491 * properties, there is no good way to know how much to enlarge a buffer for
1492 * the service to enumerate into. We choose a decent starting size and loop a
1493 * few times, each time retrying with the size suggested by the service plus
1494 * one Kb.
1495 */
1496 size_t cchBuf = 4096;
1497 Utf8Str Utf8Buf;
1498 int vrc = VERR_BUFFER_OVERFLOW;
1499 for (unsigned i = 0; i < 10 && (VERR_BUFFER_OVERFLOW == vrc); ++i)
1500 {
1501 try
1502 {
1503 Utf8Buf.reserve(cchBuf + 1024);
1504 }
1505 catch(...)
1506 {
1507 return E_OUTOFMEMORY;
1508 }
1509 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
1510 parm[1].u.pointer.addr = Utf8Buf.mutableRaw();
1511 parm[1].u.pointer.size = (uint32_t)cchBuf + 1024;
1512 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", ENUM_PROPS_HOST, 3,
1513 &parm[0]);
1514 Utf8Buf.jolt();
1515 if (parm[2].type != VBOX_HGCM_SVC_PARM_32BIT)
1516 return setError(E_FAIL, tr("Internal application error"));
1517 cchBuf = parm[2].u.uint32;
1518 }
1519 if (VERR_BUFFER_OVERFLOW == vrc)
1520 return setError(E_UNEXPECTED,
1521 tr("Temporary failure due to guest activity, please retry"));
1522
1523 /*
1524 * Finally we have to unpack the data returned by the service into the safe
1525 * arrays supplied by the caller. We start by counting the number of entries.
1526 */
1527 const char *pszBuf
1528 = reinterpret_cast<const char *>(parm[1].u.pointer.addr);
1529 unsigned cEntries = 0;
1530 /* The list is terminated by a zero-length string at the end of a set
1531 * of four strings. */
1532 for (size_t i = 0; strlen(pszBuf + i) != 0; )
1533 {
1534 /* We are counting sets of four strings. */
1535 for (unsigned j = 0; j < 4; ++j)
1536 i += strlen(pszBuf + i) + 1;
1537 ++cEntries;
1538 }
1539
1540 /*
1541 * And now we create the COM safe arrays and fill them in.
1542 */
1543 com::SafeArray<BSTR> names(cEntries);
1544 com::SafeArray<BSTR> values(cEntries);
1545 com::SafeArray<LONG64> timestamps(cEntries);
1546 com::SafeArray<BSTR> flags(cEntries);
1547 size_t iBuf = 0;
1548 /* Rely on the service to have formated the data correctly. */
1549 for (unsigned i = 0; i < cEntries; ++i)
1550 {
1551 size_t cchName = strlen(pszBuf + iBuf);
1552 Bstr(pszBuf + iBuf).detachTo(&names[i]);
1553 iBuf += cchName + 1;
1554 size_t cchValue = strlen(pszBuf + iBuf);
1555 Bstr(pszBuf + iBuf).detachTo(&values[i]);
1556 iBuf += cchValue + 1;
1557 size_t cchTimestamp = strlen(pszBuf + iBuf);
1558 timestamps[i] = RTStrToUInt64(pszBuf + iBuf);
1559 iBuf += cchTimestamp + 1;
1560 size_t cchFlags = strlen(pszBuf + iBuf);
1561 Bstr(pszBuf + iBuf).detachTo(&flags[i]);
1562 iBuf += cchFlags + 1;
1563 }
1564 names.detachTo(ComSafeArrayOutArg(aNames));
1565 values.detachTo(ComSafeArrayOutArg(aValues));
1566 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
1567 flags.detachTo(ComSafeArrayOutArg(aFlags));
1568 return S_OK;
1569}
1570
1571#endif /* VBOX_WITH_GUEST_PROPS */
1572
1573
1574// IConsole properties
1575/////////////////////////////////////////////////////////////////////////////
1576
1577STDMETHODIMP Console::COMGETTER(Machine)(IMachine **aMachine)
1578{
1579 CheckComArgOutPointerValid(aMachine);
1580
1581 AutoCaller autoCaller(this);
1582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1583
1584 /* mMachine is constant during life time, no need to lock */
1585 mMachine.queryInterfaceTo(aMachine);
1586
1587 /* callers expect to get a valid reference, better fail than crash them */
1588 if (mMachine.isNull())
1589 return E_FAIL;
1590
1591 return S_OK;
1592}
1593
1594STDMETHODIMP Console::COMGETTER(State)(MachineState_T *aMachineState)
1595{
1596 CheckComArgOutPointerValid(aMachineState);
1597
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600
1601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1602
1603 /* we return our local state (since it's always the same as on the server) */
1604 *aMachineState = mMachineState;
1605
1606 return S_OK;
1607}
1608
1609STDMETHODIMP Console::COMGETTER(Guest)(IGuest **aGuest)
1610{
1611 CheckComArgOutPointerValid(aGuest);
1612
1613 AutoCaller autoCaller(this);
1614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1615
1616 /* mGuest is constant during life time, no need to lock */
1617 mGuest.queryInterfaceTo(aGuest);
1618
1619 return S_OK;
1620}
1621
1622STDMETHODIMP Console::COMGETTER(Keyboard)(IKeyboard **aKeyboard)
1623{
1624 CheckComArgOutPointerValid(aKeyboard);
1625
1626 AutoCaller autoCaller(this);
1627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1628
1629 /* mKeyboard is constant during life time, no need to lock */
1630 mKeyboard.queryInterfaceTo(aKeyboard);
1631
1632 return S_OK;
1633}
1634
1635STDMETHODIMP Console::COMGETTER(Mouse)(IMouse **aMouse)
1636{
1637 CheckComArgOutPointerValid(aMouse);
1638
1639 AutoCaller autoCaller(this);
1640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1641
1642 /* mMouse is constant during life time, no need to lock */
1643 mMouse.queryInterfaceTo(aMouse);
1644
1645 return S_OK;
1646}
1647
1648STDMETHODIMP Console::COMGETTER(Display)(IDisplay **aDisplay)
1649{
1650 CheckComArgOutPointerValid(aDisplay);
1651
1652 AutoCaller autoCaller(this);
1653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1654
1655 /* mDisplay is constant during life time, no need to lock */
1656 mDisplay.queryInterfaceTo(aDisplay);
1657
1658 return S_OK;
1659}
1660
1661STDMETHODIMP Console::COMGETTER(Debugger)(IMachineDebugger **aDebugger)
1662{
1663 CheckComArgOutPointerValid(aDebugger);
1664
1665 AutoCaller autoCaller(this);
1666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1667
1668 /* we need a write lock because of the lazy mDebugger initialization*/
1669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 /* check if we have to create the debugger object */
1672 if (!mDebugger)
1673 {
1674 unconst(mDebugger).createObject();
1675 mDebugger->init(this);
1676 }
1677
1678 mDebugger.queryInterfaceTo(aDebugger);
1679
1680 return S_OK;
1681}
1682
1683STDMETHODIMP Console::COMGETTER(USBDevices)(ComSafeArrayOut(IUSBDevice *, aUSBDevices))
1684{
1685 CheckComArgOutSafeArrayPointerValid(aUSBDevices);
1686
1687 AutoCaller autoCaller(this);
1688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1689
1690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 SafeIfaceArray<IUSBDevice> collection(mUSBDevices);
1693 collection.detachTo(ComSafeArrayOutArg(aUSBDevices));
1694
1695 return S_OK;
1696}
1697
1698STDMETHODIMP Console::COMGETTER(RemoteUSBDevices)(ComSafeArrayOut(IHostUSBDevice *, aRemoteUSBDevices))
1699{
1700 CheckComArgOutSafeArrayPointerValid(aRemoteUSBDevices);
1701
1702 AutoCaller autoCaller(this);
1703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1704
1705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 SafeIfaceArray<IHostUSBDevice> collection(mRemoteUSBDevices);
1708 collection.detachTo(ComSafeArrayOutArg(aRemoteUSBDevices));
1709
1710 return S_OK;
1711}
1712
1713STDMETHODIMP Console::COMGETTER(VRDEServerInfo)(IVRDEServerInfo **aVRDEServerInfo)
1714{
1715 CheckComArgOutPointerValid(aVRDEServerInfo);
1716
1717 AutoCaller autoCaller(this);
1718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1719
1720 /* mDisplay is constant during life time, no need to lock */
1721 mVRDEServerInfo.queryInterfaceTo(aVRDEServerInfo);
1722
1723 return S_OK;
1724}
1725
1726STDMETHODIMP
1727Console::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1728{
1729 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1730
1731 AutoCaller autoCaller(this);
1732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1733
1734 /* loadDataFromSavedState() needs a write lock */
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 /* Read console data stored in the saved state file (if not yet done) */
1738 HRESULT rc = loadDataFromSavedState();
1739 if (FAILED(rc)) return rc;
1740
1741 SafeIfaceArray<ISharedFolder> sf(m_mapSharedFolders);
1742 sf.detachTo(ComSafeArrayOutArg(aSharedFolders));
1743
1744 return S_OK;
1745}
1746
1747
1748STDMETHODIMP Console::COMGETTER(EventSource)(IEventSource ** aEventSource)
1749{
1750 CheckComArgOutPointerValid(aEventSource);
1751
1752 AutoCaller autoCaller(this);
1753 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1754
1755 // no need to lock - lifetime constant
1756 mEventSource.queryInterfaceTo(aEventSource);
1757
1758 return S_OK;
1759}
1760
1761STDMETHODIMP Console::COMGETTER(AttachedPciDevices)(ComSafeArrayOut(IPciDeviceAttachment *, aAttachments))
1762{
1763 CheckComArgOutSafeArrayPointerValid(aAttachments);
1764
1765 AutoCaller autoCaller(this);
1766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1767
1768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1769
1770 if (mBusMgr)
1771 mBusMgr->listAttachedPciDevices(ComSafeArrayOutArg(aAttachments));
1772 else
1773 {
1774 com::SafeIfaceArray<IPciDeviceAttachment> result((size_t)0);
1775 result.detachTo(ComSafeArrayOutArg(aAttachments));
1776 }
1777
1778 return S_OK;
1779}
1780
1781// IConsole methods
1782/////////////////////////////////////////////////////////////////////////////
1783
1784
1785STDMETHODIMP Console::PowerUp(IProgress **aProgress)
1786{
1787 return powerUp(aProgress, false /* aPaused */);
1788}
1789
1790STDMETHODIMP Console::PowerUpPaused(IProgress **aProgress)
1791{
1792 return powerUp(aProgress, true /* aPaused */);
1793}
1794
1795STDMETHODIMP Console::PowerDown(IProgress **aProgress)
1796{
1797 LogFlowThisFuncEnter();
1798 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
1799
1800 CheckComArgOutPointerValid(aProgress);
1801
1802 AutoCaller autoCaller(this);
1803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1804
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 switch (mMachineState)
1808 {
1809 case MachineState_Running:
1810 case MachineState_Paused:
1811 case MachineState_Stuck:
1812 break;
1813
1814 /* Try cancel the teleportation. */
1815 case MachineState_Teleporting:
1816 case MachineState_TeleportingPausedVM:
1817 if (!mptrCancelableProgress.isNull())
1818 {
1819 HRESULT hrc = mptrCancelableProgress->Cancel();
1820 if (SUCCEEDED(hrc))
1821 break;
1822 }
1823 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a teleportation"));
1824
1825 /* Try cancel the live snapshot. */
1826 case MachineState_LiveSnapshotting:
1827 if (!mptrCancelableProgress.isNull())
1828 {
1829 HRESULT hrc = mptrCancelableProgress->Cancel();
1830 if (SUCCEEDED(hrc))
1831 break;
1832 }
1833 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a live snapshot"));
1834
1835 /* Try cancel the FT sync. */
1836 case MachineState_FaultTolerantSyncing:
1837 if (!mptrCancelableProgress.isNull())
1838 {
1839 HRESULT hrc = mptrCancelableProgress->Cancel();
1840 if (SUCCEEDED(hrc))
1841 break;
1842 }
1843 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a fault tolerant sync"));
1844
1845 /* extra nice error message for a common case */
1846 case MachineState_Saved:
1847 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down a saved virtual machine"));
1848 case MachineState_Stopping:
1849 return setError(VBOX_E_INVALID_VM_STATE, tr("The virtual machine is being powered down"));
1850 default:
1851 return setError(VBOX_E_INVALID_VM_STATE,
1852 tr("Invalid machine state: %s (must be Running, Paused or Stuck)"),
1853 Global::stringifyMachineState(mMachineState));
1854 }
1855
1856 LogFlowThisFunc(("Initiating SHUTDOWN request...\n"));
1857
1858 /* memorize the current machine state */
1859 MachineState_T lastMachineState = mMachineState;
1860
1861 HRESULT rc = S_OK;
1862 bool fBeganPowerDown = false;
1863
1864 do
1865 {
1866 ComPtr<IProgress> pProgress;
1867
1868 /*
1869 * request a progress object from the server
1870 * (this will set the machine state to Stopping on the server to block
1871 * others from accessing this machine)
1872 */
1873 rc = mControl->BeginPoweringDown(pProgress.asOutParam());
1874 if (FAILED(rc))
1875 break;
1876
1877 fBeganPowerDown = true;
1878
1879 /* sync the state with the server */
1880 setMachineStateLocally(MachineState_Stopping);
1881
1882 /* setup task object and thread to carry out the operation asynchronously */
1883 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(this, pProgress));
1884 AssertBreakStmt(task->isOk(), rc = E_FAIL);
1885
1886 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
1887 (void *) task.get(), 0,
1888 RTTHREADTYPE_MAIN_WORKER, 0,
1889 "VMPowerDown");
1890 if (RT_FAILURE(vrc))
1891 {
1892 rc = setError(E_FAIL, "Could not create VMPowerDown thread (%Rrc)", vrc);
1893 break;
1894 }
1895
1896 /* task is now owned by powerDownThread(), so release it */
1897 task.release();
1898
1899 /* pass the progress to the caller */
1900 pProgress.queryInterfaceTo(aProgress);
1901 }
1902 while (0);
1903
1904 if (FAILED(rc))
1905 {
1906 /* preserve existing error info */
1907 ErrorInfoKeeper eik;
1908
1909 if (fBeganPowerDown)
1910 {
1911 /*
1912 * cancel the requested power down procedure.
1913 * This will reset the machine state to the state it had right
1914 * before calling mControl->BeginPoweringDown().
1915 */
1916 mControl->EndPoweringDown(eik.getResultCode(), eik.getText().raw()); }
1917
1918 setMachineStateLocally(lastMachineState);
1919 }
1920
1921 LogFlowThisFunc(("rc=%Rhrc\n", rc));
1922 LogFlowThisFuncLeave();
1923
1924 return rc;
1925}
1926
1927STDMETHODIMP Console::Reset()
1928{
1929 LogFlowThisFuncEnter();
1930 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
1931
1932 AutoCaller autoCaller(this);
1933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1934
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 if ( mMachineState != MachineState_Running
1938 && mMachineState != MachineState_Teleporting
1939 && mMachineState != MachineState_LiveSnapshotting
1940 /** @todo r=bird: This should be allowed on paused VMs as well. Later. */
1941 )
1942 return setInvalidMachineStateError();
1943
1944 /* protect mpUVM */
1945 SafeVMPtr ptrVM(this);
1946 if (!ptrVM.isOk())
1947 return ptrVM.rc();
1948
1949 /* leave the lock before a VMR3* call (EMT will call us back)! */
1950 alock.leave();
1951
1952 int vrc = VMR3Reset(ptrVM);
1953
1954 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
1955 setError(VBOX_E_VM_ERROR,
1956 tr("Could not reset the machine (%Rrc)"),
1957 vrc);
1958
1959 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
1960 LogFlowThisFuncLeave();
1961 return rc;
1962}
1963
1964/*static*/ DECLCALLBACK(int) Console::unplugCpu(Console *pThis, PVM pVM, unsigned uCpu)
1965{
1966 LogFlowFunc(("pThis=%p pVM=%p uCpu=%u\n", pThis, pVM, uCpu));
1967
1968 AssertReturn(pThis, VERR_INVALID_PARAMETER);
1969
1970 int vrc = PDMR3DeviceDetach(pVM, "acpi", 0, uCpu, 0);
1971 Log(("UnplugCpu: rc=%Rrc\n", vrc));
1972
1973 return vrc;
1974}
1975
1976HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM)
1977{
1978 HRESULT rc = S_OK;
1979
1980 LogFlowThisFuncEnter();
1981 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
1982
1983 AutoCaller autoCaller(this);
1984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1985
1986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 AssertReturn(m_pVMMDev, E_FAIL);
1989 PPDMIVMMDEVPORT pVmmDevPort = m_pVMMDev->getVMMDevPort();
1990 AssertReturn(pVmmDevPort, E_FAIL);
1991
1992 if ( mMachineState != MachineState_Running
1993 && mMachineState != MachineState_Teleporting
1994 && mMachineState != MachineState_LiveSnapshotting
1995 )
1996 return setInvalidMachineStateError();
1997
1998 /* Check if the CPU is present */
1999 BOOL fCpuAttached;
2000 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
2001 if (FAILED(rc))
2002 return rc;
2003 if (!fCpuAttached)
2004 return setError(E_FAIL, tr("CPU %d is not attached"), aCpu);
2005
2006 /* Leave the lock before any EMT/VMMDev call. */
2007 alock.release();
2008 bool fLocked = true;
2009
2010 /* Check if the CPU is unlocked */
2011 PPDMIBASE pBase;
2012 int vrc = PDMR3QueryDeviceLun(pVM, "acpi", 0, aCpu, &pBase);
2013 if (RT_SUCCESS(vrc))
2014 {
2015 Assert(pBase);
2016 PPDMIACPIPORT pApicPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2017
2018 /* Notify the guest if possible. */
2019 uint32_t idCpuCore, idCpuPackage;
2020 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
2021 if (RT_SUCCESS(vrc))
2022 vrc = pVmmDevPort->pfnCpuHotUnplug(pVmmDevPort, idCpuCore, idCpuPackage);
2023 if (RT_SUCCESS(vrc))
2024 {
2025 unsigned cTries = 100;
2026 do
2027 {
2028 /* It will take some time until the event is processed in the guest. Wait... */
2029 vrc = pApicPort ? pApicPort->pfnGetCpuStatus(pApicPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
2030 if (RT_SUCCESS(vrc) && !fLocked)
2031 break;
2032
2033 /* Sleep a bit */
2034 RTThreadSleep(100);
2035 } while (cTries-- > 0);
2036 }
2037 else if (vrc == VERR_CPU_HOTPLUG_NOT_MONITORED_BY_GUEST)
2038 {
2039 /* Query one time. It is possible that the user ejected the CPU. */
2040 vrc = pApicPort ? pApicPort->pfnGetCpuStatus(pApicPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
2041 }
2042 }
2043
2044 /* If the CPU was unlocked we can detach it now. */
2045 if (RT_SUCCESS(vrc) && !fLocked)
2046 {
2047 /*
2048 * Call worker in EMT, that's faster and safer than doing everything
2049 * using VMR3ReqCall.
2050 */
2051 PVMREQ pReq;
2052 vrc = VMR3ReqCall(pVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
2053 (PFNRT)Console::unplugCpu, 3,
2054 this, pVM, aCpu);
2055 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
2056 {
2057 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
2058 AssertRC(vrc);
2059 if (RT_SUCCESS(vrc))
2060 vrc = pReq->iStatus;
2061 }
2062 VMR3ReqFree(pReq);
2063
2064 if (RT_SUCCESS(vrc))
2065 {
2066 /* Detach it from the VM */
2067 vrc = VMR3HotUnplugCpu(pVM, aCpu);
2068 AssertRC(vrc);
2069 }
2070 else
2071 rc = setError(VBOX_E_VM_ERROR,
2072 tr("Hot-Remove failed (rc=%Rrc)"), vrc);
2073 }
2074 else
2075 rc = setError(VBOX_E_VM_ERROR,
2076 tr("Hot-Remove was aborted because the CPU may still be used by the guest"), VERR_RESOURCE_BUSY);
2077
2078 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
2079 LogFlowThisFuncLeave();
2080 return rc;
2081}
2082
2083/*static*/ DECLCALLBACK(int) Console::plugCpu(Console *pThis, PVM pVM, unsigned uCpu)
2084{
2085 LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
2086
2087 AssertReturn(pThis, VERR_INVALID_PARAMETER);
2088
2089 int rc = VMR3HotPlugCpu(pVM, uCpu);
2090 AssertRC(rc);
2091
2092 PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pVM), "Devices/acpi/0/");
2093 AssertRelease(pInst);
2094 /* nuke anything which might have been left behind. */
2095 CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu));
2096
2097#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
2098
2099 PCFGMNODE pLunL0;
2100 PCFGMNODE pCfg;
2101 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", uCpu); RC_CHECK();
2102 rc = CFGMR3InsertString(pLunL0, "Driver", "ACPICpu"); RC_CHECK();
2103 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
2104
2105 /*
2106 * Attach the driver.
2107 */
2108 PPDMIBASE pBase;
2109 rc = PDMR3DeviceAttach(pVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK();
2110
2111 Log(("PlugCpu: rc=%Rrc\n", rc));
2112
2113 CFGMR3Dump(pInst);
2114
2115#undef RC_CHECK
2116
2117 return VINF_SUCCESS;
2118}
2119
2120HRESULT Console::doCPUAdd(ULONG aCpu, PVM pVM)
2121{
2122 HRESULT rc = S_OK;
2123
2124 LogFlowThisFuncEnter();
2125 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2126
2127 AutoCaller autoCaller(this);
2128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2129
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 if ( mMachineState != MachineState_Running
2133 && mMachineState != MachineState_Teleporting
2134 && mMachineState != MachineState_LiveSnapshotting
2135 /** @todo r=bird: This should be allowed on paused VMs as well. Later. */
2136 )
2137 return setInvalidMachineStateError();
2138
2139 AssertReturn(m_pVMMDev, E_FAIL);
2140 PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
2141 AssertReturn(pDevPort, E_FAIL);
2142
2143 /* Check if the CPU is present */
2144 BOOL fCpuAttached;
2145 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
2146 if (FAILED(rc)) return rc;
2147
2148 if (fCpuAttached)
2149 return setError(E_FAIL,
2150 tr("CPU %d is already attached"), aCpu);
2151
2152 /*
2153 * Call worker in EMT, that's faster and safer than doing everything
2154 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
2155 * here to make requests from under the lock in order to serialize them.
2156 */
2157 PVMREQ pReq;
2158 int vrc = VMR3ReqCall(pVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
2159 (PFNRT)Console::plugCpu, 3,
2160 this, pVM, aCpu);
2161
2162 /* leave the lock before a VMR3* call (EMT will call us back)! */
2163 alock.release();
2164
2165 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
2166 {
2167 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
2168 AssertRC(vrc);
2169 if (RT_SUCCESS(vrc))
2170 vrc = pReq->iStatus;
2171 }
2172 VMR3ReqFree(pReq);
2173
2174 rc = RT_SUCCESS(vrc) ? S_OK :
2175 setError(VBOX_E_VM_ERROR,
2176 tr("Could not add CPU to the machine (%Rrc)"),
2177 vrc);
2178
2179 if (RT_SUCCESS(vrc))
2180 {
2181 /* Notify the guest if possible. */
2182 uint32_t idCpuCore, idCpuPackage;
2183 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc);
2184 if (RT_SUCCESS(vrc))
2185 vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage);
2186 /** @todo warning if the guest doesn't support it */
2187 }
2188
2189 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
2190 LogFlowThisFuncLeave();
2191 return rc;
2192}
2193
2194STDMETHODIMP Console::Pause()
2195{
2196 LogFlowThisFuncEnter();
2197
2198 AutoCaller autoCaller(this);
2199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2200
2201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2202
2203 switch (mMachineState)
2204 {
2205 case MachineState_Running:
2206 case MachineState_Teleporting:
2207 case MachineState_LiveSnapshotting:
2208 break;
2209
2210 case MachineState_Paused:
2211 case MachineState_TeleportingPausedVM:
2212 case MachineState_Saving:
2213 return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
2214
2215 default:
2216 return setInvalidMachineStateError();
2217 }
2218
2219 /* get the VM handle. */
2220 SafeVMPtr ptrVM(this);
2221 if (!ptrVM.isOk())
2222 return ptrVM.rc();
2223
2224 LogFlowThisFunc(("Sending PAUSE request...\n"));
2225
2226 /* leave the lock before a VMR3* call (EMT will call us back)! */
2227 alock.leave();
2228
2229 int vrc = VMR3Suspend(ptrVM);
2230
2231 HRESULT hrc = S_OK;
2232 if (RT_FAILURE(vrc))
2233 hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
2234
2235 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
2236 LogFlowThisFuncLeave();
2237 return hrc;
2238}
2239
2240STDMETHODIMP Console::Resume()
2241{
2242 LogFlowThisFuncEnter();
2243
2244 AutoCaller autoCaller(this);
2245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2246
2247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2248
2249 if (mMachineState != MachineState_Paused)
2250 return setError(VBOX_E_INVALID_VM_STATE,
2251 tr("Cannot resume the machine as it is not paused (machine state: %s)"),
2252 Global::stringifyMachineState(mMachineState));
2253
2254 /* get the VM handle. */
2255 SafeVMPtr ptrVM(this);
2256 if (!ptrVM.isOk())
2257 return ptrVM.rc();
2258
2259 LogFlowThisFunc(("Sending RESUME request...\n"));
2260
2261 /* leave the lock before a VMR3* call (EMT will call us back)! */
2262 alock.leave();
2263
2264#ifdef VBOX_WITH_EXTPACK
2265 int vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, ptrVM); /** @todo called a few times too many... */
2266#else
2267 int vrc = VINF_SUCCESS;
2268#endif
2269 if (RT_SUCCESS(vrc))
2270 {
2271 if (VMR3GetState(ptrVM) == VMSTATE_CREATED)
2272 vrc = VMR3PowerOn(ptrVM); /* (PowerUpPaused) */
2273 else
2274 vrc = VMR3Resume(ptrVM);
2275 }
2276
2277 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2278 setError(VBOX_E_VM_ERROR,
2279 tr("Could not resume the machine execution (%Rrc)"),
2280 vrc);
2281
2282 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2283 LogFlowThisFuncLeave();
2284 return rc;
2285}
2286
2287STDMETHODIMP Console::PowerButton()
2288{
2289 LogFlowThisFuncEnter();
2290
2291 AutoCaller autoCaller(this);
2292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2293
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 if ( mMachineState != MachineState_Running
2297 && mMachineState != MachineState_Teleporting
2298 && mMachineState != MachineState_LiveSnapshotting
2299 )
2300 return setInvalidMachineStateError();
2301
2302 /* get the VM handle. */
2303 SafeVMPtr ptrVM(this);
2304 if (!ptrVM.isOk())
2305 return ptrVM.rc();
2306/** @todo leave the console lock? */
2307
2308 /* get the acpi device interface and press the button. */
2309 PPDMIBASE pBase;
2310 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2311 if (RT_SUCCESS(vrc))
2312 {
2313 Assert(pBase);
2314 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2315 if (pPort)
2316 vrc = pPort->pfnPowerButtonPress(pPort);
2317 else
2318 vrc = VERR_PDM_MISSING_INTERFACE;
2319 }
2320
2321 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2322 setError(VBOX_E_PDM_ERROR,
2323 tr("Controlled power off failed (%Rrc)"),
2324 vrc);
2325
2326 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2327 LogFlowThisFuncLeave();
2328 return rc;
2329}
2330
2331STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
2332{
2333 LogFlowThisFuncEnter();
2334
2335 CheckComArgOutPointerValid(aHandled);
2336
2337 *aHandled = FALSE;
2338
2339 AutoCaller autoCaller(this);
2340
2341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 if ( mMachineState != MachineState_Running
2344 && mMachineState != MachineState_Teleporting
2345 && mMachineState != MachineState_LiveSnapshotting
2346 )
2347 return setInvalidMachineStateError();
2348
2349 /* get the VM handle. */
2350 SafeVMPtr ptrVM(this);
2351 if (!ptrVM.isOk())
2352 return ptrVM.rc();
2353/** @todo leave the console lock? */
2354
2355 /* get the acpi device interface and check if the button press was handled. */
2356 PPDMIBASE pBase;
2357 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2358 if (RT_SUCCESS(vrc))
2359 {
2360 Assert(pBase);
2361 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2362 if (pPort)
2363 {
2364 bool fHandled = false;
2365 vrc = pPort->pfnGetPowerButtonHandled(pPort, &fHandled);
2366 if (RT_SUCCESS(vrc))
2367 *aHandled = fHandled;
2368 }
2369 else
2370 vrc = VERR_PDM_MISSING_INTERFACE;
2371 }
2372
2373 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2374 setError(VBOX_E_PDM_ERROR,
2375 tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"),
2376 vrc);
2377
2378 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2379 LogFlowThisFuncLeave();
2380 return rc;
2381}
2382
2383STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
2384{
2385 LogFlowThisFuncEnter();
2386
2387 CheckComArgOutPointerValid(aEntered);
2388
2389 *aEntered = FALSE;
2390
2391 AutoCaller autoCaller(this);
2392
2393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2394
2395 if ( mMachineState != MachineState_Running
2396 && mMachineState != MachineState_Teleporting
2397 && mMachineState != MachineState_LiveSnapshotting
2398 )
2399 return setError(VBOX_E_INVALID_VM_STATE,
2400 tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"),
2401 Global::stringifyMachineState(mMachineState));
2402
2403 /* get the VM handle. */
2404 SafeVMPtr ptrVM(this);
2405 if (!ptrVM.isOk())
2406 return ptrVM.rc();
2407
2408/** @todo leave the console lock? */
2409
2410 /* get the acpi device interface and query the information. */
2411 PPDMIBASE pBase;
2412 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2413 if (RT_SUCCESS(vrc))
2414 {
2415 Assert(pBase);
2416 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2417 if (pPort)
2418 {
2419 bool fEntered = false;
2420 vrc = pPort->pfnGetGuestEnteredACPIMode(pPort, &fEntered);
2421 if (RT_SUCCESS(vrc))
2422 *aEntered = fEntered;
2423 }
2424 else
2425 vrc = VERR_PDM_MISSING_INTERFACE;
2426 }
2427
2428 LogFlowThisFuncLeave();
2429 return S_OK;
2430}
2431
2432STDMETHODIMP Console::SleepButton()
2433{
2434 LogFlowThisFuncEnter();
2435
2436 AutoCaller autoCaller(this);
2437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2438
2439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2440
2441 if (mMachineState != MachineState_Running) /** @todo Live Migration: ??? */
2442 return setInvalidMachineStateError();
2443
2444 /* get the VM handle. */
2445 SafeVMPtr ptrVM(this);
2446 if (!ptrVM.isOk())
2447 return ptrVM.rc();
2448
2449/** @todo leave the console lock? */
2450
2451 /* get the acpi device interface and press the sleep button. */
2452 PPDMIBASE pBase;
2453 int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase);
2454 if (RT_SUCCESS(vrc))
2455 {
2456 Assert(pBase);
2457 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2458 if (pPort)
2459 vrc = pPort->pfnSleepButtonPress(pPort);
2460 else
2461 vrc = VERR_PDM_MISSING_INTERFACE;
2462 }
2463
2464 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2465 setError(VBOX_E_PDM_ERROR,
2466 tr("Sending sleep button event failed (%Rrc)"),
2467 vrc);
2468
2469 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2470 LogFlowThisFuncLeave();
2471 return rc;
2472}
2473
2474STDMETHODIMP Console::SaveState(IProgress **aProgress)
2475{
2476 LogFlowThisFuncEnter();
2477 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2478
2479 CheckComArgOutPointerValid(aProgress);
2480
2481 AutoCaller autoCaller(this);
2482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2483
2484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 if ( mMachineState != MachineState_Running
2487 && mMachineState != MachineState_Paused)
2488 {
2489 return setError(VBOX_E_INVALID_VM_STATE,
2490 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
2491 Global::stringifyMachineState(mMachineState));
2492 }
2493
2494 /* memorize the current machine state */
2495 MachineState_T lastMachineState = mMachineState;
2496
2497 if (mMachineState == MachineState_Running)
2498 {
2499 HRESULT rc = Pause();
2500 if (FAILED(rc))
2501 return rc;
2502 }
2503
2504 HRESULT rc = S_OK;
2505 bool fBeganSavingState = false;
2506 bool fTaskCreationFailed = false;
2507
2508 do
2509 {
2510 ComPtr<IProgress> pProgress;
2511 Bstr stateFilePath;
2512
2513 /*
2514 * request a saved state file path from the server
2515 * (this will set the machine state to Saving on the server to block
2516 * others from accessing this machine)
2517 */
2518 rc = mControl->BeginSavingState(pProgress.asOutParam(),
2519 stateFilePath.asOutParam());
2520 if (FAILED(rc))
2521 break;
2522
2523 fBeganSavingState = true;
2524
2525 /* sync the state with the server */
2526 setMachineStateLocally(MachineState_Saving);
2527
2528 /* ensure the directory for the saved state file exists */
2529 {
2530 Utf8Str dir = stateFilePath;
2531 dir.stripFilename();
2532 if (!RTDirExists(dir.c_str()))
2533 {
2534 int vrc = RTDirCreateFullPath(dir.c_str(), 0777);
2535 if (RT_FAILURE(vrc))
2536 {
2537 rc = setError(VBOX_E_FILE_ERROR,
2538 tr("Could not create a directory '%s' to save the state to (%Rrc)"),
2539 dir.c_str(), vrc);
2540 break;
2541 }
2542 }
2543 }
2544
2545 /* create a task object early to ensure mpVM protection is successful */
2546 std::auto_ptr<VMSaveTask> task(new VMSaveTask(this, pProgress,
2547 stateFilePath));
2548 rc = task->rc();
2549 /*
2550 * If we fail here it means a PowerDown() call happened on another
2551 * thread while we were doing Pause() (which leaves the Console lock).
2552 * We assign PowerDown() a higher precedence than SaveState(),
2553 * therefore just return the error to the caller.
2554 */
2555 if (FAILED(rc))
2556 {
2557 fTaskCreationFailed = true;
2558 break;
2559 }
2560
2561 /* create a thread to wait until the VM state is saved */
2562 int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *)task.get(),
2563 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
2564 if (RT_FAILURE(vrc))
2565 {
2566 rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
2567 break;
2568 }
2569
2570 /* task is now owned by saveStateThread(), so release it */
2571 task.release();
2572
2573 /* return the progress to the caller */
2574 pProgress.queryInterfaceTo(aProgress);
2575 } while (0);
2576
2577 if (FAILED(rc) && !fTaskCreationFailed)
2578 {
2579 /* preserve existing error info */
2580 ErrorInfoKeeper eik;
2581
2582 if (fBeganSavingState)
2583 {
2584 /*
2585 * cancel the requested save state procedure.
2586 * This will reset the machine state to the state it had right
2587 * before calling mControl->BeginSavingState().
2588 */
2589 mControl->EndSavingState(eik.getResultCode(), eik.getText().raw());
2590 }
2591
2592 if (lastMachineState == MachineState_Running)
2593 {
2594 /* restore the paused state if appropriate */
2595 setMachineStateLocally(MachineState_Paused);
2596 /* restore the running state if appropriate */
2597 Resume();
2598 }
2599 else
2600 setMachineStateLocally(lastMachineState);
2601 }
2602
2603 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2604 LogFlowThisFuncLeave();
2605 return rc;
2606}
2607
2608STDMETHODIMP Console::AdoptSavedState(IN_BSTR aSavedStateFile)
2609{
2610 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
2611
2612 AutoCaller autoCaller(this);
2613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2614
2615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 if ( mMachineState != MachineState_PoweredOff
2618 && mMachineState != MachineState_Teleported
2619 && mMachineState != MachineState_Aborted
2620 )
2621 return setError(VBOX_E_INVALID_VM_STATE,
2622 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
2623 Global::stringifyMachineState(mMachineState));
2624
2625 return mControl->AdoptSavedState(aSavedStateFile);
2626}
2627
2628STDMETHODIMP Console::DiscardSavedState(BOOL aRemoveFile)
2629{
2630 AutoCaller autoCaller(this);
2631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2632
2633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 if (mMachineState != MachineState_Saved)
2636 return setError(VBOX_E_INVALID_VM_STATE,
2637 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
2638 Global::stringifyMachineState(mMachineState));
2639
2640 HRESULT rc = mControl->SetRemoveSavedStateFile(aRemoveFile);
2641 if (FAILED(rc)) return rc;
2642
2643 /*
2644 * Saved -> PoweredOff transition will be detected in the SessionMachine
2645 * and properly handled.
2646 */
2647 rc = setMachineState(MachineState_PoweredOff);
2648
2649 return rc;
2650}
2651
2652/** read the value of a LEd. */
2653inline uint32_t readAndClearLed(PPDMLED pLed)
2654{
2655 if (!pLed)
2656 return 0;
2657 uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;
2658 pLed->Asserted.u32 = 0;
2659 return u32;
2660}
2661
2662STDMETHODIMP Console::GetDeviceActivity(DeviceType_T aDeviceType,
2663 DeviceActivity_T *aDeviceActivity)
2664{
2665 CheckComArgNotNull(aDeviceActivity);
2666
2667 AutoCaller autoCaller(this);
2668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2669
2670 /*
2671 * Note: we don't lock the console object here because
2672 * readAndClearLed() should be thread safe.
2673 */
2674
2675 /* Get LED array to read */
2676 PDMLEDCORE SumLed = {0};
2677 switch (aDeviceType)
2678 {
2679 case DeviceType_Floppy:
2680 case DeviceType_DVD:
2681 case DeviceType_HardDisk:
2682 {
2683 for (unsigned i = 0; i < RT_ELEMENTS(mapStorageLeds); ++i)
2684 if (maStorageDevType[i] == aDeviceType)
2685 SumLed.u32 |= readAndClearLed(mapStorageLeds[i]);
2686 break;
2687 }
2688
2689 case DeviceType_Network:
2690 {
2691 for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); ++i)
2692 SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);
2693 break;
2694 }
2695
2696 case DeviceType_USB:
2697 {
2698 for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); ++i)
2699 SumLed.u32 |= readAndClearLed(mapUSBLed[i]);
2700 break;
2701 }
2702
2703 case DeviceType_SharedFolder:
2704 {
2705 SumLed.u32 |= readAndClearLed(mapSharedFolderLed);
2706 break;
2707 }
2708
2709 default:
2710 return setError(E_INVALIDARG,
2711 tr("Invalid device type: %d"),
2712 aDeviceType);
2713 }
2714
2715 /* Compose the result */
2716 switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))
2717 {
2718 case 0:
2719 *aDeviceActivity = DeviceActivity_Idle;
2720 break;
2721 case PDMLED_READING:
2722 *aDeviceActivity = DeviceActivity_Reading;
2723 break;
2724 case PDMLED_WRITING:
2725 case PDMLED_READING | PDMLED_WRITING:
2726 *aDeviceActivity = DeviceActivity_Writing;
2727 break;
2728 }
2729
2730 return S_OK;
2731}
2732
2733STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId)
2734{
2735#ifdef VBOX_WITH_USB
2736 AutoCaller autoCaller(this);
2737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2738
2739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2740
2741 if ( mMachineState != MachineState_Running
2742 && mMachineState != MachineState_Paused)
2743 return setError(VBOX_E_INVALID_VM_STATE,
2744 tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"),
2745 Global::stringifyMachineState(mMachineState));
2746
2747 /* Get the VM handle. */
2748 SafeVMPtr ptrVM(this);
2749 if (!ptrVM.isOk())
2750 return ptrVM.rc();
2751
2752 /* Don't proceed unless we've found the usb controller. */
2753 PPDMIBASE pBase = NULL;
2754 int vrc = PDMR3QueryLun(ptrVM, "usb-ohci", 0, 0, &pBase);
2755 if (RT_FAILURE(vrc))
2756 return setError(VBOX_E_PDM_ERROR,
2757 tr("The virtual machine does not have a USB controller"));
2758
2759 /* leave the lock because the USB Proxy service may call us back
2760 * (via onUSBDeviceAttach()) */
2761 alock.leave();
2762
2763 /* Request the device capture */
2764 return mControl->CaptureUSBDevice(aId);
2765
2766#else /* !VBOX_WITH_USB */
2767 return setError(VBOX_E_PDM_ERROR,
2768 tr("The virtual machine does not have a USB controller"));
2769#endif /* !VBOX_WITH_USB */
2770}
2771
2772STDMETHODIMP Console::DetachUSBDevice(IN_BSTR aId, IUSBDevice **aDevice)
2773{
2774#ifdef VBOX_WITH_USB
2775 CheckComArgOutPointerValid(aDevice);
2776
2777 AutoCaller autoCaller(this);
2778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2779
2780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 /* Find it. */
2783 ComObjPtr<OUSBDevice> pUSBDevice;
2784 USBDeviceList::iterator it = mUSBDevices.begin();
2785 Guid uuid(aId);
2786 while (it != mUSBDevices.end())
2787 {
2788 if ((*it)->id() == uuid)
2789 {
2790 pUSBDevice = *it;
2791 break;
2792 }
2793 ++ it;
2794 }
2795
2796 if (!pUSBDevice)
2797 return setError(E_INVALIDARG,
2798 tr("USB device with UUID {%RTuuid} is not attached to this machine"),
2799 Guid(aId).raw());
2800
2801 /*
2802 * Inform the USB device and USB proxy about what's cooking.
2803 */
2804 alock.leave();
2805 HRESULT rc2 = mControl->DetachUSBDevice(aId, false /* aDone */);
2806 if (FAILED(rc2))
2807 return rc2;
2808 alock.enter();
2809
2810 /* Request the PDM to detach the USB device. */
2811 HRESULT rc = detachUSBDevice(it);
2812
2813 if (SUCCEEDED(rc))
2814 {
2815 /* leave the lock since we don't need it any more (note though that
2816 * the USB Proxy service must not call us back here) */
2817 alock.leave();
2818
2819 /* Request the device release. Even if it fails, the device will
2820 * remain as held by proxy, which is OK for us (the VM process). */
2821 rc = mControl->DetachUSBDevice(aId, true /* aDone */);
2822 }
2823
2824 return rc;
2825
2826
2827#else /* !VBOX_WITH_USB */
2828 return setError(VBOX_E_PDM_ERROR,
2829 tr("The virtual machine does not have a USB controller"));
2830#endif /* !VBOX_WITH_USB */
2831}
2832
2833STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDevice)
2834{
2835#ifdef VBOX_WITH_USB
2836 CheckComArgStrNotEmptyOrNull(aAddress);
2837 CheckComArgOutPointerValid(aDevice);
2838
2839 *aDevice = NULL;
2840
2841 SafeIfaceArray<IUSBDevice> devsvec;
2842 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
2843 if (FAILED(rc)) return rc;
2844
2845 for (size_t i = 0; i < devsvec.size(); ++i)
2846 {
2847 Bstr address;
2848 rc = devsvec[i]->COMGETTER(Address)(address.asOutParam());
2849 if (FAILED(rc)) return rc;
2850 if (address == aAddress)
2851 {
2852 ComObjPtr<OUSBDevice> pUSBDevice;
2853 pUSBDevice.createObject();
2854 pUSBDevice->init(devsvec[i]);
2855 return pUSBDevice.queryInterfaceTo(aDevice);
2856 }
2857 }
2858
2859 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
2860 tr("Could not find a USB device with address '%ls'"),
2861 aAddress);
2862
2863#else /* !VBOX_WITH_USB */
2864 return E_NOTIMPL;
2865#endif /* !VBOX_WITH_USB */
2866}
2867
2868STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
2869{
2870#ifdef VBOX_WITH_USB
2871 CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
2872 CheckComArgOutPointerValid(aDevice);
2873
2874 *aDevice = NULL;
2875
2876 SafeIfaceArray<IUSBDevice> devsvec;
2877 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
2878 if (FAILED(rc)) return rc;
2879
2880 for (size_t i = 0; i < devsvec.size(); ++i)
2881 {
2882 Bstr id;
2883 rc = devsvec[i]->COMGETTER(Id)(id.asOutParam());
2884 if (FAILED(rc)) return rc;
2885 if (id == aId)
2886 {
2887 ComObjPtr<OUSBDevice> pUSBDevice;
2888 pUSBDevice.createObject();
2889 pUSBDevice->init(devsvec[i]);
2890 return pUSBDevice.queryInterfaceTo(aDevice);
2891 }
2892 }
2893
2894 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
2895 tr("Could not find a USB device with uuid {%RTuuid}"),
2896 Guid(aId).raw());
2897
2898#else /* !VBOX_WITH_USB */
2899 return E_NOTIMPL;
2900#endif /* !VBOX_WITH_USB */
2901}
2902
2903STDMETHODIMP
2904Console::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
2905{
2906 CheckComArgStrNotEmptyOrNull(aName);
2907 CheckComArgStrNotEmptyOrNull(aHostPath);
2908
2909 LogFlowThisFunc(("Entering for '%ls' -> '%ls'\n", aName, aHostPath));
2910
2911 AutoCaller autoCaller(this);
2912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2913
2914 Utf8Str strName(aName);
2915 Utf8Str strHostPath(aHostPath);
2916
2917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 /// @todo see @todo in AttachUSBDevice() about the Paused state
2920 if (mMachineState == MachineState_Saved)
2921 return setError(VBOX_E_INVALID_VM_STATE,
2922 tr("Cannot create a transient shared folder on the machine in the saved state"));
2923 if ( mMachineState != MachineState_PoweredOff
2924 && mMachineState != MachineState_Teleported
2925 && mMachineState != MachineState_Aborted
2926 && mMachineState != MachineState_Running
2927 && mMachineState != MachineState_Paused
2928 )
2929 return setError(VBOX_E_INVALID_VM_STATE,
2930 tr("Cannot create a transient shared folder on the machine while it is changing the state (machine state: %s)"),
2931 Global::stringifyMachineState(mMachineState));
2932
2933 ComObjPtr<SharedFolder> pSharedFolder;
2934 HRESULT rc = findSharedFolder(strName, pSharedFolder, false /* aSetError */);
2935 if (SUCCEEDED(rc))
2936 return setError(VBOX_E_FILE_ERROR,
2937 tr("Shared folder named '%s' already exists"),
2938 strName.c_str());
2939
2940 pSharedFolder.createObject();
2941 rc = pSharedFolder->init(this,
2942 strName,
2943 strHostPath,
2944 aWritable,
2945 aAutoMount,
2946 true /* fFailOnError */);
2947 if (FAILED(rc)) return rc;
2948
2949 /* If the VM is online and supports shared folders, share this folder
2950 * under the specified name. (Ignore any failure to obtain the VM handle.) */
2951 SafeVMPtrQuiet ptrVM(this);
2952 if ( ptrVM.isOk()
2953 && m_pVMMDev
2954 && m_pVMMDev->isShFlActive()
2955 )
2956 {
2957 /* first, remove the machine or the global folder if there is any */
2958 SharedFolderDataMap::const_iterator it;
2959 if (findOtherSharedFolder(aName, it))
2960 {
2961 rc = removeSharedFolder(aName);
2962 if (FAILED(rc))
2963 return rc;
2964 }
2965
2966 /* second, create the given folder */
2967 rc = createSharedFolder(aName, SharedFolderData(aHostPath, aWritable, aAutoMount));
2968 if (FAILED(rc))
2969 return rc;
2970 }
2971
2972 m_mapSharedFolders.insert(std::make_pair(aName, pSharedFolder));
2973
2974 /* notify console callbacks after the folder is added to the list */
2975 fireSharedFolderChangedEvent(mEventSource, Scope_Session);
2976
2977 LogFlowThisFunc(("Leaving for '%ls' -> '%ls'\n", aName, aHostPath));
2978
2979 return rc;
2980}
2981
2982STDMETHODIMP Console::RemoveSharedFolder(IN_BSTR aName)
2983{
2984 CheckComArgStrNotEmptyOrNull(aName);
2985
2986 AutoCaller autoCaller(this);
2987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2988
2989 LogFlowThisFunc(("Entering for '%ls'\n", aName));
2990
2991 Utf8Str strName(aName);
2992
2993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 /// @todo see @todo in AttachUSBDevice() about the Paused state
2996 if (mMachineState == MachineState_Saved)
2997 return setError(VBOX_E_INVALID_VM_STATE,
2998 tr("Cannot remove a transient shared folder from the machine in the saved state"));
2999 if ( mMachineState != MachineState_PoweredOff
3000 && mMachineState != MachineState_Teleported
3001 && mMachineState != MachineState_Aborted
3002 && mMachineState != MachineState_Running
3003 && mMachineState != MachineState_Paused
3004 )
3005 return setError(VBOX_E_INVALID_VM_STATE,
3006 tr("Cannot remove a transient shared folder from the machine while it is changing the state (machine state: %s)"),
3007 Global::stringifyMachineState(mMachineState));
3008
3009 ComObjPtr<SharedFolder> pSharedFolder;
3010 HRESULT rc = findSharedFolder(aName, pSharedFolder, true /* aSetError */);
3011 if (FAILED(rc)) return rc;
3012
3013 /* protect the VM handle (if not NULL) */
3014 SafeVMPtrQuiet ptrVM(this);
3015 if ( ptrVM.isOk()
3016 && m_pVMMDev
3017 && m_pVMMDev->isShFlActive()
3018 )
3019 {
3020 /* if the VM is online and supports shared folders, UNshare this
3021 * folder. */
3022
3023 /* first, remove the given folder */
3024 rc = removeSharedFolder(strName);
3025 if (FAILED(rc)) return rc;
3026
3027 /* first, remove the machine or the global folder if there is any */
3028 SharedFolderDataMap::const_iterator it;
3029 if (findOtherSharedFolder(strName, it))
3030 {
3031 rc = createSharedFolder(strName, it->second);
3032 /* don't check rc here because we need to remove the console
3033 * folder from the collection even on failure */
3034 }
3035 }
3036
3037 m_mapSharedFolders.erase(strName);
3038
3039 /* notify console callbacks after the folder is removed to the list */
3040 fireSharedFolderChangedEvent(mEventSource, Scope_Session);
3041
3042 LogFlowThisFunc(("Leaving for '%ls'\n", aName));
3043
3044 return rc;
3045}
3046
3047STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
3048 IN_BSTR aDescription,
3049 IProgress **aProgress)
3050{
3051 LogFlowThisFuncEnter();
3052 LogFlowThisFunc(("aName='%ls' mMachineState=%d\n", aName, mMachineState));
3053
3054 CheckComArgStrNotEmptyOrNull(aName);
3055 CheckComArgOutPointerValid(aProgress);
3056
3057 AutoCaller autoCaller(this);
3058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3059
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
3062 if (Global::IsTransient(mMachineState))
3063 return setError(VBOX_E_INVALID_VM_STATE,
3064 tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
3065 Global::stringifyMachineState(mMachineState));
3066
3067 HRESULT rc = S_OK;
3068
3069 /* prepare the progress object:
3070 a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
3071 ULONG cOperations = 2; // always at least setting up + finishing up
3072 ULONG ulTotalOperationsWeight = 2; // one each for setting up + finishing up
3073 SafeIfaceArray<IMediumAttachment> aMediumAttachments;
3074 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aMediumAttachments));
3075 if (FAILED(rc))
3076 return setError(rc, tr("Cannot get medium attachments of the machine"));
3077
3078 ULONG ulMemSize;
3079 rc = mMachine->COMGETTER(MemorySize)(&ulMemSize);
3080 if (FAILED(rc))
3081 return rc;
3082
3083 for (size_t i = 0;
3084 i < aMediumAttachments.size();
3085 ++i)
3086 {
3087 DeviceType_T type;
3088 rc = aMediumAttachments[i]->COMGETTER(Type)(&type);
3089 if (FAILED(rc))
3090 return rc;
3091
3092 if (type == DeviceType_HardDisk)
3093 {
3094 ++cOperations;
3095
3096 // assume that creating a diff image takes as long as saving a 1MB state
3097 // (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
3098 ulTotalOperationsWeight += 1;
3099 }
3100 }
3101
3102 // b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
3103 bool const fTakingSnapshotOnline = Global::IsOnline(mMachineState);
3104
3105 LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
3106
3107 if (fTakingSnapshotOnline)
3108 {
3109 ++cOperations;
3110 ulTotalOperationsWeight += ulMemSize;
3111 }
3112
3113 // finally, create the progress object
3114 ComObjPtr<Progress> pProgress;
3115 pProgress.createObject();
3116 rc = pProgress->init(static_cast<IConsole*>(this),
3117 Bstr(tr("Taking a snapshot of the virtual machine")).raw(),
3118 mMachineState == MachineState_Running /* aCancelable */,
3119 cOperations,
3120 ulTotalOperationsWeight,
3121 Bstr(tr("Setting up snapshot operation")).raw(), // first sub-op description
3122 1); // ulFirstOperationWeight
3123
3124 if (FAILED(rc))
3125 return rc;
3126
3127 VMTakeSnapshotTask *pTask;
3128 if (!(pTask = new VMTakeSnapshotTask(this, pProgress, aName, aDescription)))
3129 return E_OUTOFMEMORY;
3130
3131 Assert(pTask->mProgress);
3132
3133 try
3134 {
3135 mptrCancelableProgress = pProgress;
3136
3137 /*
3138 * If we fail here it means a PowerDown() call happened on another
3139 * thread while we were doing Pause() (which leaves the Console lock).
3140 * We assign PowerDown() a higher precedence than TakeSnapshot(),
3141 * therefore just return the error to the caller.
3142 */
3143 rc = pTask->rc();
3144 if (FAILED(rc)) throw rc;
3145
3146 pTask->ulMemSize = ulMemSize;
3147
3148 /* memorize the current machine state */
3149 pTask->lastMachineState = mMachineState;
3150 pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
3151
3152 int vrc = RTThreadCreate(NULL,
3153 Console::fntTakeSnapshotWorker,
3154 (void *)pTask,
3155 0,
3156 RTTHREADTYPE_MAIN_WORKER,
3157 0,
3158 "ConsoleTakeSnap");
3159 if (FAILED(vrc))
3160 throw setError(E_FAIL,
3161 tr("Could not create VMTakeSnap thread (%Rrc)"),
3162 vrc);
3163
3164 pTask->mProgress.queryInterfaceTo(aProgress);
3165 }
3166 catch (HRESULT erc)
3167 {
3168 delete pTask;
3169 rc = erc;
3170 mptrCancelableProgress.setNull();
3171 }
3172
3173 LogFlowThisFunc(("rc=%Rhrc\n", rc));
3174 LogFlowThisFuncLeave();
3175 return rc;
3176}
3177
3178STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
3179{
3180 CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
3181 CheckComArgOutPointerValid(aProgress);
3182
3183 AutoCaller autoCaller(this);
3184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3185
3186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 if (Global::IsTransient(mMachineState))
3189 return setError(VBOX_E_INVALID_VM_STATE,
3190 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3191 Global::stringifyMachineState(mMachineState));
3192
3193
3194 MachineState_T machineState = MachineState_Null;
3195 HRESULT rc = mControl->DeleteSnapshot(this, aId, &machineState, aProgress);
3196 if (FAILED(rc)) return rc;
3197
3198 setMachineStateLocally(machineState);
3199 return S_OK;
3200}
3201
3202STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
3203{
3204 AutoCaller autoCaller(this);
3205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3206
3207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3208
3209 if (Global::IsOnlineOrTransient(mMachineState))
3210 return setError(VBOX_E_INVALID_VM_STATE,
3211 tr("Cannot delete the current state of the running machine (machine state: %s)"),
3212 Global::stringifyMachineState(mMachineState));
3213
3214 MachineState_T machineState = MachineState_Null;
3215 HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
3216 if (FAILED(rc)) return rc;
3217
3218 setMachineStateLocally(machineState);
3219 return S_OK;
3220}
3221
3222// Non-interface public methods
3223/////////////////////////////////////////////////////////////////////////////
3224
3225/*static*/
3226HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
3227{
3228 va_list args;
3229 va_start(args, pcsz);
3230 HRESULT rc = setErrorInternal(aResultCode,
3231 getStaticClassIID(),
3232 getStaticComponentName(),
3233 Utf8Str(pcsz, args),
3234 false /* aWarning */,
3235 true /* aLogIt */);
3236 va_end(args);
3237 return rc;
3238}
3239
3240HRESULT Console::setInvalidMachineStateError()
3241{
3242 return setError(VBOX_E_INVALID_VM_STATE,
3243 tr("Invalid machine state: %s"),
3244 Global::stringifyMachineState(mMachineState));
3245}
3246
3247
3248/**
3249 * @copydoc VirtualBox::handleUnexpectedExceptions
3250 */
3251/* static */
3252HRESULT Console::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3253{
3254 try
3255 {
3256 /* re-throw the current exception */
3257 throw;
3258 }
3259 catch (const std::exception &err)
3260 {
3261 return setErrorStatic(E_FAIL,
3262 tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3263 err.what(), typeid(err).name(),
3264 pszFile, iLine, pszFunction);
3265 }
3266 catch (...)
3267 {
3268 return setErrorStatic(E_FAIL,
3269 tr("Unknown exception\n%s[%d] (%s)"),
3270 pszFile, iLine, pszFunction);
3271 }
3272
3273 /* should not get here */
3274 AssertFailed();
3275 return E_FAIL;
3276}
3277
3278/* static */
3279const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3280{
3281 switch (enmCtrlType)
3282 {
3283 case StorageControllerType_LsiLogic:
3284 return "lsilogicscsi";
3285 case StorageControllerType_BusLogic:
3286 return "buslogic";
3287 case StorageControllerType_LsiLogicSas:
3288 return "lsilogicsas";
3289 case StorageControllerType_IntelAhci:
3290 return "ahci";
3291 case StorageControllerType_PIIX3:
3292 case StorageControllerType_PIIX4:
3293 case StorageControllerType_ICH6:
3294 return "piix3ide";
3295 case StorageControllerType_I82078:
3296 return "i82078";
3297 default:
3298 return NULL;
3299 }
3300}
3301
3302HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3303{
3304 switch (enmBus)
3305 {
3306 case StorageBus_IDE:
3307 case StorageBus_Floppy:
3308 {
3309 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3310 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3311 uLun = 2 * port + device;
3312 return S_OK;
3313 }
3314 case StorageBus_SATA:
3315 case StorageBus_SCSI:
3316 case StorageBus_SAS:
3317 {
3318 uLun = port;
3319 return S_OK;
3320 }
3321 default:
3322 uLun = 0;
3323 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3324 }
3325}
3326
3327// private methods
3328/////////////////////////////////////////////////////////////////////////////
3329
3330/**
3331 * Process a medium change.
3332 *
3333 * @param aMediumAttachment The medium attachment with the new medium state.
3334 * @param fForce Force medium chance, if it is locked or not.
3335 * @param pVM Safe VM handle.
3336 *
3337 * @note Locks this object for writing.
3338 */
3339HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PVM pVM)
3340{
3341 AutoCaller autoCaller(this);
3342 AssertComRCReturnRC(autoCaller.rc());
3343
3344 /* We will need to release the write lock before calling EMT */
3345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3346
3347 HRESULT rc = S_OK;
3348 const char *pszDevice = NULL;
3349
3350 SafeIfaceArray<IStorageController> ctrls;
3351 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3352 AssertComRC(rc);
3353 IMedium *pMedium;
3354 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3355 AssertComRC(rc);
3356 Bstr mediumLocation;
3357 if (pMedium)
3358 {
3359 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3360 AssertComRC(rc);
3361 }
3362
3363 Bstr attCtrlName;
3364 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3365 AssertComRC(rc);
3366 ComPtr<IStorageController> pStorageController;
3367 for (size_t i = 0; i < ctrls.size(); ++i)
3368 {
3369 Bstr ctrlName;
3370 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3371 AssertComRC(rc);
3372 if (attCtrlName == ctrlName)
3373 {
3374 pStorageController = ctrls[i];
3375 break;
3376 }
3377 }
3378 if (pStorageController.isNull())
3379 return setError(E_FAIL,
3380 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3381
3382 StorageControllerType_T enmCtrlType;
3383 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3384 AssertComRC(rc);
3385 pszDevice = convertControllerTypeToDev(enmCtrlType);
3386
3387 StorageBus_T enmBus;
3388 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3389 AssertComRC(rc);
3390 ULONG uInstance;
3391 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3392 AssertComRC(rc);
3393 BOOL fUseHostIOCache;
3394 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3395 AssertComRC(rc);
3396
3397 /*
3398 * Call worker in EMT, that's faster and safer than doing everything
3399 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3400 * here to make requests from under the lock in order to serialize them.
3401 */
3402 PVMREQ pReq;
3403 int vrc = VMR3ReqCall(pVM,
3404 VMCPUID_ANY,
3405 &pReq,
3406 0 /* no wait! */,
3407 VMREQFLAGS_VBOX_STATUS,
3408 (PFNRT)Console::changeRemovableMedium,
3409 8,
3410 this,
3411 pVM,
3412 pszDevice,
3413 uInstance,
3414 enmBus,
3415 fUseHostIOCache,
3416 aMediumAttachment,
3417 fForce);
3418
3419 /* leave the lock before waiting for a result (EMT will call us back!) */
3420 alock.leave();
3421
3422 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3423 {
3424 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3425 AssertRC(vrc);
3426 if (RT_SUCCESS(vrc))
3427 vrc = pReq->iStatus;
3428 }
3429 VMR3ReqFree(pReq);
3430
3431 if (RT_SUCCESS(vrc))
3432 {
3433 LogFlowThisFunc(("Returns S_OK\n"));
3434 return S_OK;
3435 }
3436
3437 if (!pMedium)
3438 return setError(E_FAIL,
3439 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3440 mediumLocation.raw(), vrc);
3441
3442 return setError(E_FAIL,
3443 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3444 vrc);
3445}
3446
3447/**
3448 * Performs the medium change in EMT.
3449 *
3450 * @returns VBox status code.
3451 *
3452 * @param pThis Pointer to the Console object.
3453 * @param pVM The VM handle.
3454 * @param pcszDevice The PDM device name.
3455 * @param uInstance The PDM device instance.
3456 * @param uLun The PDM LUN number of the drive.
3457 * @param fHostDrive True if this is a host drive attachment.
3458 * @param pszPath The path to the media / drive which is now being mounted / captured.
3459 * If NULL no media or drive is attached and the LUN will be configured with
3460 * the default block driver with no media. This will also be the state if
3461 * mounting / capturing the specified media / drive fails.
3462 * @param pszFormat Medium format string, usually "RAW".
3463 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3464 *
3465 * @thread EMT
3466 */
3467DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3468 PVM pVM,
3469 const char *pcszDevice,
3470 unsigned uInstance,
3471 StorageBus_T enmBus,
3472 bool fUseHostIOCache,
3473 IMediumAttachment *aMediumAtt,
3474 bool fForce)
3475{
3476 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3477 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
3478
3479 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3480
3481 AutoCaller autoCaller(pConsole);
3482 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3483
3484 /*
3485 * Suspend the VM first.
3486 *
3487 * The VM must not be running since it might have pending I/O to
3488 * the drive which is being changed.
3489 */
3490 bool fResume;
3491 VMSTATE enmVMState = VMR3GetState(pVM);
3492 switch (enmVMState)
3493 {
3494 case VMSTATE_RESETTING:
3495 case VMSTATE_RUNNING:
3496 {
3497 LogFlowFunc(("Suspending the VM...\n"));
3498 /* disable the callback to prevent Console-level state change */
3499 pConsole->mVMStateChangeCallbackDisabled = true;
3500 int rc = VMR3Suspend(pVM);
3501 pConsole->mVMStateChangeCallbackDisabled = false;
3502 AssertRCReturn(rc, rc);
3503 fResume = true;
3504 break;
3505 }
3506
3507 case VMSTATE_SUSPENDED:
3508 case VMSTATE_CREATED:
3509 case VMSTATE_OFF:
3510 fResume = false;
3511 break;
3512
3513 case VMSTATE_RUNNING_LS:
3514 case VMSTATE_RUNNING_FT:
3515 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3516 COM_IIDOF(IConsole),
3517 getStaticComponentName(),
3518 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3519 false /*aWarning*/,
3520 true /*aLogIt*/);
3521
3522 default:
3523 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3524 }
3525
3526 /* Determine the base path for the device instance. */
3527 PCFGMNODE pCtlInst;
3528 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
3529 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3530
3531 int rc = VINF_SUCCESS;
3532 int rcRet = VINF_SUCCESS;
3533
3534 rcRet = pConsole->configMediumAttachment(pCtlInst,
3535 pcszDevice,
3536 uInstance,
3537 enmBus,
3538 fUseHostIOCache,
3539 false /* fSetupMerge */,
3540 false /* fBuiltinIoCache */,
3541 0 /* uMergeSource */,
3542 0 /* uMergeTarget */,
3543 aMediumAtt,
3544 pConsole->mMachineState,
3545 NULL /* phrc */,
3546 true /* fAttachDetach */,
3547 fForce /* fForceUnmount */,
3548 pVM,
3549 NULL /* paLedDevType */);
3550 /** @todo this dumps everything attached to this device instance, which
3551 * is more than necessary. Dumping the changed LUN would be enough. */
3552 CFGMR3Dump(pCtlInst);
3553
3554 /*
3555 * Resume the VM if necessary.
3556 */
3557 if (fResume)
3558 {
3559 LogFlowFunc(("Resuming the VM...\n"));
3560 /* disable the callback to prevent Console-level state change */
3561 pConsole->mVMStateChangeCallbackDisabled = true;
3562 rc = VMR3Resume(pVM);
3563 pConsole->mVMStateChangeCallbackDisabled = false;
3564 AssertRC(rc);
3565 if (RT_FAILURE(rc))
3566 {
3567 /* too bad, we failed. try to sync the console state with the VMM state */
3568 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3569 }
3570 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3571 // error (if any) will be hidden from the caller. For proper reporting
3572 // of such multiple errors to the caller we need to enhance the
3573 // IVirtualBoxError interface. For now, give the first error the higher
3574 // priority.
3575 if (RT_SUCCESS(rcRet))
3576 rcRet = rc;
3577 }
3578
3579 LogFlowFunc(("Returning %Rrc\n", rcRet));
3580 return rcRet;
3581}
3582
3583
3584/**
3585 * Called by IInternalSessionControl::OnNetworkAdapterChange().
3586 *
3587 * @note Locks this object for writing.
3588 */
3589HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
3590{
3591 LogFlowThisFunc(("\n"));
3592
3593 AutoCaller autoCaller(this);
3594 AssertComRCReturnRC(autoCaller.rc());
3595
3596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3597
3598 HRESULT rc = S_OK;
3599
3600 /* don't trigger network change if the VM isn't running */
3601 SafeVMPtrQuiet ptrVM(this);
3602 if (ptrVM.isOk())
3603 {
3604 /* Get the properties we need from the adapter */
3605 BOOL fCableConnected, fTraceEnabled;
3606 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
3607 AssertComRC(rc);
3608 if (SUCCEEDED(rc))
3609 {
3610 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
3611 AssertComRC(rc);
3612 }
3613 if (SUCCEEDED(rc))
3614 {
3615 ULONG ulInstance;
3616 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
3617 AssertComRC(rc);
3618 if (SUCCEEDED(rc))
3619 {
3620 /*
3621 * Find the adapter instance, get the config interface and update
3622 * the link state.
3623 */
3624 NetworkAdapterType_T adapterType;
3625 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3626 AssertComRC(rc);
3627 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
3628 PPDMIBASE pBase;
3629 int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
3630 if (RT_SUCCESS(vrc))
3631 {
3632 Assert(pBase);
3633 PPDMINETWORKCONFIG pINetCfg;
3634 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
3635 if (pINetCfg)
3636 {
3637 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
3638 fCableConnected));
3639 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
3640 fCableConnected ? PDMNETWORKLINKSTATE_UP
3641 : PDMNETWORKLINKSTATE_DOWN);
3642 ComAssertRC(vrc);
3643 }
3644 if (RT_SUCCESS(vrc) && changeAdapter)
3645 {
3646 VMSTATE enmVMState = VMR3GetState(ptrVM);
3647 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
3648 || enmVMState == VMSTATE_SUSPENDED)
3649 {
3650 if (fTraceEnabled && fCableConnected && pINetCfg)
3651 {
3652 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
3653 ComAssertRC(vrc);
3654 }
3655
3656 rc = doNetworkAdapterChange(ptrVM, pszAdapterName, ulInstance, 0, aNetworkAdapter);
3657
3658 if (fTraceEnabled && fCableConnected && pINetCfg)
3659 {
3660 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
3661 ComAssertRC(vrc);
3662 }
3663 }
3664 }
3665 }
3666 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
3667 return setError(E_FAIL,
3668 tr("The network adapter #%u is not enabled"), ulInstance);
3669 else
3670 ComAssertRC(vrc);
3671
3672 if (RT_FAILURE(vrc))
3673 rc = E_FAIL;
3674 }
3675 }
3676 ptrVM.release();
3677 }
3678
3679 /* notify console callbacks on success */
3680 if (SUCCEEDED(rc))
3681 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
3682
3683 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3684 return rc;
3685}
3686
3687/**
3688 * Called by IInternalSessionControl::OnNATEngineChange().
3689 *
3690 * @note Locks this object for writing.
3691 */
3692HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
3693 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
3694{
3695 LogFlowThisFunc(("\n"));
3696
3697 AutoCaller autoCaller(this);
3698 AssertComRCReturnRC(autoCaller.rc());
3699
3700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3701
3702 HRESULT rc = S_OK;
3703
3704 /* don't trigger nat engine change if the VM isn't running */
3705 SafeVMPtrQuiet ptrVM(this);
3706 if (ptrVM.isOk())
3707 {
3708 do
3709 {
3710 ComPtr<INetworkAdapter> pNetworkAdapter;
3711 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
3712 if ( FAILED(rc)
3713 || pNetworkAdapter.isNull())
3714 break;
3715
3716 /*
3717 * Find the adapter instance, get the config interface and update
3718 * the link state.
3719 */
3720 NetworkAdapterType_T adapterType;
3721 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3722 if (FAILED(rc))
3723 {
3724 AssertComRC(rc);
3725 rc = E_FAIL;
3726 break;
3727 }
3728
3729 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
3730 PPDMIBASE pBase;
3731 int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
3732 if (RT_FAILURE(vrc))
3733 {
3734 ComAssertRC(vrc);
3735 rc = E_FAIL;
3736 break;
3737 }
3738
3739 NetworkAttachmentType_T attachmentType;
3740 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3741 if ( FAILED(rc)
3742 || attachmentType != NetworkAttachmentType_NAT)
3743 {
3744 rc = E_FAIL;
3745 break;
3746 }
3747
3748 /* look down for PDMINETWORKNATCONFIG interface */
3749 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
3750 while (pBase)
3751 {
3752 pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID);
3753 if (pNetNatCfg)
3754 break;
3755 /** @todo r=bird: This stinks! */
3756 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase);
3757 pBase = pDrvIns->pDownBase;
3758 }
3759 if (!pNetNatCfg)
3760 break;
3761
3762 bool fUdp = aProto == NATProtocol_UDP;
3763 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, aNatRuleRemove, fUdp,
3764 Utf8Str(aHostIp).c_str(), aHostPort, Utf8Str(aGuestIp).c_str(),
3765 aGuestPort);
3766 if (RT_FAILURE(vrc))
3767 rc = E_FAIL;
3768 } while (0); /* break loop */
3769 ptrVM.release();
3770 }
3771
3772 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3773 return rc;
3774}
3775
3776
3777/**
3778 * Process a network adaptor change.
3779 *
3780 * @returns COM status code.
3781 *
3782 * @parma pVM The VM handle (caller hold this safely).
3783 * @param pszDevice The PDM device name.
3784 * @param uInstance The PDM device instance.
3785 * @param uLun The PDM LUN number of the drive.
3786 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3787 *
3788 * @note Locks this object for writing.
3789 */
3790HRESULT Console::doNetworkAdapterChange(PVM pVM,
3791 const char *pszDevice,
3792 unsigned uInstance,
3793 unsigned uLun,
3794 INetworkAdapter *aNetworkAdapter)
3795{
3796 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3797 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3798
3799 AutoCaller autoCaller(this);
3800 AssertComRCReturnRC(autoCaller.rc());
3801
3802 /* We will need to release the write lock before calling EMT */
3803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3804
3805 /* Get the VM handle. */
3806 SafeVMPtr ptrVM(this);
3807 if (!ptrVM.isOk())
3808 return ptrVM.rc();
3809
3810 /*
3811 * Call worker in EMT, that's faster and safer than doing everything
3812 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3813 * here to make requests from under the lock in order to serialize them.
3814 */
3815 PVMREQ pReq;
3816 int vrc = VMR3ReqCall(pVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3817 (PFNRT) Console::changeNetworkAttachment, 6,
3818 this, ptrVM.raw(), pszDevice, uInstance, uLun, aNetworkAdapter);
3819
3820 /* leave the lock before waiting for a result (EMT will call us back!) */
3821 alock.leave();
3822
3823 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3824 {
3825 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3826 AssertRC(vrc);
3827 if (RT_SUCCESS(vrc))
3828 vrc = pReq->iStatus;
3829 }
3830 VMR3ReqFree(pReq);
3831
3832 if (RT_SUCCESS(vrc))
3833 {
3834 LogFlowThisFunc(("Returns S_OK\n"));
3835 return S_OK;
3836 }
3837
3838 return setError(E_FAIL,
3839 tr("Could not change the network adaptor attachement type (%Rrc)"),
3840 vrc);
3841}
3842
3843
3844/**
3845 * Performs the Network Adaptor change in EMT.
3846 *
3847 * @returns VBox status code.
3848 *
3849 * @param pThis Pointer to the Console object.
3850 * @param pVM The VM handle.
3851 * @param pszDevice The PDM device name.
3852 * @param uInstance The PDM device instance.
3853 * @param uLun The PDM LUN number of the drive.
3854 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3855 *
3856 * @thread EMT
3857 * @note Locks the Console object for writing.
3858 */
3859DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
3860 PVM pVM,
3861 const char *pszDevice,
3862 unsigned uInstance,
3863 unsigned uLun,
3864 INetworkAdapter *aNetworkAdapter)
3865{
3866 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3867 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3868
3869 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3870
3871 AssertMsg( ( !strcmp(pszDevice, "pcnet")
3872 || !strcmp(pszDevice, "e1000")
3873 || !strcmp(pszDevice, "virtio-net"))
3874 && uLun == 0
3875 && uInstance < SchemaDefs::NetworkAdapterCount,
3876 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3877 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3878
3879 AutoCaller autoCaller(pThis);
3880 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3881
3882 /*
3883 * Suspend the VM first.
3884 *
3885 * The VM must not be running since it might have pending I/O to
3886 * the drive which is being changed.
3887 */
3888 bool fResume;
3889 VMSTATE enmVMState = VMR3GetState(pVM);
3890 switch (enmVMState)
3891 {
3892 case VMSTATE_RESETTING:
3893 case VMSTATE_RUNNING:
3894 {
3895 LogFlowFunc(("Suspending the VM...\n"));
3896 /* disable the callback to prevent Console-level state change */
3897 pThis->mVMStateChangeCallbackDisabled = true;
3898 int rc = VMR3Suspend(pVM);
3899 pThis->mVMStateChangeCallbackDisabled = false;
3900 AssertRCReturn(rc, rc);
3901 fResume = true;
3902 break;
3903 }
3904
3905 case VMSTATE_SUSPENDED:
3906 case VMSTATE_CREATED:
3907 case VMSTATE_OFF:
3908 fResume = false;
3909 break;
3910
3911 default:
3912 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3913 }
3914
3915 int rc = VINF_SUCCESS;
3916 int rcRet = VINF_SUCCESS;
3917
3918 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3919 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3920 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
3921 AssertRelease(pInst);
3922
3923 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
3924 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
3925
3926 /*
3927 * Resume the VM if necessary.
3928 */
3929 if (fResume)
3930 {
3931 LogFlowFunc(("Resuming the VM...\n"));
3932 /* disable the callback to prevent Console-level state change */
3933 pThis->mVMStateChangeCallbackDisabled = true;
3934 rc = VMR3Resume(pVM);
3935 pThis->mVMStateChangeCallbackDisabled = false;
3936 AssertRC(rc);
3937 if (RT_FAILURE(rc))
3938 {
3939 /* too bad, we failed. try to sync the console state with the VMM state */
3940 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3941 }
3942 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3943 // error (if any) will be hidden from the caller. For proper reporting
3944 // of such multiple errors to the caller we need to enhance the
3945 // IVirtualBoxError interface. For now, give the first error the higher
3946 // priority.
3947 if (RT_SUCCESS(rcRet))
3948 rcRet = rc;
3949 }
3950
3951 LogFlowFunc(("Returning %Rrc\n", rcRet));
3952 return rcRet;
3953}
3954
3955
3956/**
3957 * Called by IInternalSessionControl::OnSerialPortChange().
3958 *
3959 * @note Locks this object for writing.
3960 */
3961HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
3962{
3963 LogFlowThisFunc(("\n"));
3964
3965 AutoCaller autoCaller(this);
3966 AssertComRCReturnRC(autoCaller.rc());
3967
3968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3969
3970 HRESULT rc = S_OK;
3971
3972 /* don't trigger serial port change if the VM isn't running */
3973 SafeVMPtrQuiet ptrVM(this);
3974 if (ptrVM.isOk())
3975 {
3976 /* nothing to do so far */
3977 ptrVM.release();
3978 }
3979
3980 /* notify console callbacks on success */
3981 if (SUCCEEDED(rc))
3982 fireSerialPortChangedEvent(mEventSource, aSerialPort);
3983
3984 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3985 return rc;
3986}
3987
3988/**
3989 * Called by IInternalSessionControl::OnParallelPortChange().
3990 *
3991 * @note Locks this object for writing.
3992 */
3993HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
3994{
3995 LogFlowThisFunc(("\n"));
3996
3997 AutoCaller autoCaller(this);
3998 AssertComRCReturnRC(autoCaller.rc());
3999
4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4001
4002 HRESULT rc = S_OK;
4003
4004 /* don't trigger parallel port change if the VM isn't running */
4005 SafeVMPtrQuiet ptrVM(this);
4006 if (ptrVM.isOk())
4007 {
4008 /* nothing to do so far */
4009 ptrVM.release();
4010 }
4011
4012 /* notify console callbacks on success */
4013 if (SUCCEEDED(rc))
4014 fireParallelPortChangedEvent(mEventSource, aParallelPort);
4015
4016 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4017 return rc;
4018}
4019
4020/**
4021 * Called by IInternalSessionControl::OnStorageControllerChange().
4022 *
4023 * @note Locks this object for writing.
4024 */
4025HRESULT Console::onStorageControllerChange()
4026{
4027 LogFlowThisFunc(("\n"));
4028
4029 AutoCaller autoCaller(this);
4030 AssertComRCReturnRC(autoCaller.rc());
4031
4032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4033
4034 HRESULT rc = S_OK;
4035
4036 /* don't trigger storage controller change if the VM isn't running */
4037 SafeVMPtrQuiet ptrVM(this);
4038 if (ptrVM.isOk())
4039 {
4040 /* nothing to do so far */
4041 ptrVM.release();
4042 }
4043
4044 /* notify console callbacks on success */
4045 if (SUCCEEDED(rc))
4046 fireStorageControllerChangedEvent(mEventSource);
4047
4048 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4049 return rc;
4050}
4051
4052/**
4053 * Called by IInternalSessionControl::OnMediumChange().
4054 *
4055 * @note Locks this object for writing.
4056 */
4057HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
4058{
4059 LogFlowThisFunc(("\n"));
4060
4061 AutoCaller autoCaller(this);
4062 AssertComRCReturnRC(autoCaller.rc());
4063
4064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4065
4066 HRESULT rc = S_OK;
4067
4068 /* don't trigger medium change if the VM isn't running */
4069 SafeVMPtrQuiet ptrVM(this);
4070 if (ptrVM.isOk())
4071 {
4072 rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM);
4073 ptrVM.release();
4074 }
4075
4076 /* notify console callbacks on success */
4077 if (SUCCEEDED(rc))
4078 fireMediumChangedEvent(mEventSource, aMediumAttachment);
4079
4080 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4081 return rc;
4082}
4083
4084/**
4085 * Called by IInternalSessionControl::OnCPUChange().
4086 *
4087 * @note Locks this object for writing.
4088 */
4089HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
4090{
4091 LogFlowThisFunc(("\n"));
4092
4093 AutoCaller autoCaller(this);
4094 AssertComRCReturnRC(autoCaller.rc());
4095
4096 HRESULT rc = S_OK;
4097
4098 /* don't trigger CPU change if the VM isn't running */
4099 SafeVMPtrQuiet ptrVM(this);
4100 if (ptrVM.isOk())
4101 {
4102 if (aRemove)
4103 rc = doCPURemove(aCPU, ptrVM);
4104 else
4105 rc = doCPUAdd(aCPU, ptrVM);
4106 ptrVM.release();
4107 }
4108
4109 /* notify console callbacks on success */
4110 if (SUCCEEDED(rc))
4111 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
4112
4113 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4114 return rc;
4115}
4116
4117/**
4118 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
4119 *
4120 * @note Locks this object for writing.
4121 */
4122HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4123{
4124 LogFlowThisFunc(("\n"));
4125
4126 AutoCaller autoCaller(this);
4127 AssertComRCReturnRC(autoCaller.rc());
4128
4129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4130
4131 HRESULT rc = S_OK;
4132
4133 /* don't trigger the CPU priority change if the VM isn't running */
4134 SafeVMPtrQuiet ptrVM(this);
4135 if (ptrVM.isOk())
4136 {
4137 if ( mMachineState == MachineState_Running
4138 || mMachineState == MachineState_Teleporting
4139 || mMachineState == MachineState_LiveSnapshotting
4140 )
4141 {
4142 /* No need to call in the EMT thread. */
4143 rc = VMR3SetCpuExecutionCap(ptrVM, aExecutionCap);
4144 }
4145 else
4146 rc = setInvalidMachineStateError();
4147 ptrVM.release();
4148 }
4149
4150 /* notify console callbacks on success */
4151 if (SUCCEEDED(rc))
4152 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4153
4154 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4155 return rc;
4156}
4157
4158/**
4159 * Called by IInternalSessionControl::OnVRDEServerChange().
4160 *
4161 * @note Locks this object for writing.
4162 */
4163HRESULT Console::onVRDEServerChange(BOOL aRestart)
4164{
4165 AutoCaller autoCaller(this);
4166 AssertComRCReturnRC(autoCaller.rc());
4167
4168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4169
4170 HRESULT rc = S_OK;
4171
4172 if ( mVRDEServer
4173 && ( mMachineState == MachineState_Running
4174 || mMachineState == MachineState_Teleporting
4175 || mMachineState == MachineState_LiveSnapshotting
4176 )
4177 )
4178 {
4179 BOOL vrdpEnabled = FALSE;
4180
4181 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
4182 ComAssertComRCRetRC(rc);
4183
4184 if (aRestart)
4185 {
4186 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
4187 alock.leave();
4188
4189 if (vrdpEnabled)
4190 {
4191 // If there was no VRDP server started the 'stop' will do nothing.
4192 // However if a server was started and this notification was called,
4193 // we have to restart the server.
4194 mConsoleVRDPServer->Stop();
4195
4196 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
4197 rc = E_FAIL;
4198 else
4199 mConsoleVRDPServer->EnableConnections();
4200 }
4201 else
4202 {
4203 mConsoleVRDPServer->Stop();
4204 }
4205
4206 alock.enter();
4207 }
4208 }
4209
4210 /* notify console callbacks on success */
4211 if (SUCCEEDED(rc))
4212 fireVRDEServerChangedEvent(mEventSource);
4213
4214 return rc;
4215}
4216
4217/**
4218 * @note Locks this object for reading.
4219 */
4220void Console::onVRDEServerInfoChange()
4221{
4222 AutoCaller autoCaller(this);
4223 AssertComRCReturnVoid(autoCaller.rc());
4224
4225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4226
4227 fireVRDEServerInfoChangedEvent(mEventSource);
4228}
4229
4230
4231/**
4232 * Called by IInternalSessionControl::OnUSBControllerChange().
4233 *
4234 * @note Locks this object for writing.
4235 */
4236HRESULT Console::onUSBControllerChange()
4237{
4238 LogFlowThisFunc(("\n"));
4239
4240 AutoCaller autoCaller(this);
4241 AssertComRCReturnRC(autoCaller.rc());
4242
4243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4244
4245 HRESULT rc = S_OK;
4246
4247 /* don't trigger USB controller change if the VM isn't running */
4248 SafeVMPtrQuiet ptrVM(this);
4249 if (ptrVM.isOk())
4250 {
4251 /// @todo implement one day.
4252 // Anyway, if we want to query the machine's USB Controller we need
4253 // to cache it to mUSBController in #init() (similar to mDVDDrive).
4254 //
4255 // bird: While the VM supports hot-plugging, I doubt any guest can
4256 // handle it at this time... :-)
4257
4258 /* nothing to do so far */
4259 ptrVM.release();
4260 }
4261
4262 /* notify console callbacks on success */
4263 if (SUCCEEDED(rc))
4264 fireUSBControllerChangedEvent(mEventSource);
4265
4266 return rc;
4267}
4268
4269/**
4270 * Called by IInternalSessionControl::OnSharedFolderChange().
4271 *
4272 * @note Locks this object for writing.
4273 */
4274HRESULT Console::onSharedFolderChange(BOOL aGlobal)
4275{
4276 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
4277
4278 AutoCaller autoCaller(this);
4279 AssertComRCReturnRC(autoCaller.rc());
4280
4281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4282
4283 HRESULT rc = fetchSharedFolders(aGlobal);
4284
4285 /* notify console callbacks on success */
4286 if (SUCCEEDED(rc))
4287 {
4288 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
4289 }
4290
4291 return rc;
4292}
4293
4294/**
4295 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4296 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4297 * returns TRUE for a given remote USB device.
4298 *
4299 * @return S_OK if the device was attached to the VM.
4300 * @return failure if not attached.
4301 *
4302 * @param aDevice
4303 * The device in question.
4304 * @param aMaskedIfs
4305 * The interfaces to hide from the guest.
4306 *
4307 * @note Locks this object for writing.
4308 */
4309HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4310{
4311#ifdef VBOX_WITH_USB
4312 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4313
4314 AutoCaller autoCaller(this);
4315 ComAssertComRCRetRC(autoCaller.rc());
4316
4317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4318
4319 /* Get the VM pointer (we don't need error info, since it's a callback). */
4320 SafeVMPtrQuiet ptrVM(this);
4321 if (!ptrVM.isOk())
4322 {
4323 /* The VM may be no more operational when this message arrives
4324 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4325 * autoVMCaller.rc() will return a failure in this case. */
4326 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4327 mMachineState));
4328 return ptrVM.rc();
4329 }
4330
4331 if (aError != NULL)
4332 {
4333 /* notify callbacks about the error */
4334 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4335 return S_OK;
4336 }
4337
4338 /* Don't proceed unless there's at least one USB hub. */
4339 if (!PDMR3USBHasHub(ptrVM))
4340 {
4341 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4342 return E_FAIL;
4343 }
4344
4345 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4346 if (FAILED(rc))
4347 {
4348 /* take the current error info */
4349 com::ErrorInfoKeeper eik;
4350 /* the error must be a VirtualBoxErrorInfo instance */
4351 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
4352 Assert(!pError.isNull());
4353 if (!pError.isNull())
4354 {
4355 /* notify callbacks about the error */
4356 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
4357 }
4358 }
4359
4360 return rc;
4361
4362#else /* !VBOX_WITH_USB */
4363 return E_FAIL;
4364#endif /* !VBOX_WITH_USB */
4365}
4366
4367/**
4368 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
4369 * processRemoteUSBDevices().
4370 *
4371 * @note Locks this object for writing.
4372 */
4373HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
4374 IVirtualBoxErrorInfo *aError)
4375{
4376#ifdef VBOX_WITH_USB
4377 Guid Uuid(aId);
4378 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
4379
4380 AutoCaller autoCaller(this);
4381 AssertComRCReturnRC(autoCaller.rc());
4382
4383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4384
4385 /* Find the device. */
4386 ComObjPtr<OUSBDevice> pUSBDevice;
4387 USBDeviceList::iterator it = mUSBDevices.begin();
4388 while (it != mUSBDevices.end())
4389 {
4390 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
4391 if ((*it)->id() == Uuid)
4392 {
4393 pUSBDevice = *it;
4394 break;
4395 }
4396 ++ it;
4397 }
4398
4399
4400 if (pUSBDevice.isNull())
4401 {
4402 LogFlowThisFunc(("USB device not found.\n"));
4403
4404 /* The VM may be no more operational when this message arrives
4405 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
4406 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
4407 * failure in this case. */
4408
4409 AutoVMCallerQuiet autoVMCaller(this);
4410 if (FAILED(autoVMCaller.rc()))
4411 {
4412 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
4413 mMachineState));
4414 return autoVMCaller.rc();
4415 }
4416
4417 /* the device must be in the list otherwise */
4418 AssertFailedReturn(E_FAIL);
4419 }
4420
4421 if (aError != NULL)
4422 {
4423 /* notify callback about an error */
4424 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
4425 return S_OK;
4426 }
4427
4428 HRESULT rc = detachUSBDevice(it);
4429
4430 if (FAILED(rc))
4431 {
4432 /* take the current error info */
4433 com::ErrorInfoKeeper eik;
4434 /* the error must be a VirtualBoxErrorInfo instance */
4435 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
4436 Assert(!pError.isNull());
4437 if (!pError.isNull())
4438 {
4439 /* notify callbacks about the error */
4440 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
4441 }
4442 }
4443
4444 return rc;
4445
4446#else /* !VBOX_WITH_USB */
4447 return E_FAIL;
4448#endif /* !VBOX_WITH_USB */
4449}
4450
4451/**
4452 * Called by IInternalSessionControl::OnBandwidthGroupChange().
4453 *
4454 * @note Locks this object for writing.
4455 */
4456HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
4457{
4458 LogFlowThisFunc(("\n"));
4459
4460 AutoCaller autoCaller(this);
4461 AssertComRCReturnRC(autoCaller.rc());
4462
4463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4464
4465 HRESULT rc = S_OK;
4466
4467 /* don't trigger the CPU priority change if the VM isn't running */
4468 SafeVMPtrQuiet ptrVM(this);
4469 if (ptrVM.isOk())
4470 {
4471 if ( mMachineState == MachineState_Running
4472 || mMachineState == MachineState_Teleporting
4473 || mMachineState == MachineState_LiveSnapshotting
4474 )
4475 {
4476 /* No need to call in the EMT thread. */
4477 ULONG cMax;
4478 Bstr strName;
4479 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
4480 if (SUCCEEDED(rc))
4481 rc = aBandwidthGroup->COMGETTER(MaxMbPerSec)(&cMax);
4482
4483 if (SUCCEEDED(rc))
4484 {
4485 int vrc;
4486 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(),
4487 cMax * _1M);
4488 AssertRC(vrc);
4489 }
4490 }
4491 else
4492 rc = setInvalidMachineStateError();
4493 ptrVM.release();
4494 }
4495
4496 /* notify console callbacks on success */
4497 if (SUCCEEDED(rc))
4498 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
4499
4500 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4501 return rc;
4502}
4503
4504/**
4505 * @note Temporarily locks this object for writing.
4506 */
4507HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
4508 LONG64 *aTimestamp, BSTR *aFlags)
4509{
4510#ifndef VBOX_WITH_GUEST_PROPS
4511 ReturnComNotImplemented();
4512#else /* VBOX_WITH_GUEST_PROPS */
4513 if (!VALID_PTR(aName))
4514 return E_INVALIDARG;
4515 if (!VALID_PTR(aValue))
4516 return E_POINTER;
4517 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
4518 return E_POINTER;
4519 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4520 return E_POINTER;
4521
4522 AutoCaller autoCaller(this);
4523 AssertComRCReturnRC(autoCaller.rc());
4524
4525 /* protect mpVM (if not NULL) */
4526 AutoVMCallerWeak autoVMCaller(this);
4527 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4528
4529 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4530 * autoVMCaller, so there is no need to hold a lock of this */
4531
4532 HRESULT rc = E_UNEXPECTED;
4533 using namespace guestProp;
4534
4535 try
4536 {
4537 VBOXHGCMSVCPARM parm[4];
4538 Utf8Str Utf8Name = aName;
4539 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4540
4541 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4542 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4543 /* The + 1 is the null terminator */
4544 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4545 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4546 parm[1].u.pointer.addr = pszBuffer;
4547 parm[1].u.pointer.size = sizeof(pszBuffer);
4548 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
4549 4, &parm[0]);
4550 /* The returned string should never be able to be greater than our buffer */
4551 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
4552 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4553 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4554 {
4555 rc = S_OK;
4556 if (vrc != VERR_NOT_FOUND)
4557 {
4558 Utf8Str strBuffer(pszBuffer);
4559 strBuffer.cloneTo(aValue);
4560
4561 if (aTimestamp)
4562 *aTimestamp = parm[2].u.uint64;
4563
4564 if (aFlags)
4565 {
4566 size_t iFlags = strBuffer.length() + 1;
4567 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4568 }
4569 }
4570 else
4571 aValue = NULL;
4572 }
4573 else
4574 rc = setError(E_UNEXPECTED,
4575 tr("The service call failed with the error %Rrc"),
4576 vrc);
4577 }
4578 catch(std::bad_alloc & /*e*/)
4579 {
4580 rc = E_OUTOFMEMORY;
4581 }
4582 return rc;
4583#endif /* VBOX_WITH_GUEST_PROPS */
4584}
4585
4586/**
4587 * @note Temporarily locks this object for writing.
4588 */
4589HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4590{
4591#ifndef VBOX_WITH_GUEST_PROPS
4592 ReturnComNotImplemented();
4593#else /* VBOX_WITH_GUEST_PROPS */
4594 if (!VALID_PTR(aName))
4595 return E_INVALIDARG;
4596 if ((aValue != NULL) && !VALID_PTR(aValue))
4597 return E_INVALIDARG;
4598 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4599 return E_INVALIDARG;
4600
4601 AutoCaller autoCaller(this);
4602 AssertComRCReturnRC(autoCaller.rc());
4603
4604 /* protect mpVM (if not NULL) */
4605 AutoVMCallerWeak autoVMCaller(this);
4606 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4607
4608 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4609 * autoVMCaller, so there is no need to hold a lock of this */
4610
4611 HRESULT rc = E_UNEXPECTED;
4612 using namespace guestProp;
4613
4614 VBOXHGCMSVCPARM parm[3];
4615 Utf8Str Utf8Name = aName;
4616 int vrc = VINF_SUCCESS;
4617
4618 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4619 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4620 /* The + 1 is the null terminator */
4621 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4622 Utf8Str Utf8Value = aValue;
4623 if (aValue != NULL)
4624 {
4625 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4626 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4627 /* The + 1 is the null terminator */
4628 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4629 }
4630 Utf8Str Utf8Flags = aFlags;
4631 if (aFlags != NULL)
4632 {
4633 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4634 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4635 /* The + 1 is the null terminator */
4636 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4637 }
4638 if ((aValue != NULL) && (aFlags != NULL))
4639 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
4640 3, &parm[0]);
4641 else if (aValue != NULL)
4642 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4643 2, &parm[0]);
4644 else
4645 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
4646 1, &parm[0]);
4647 if (RT_SUCCESS(vrc))
4648 rc = S_OK;
4649 else
4650 rc = setError(E_UNEXPECTED,
4651 tr("The service call failed with the error %Rrc"),
4652 vrc);
4653 return rc;
4654#endif /* VBOX_WITH_GUEST_PROPS */
4655}
4656
4657
4658/**
4659 * @note Temporarily locks this object for writing.
4660 */
4661HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
4662 ComSafeArrayOut(BSTR, aNames),
4663 ComSafeArrayOut(BSTR, aValues),
4664 ComSafeArrayOut(LONG64, aTimestamps),
4665 ComSafeArrayOut(BSTR, aFlags))
4666{
4667#ifndef VBOX_WITH_GUEST_PROPS
4668 ReturnComNotImplemented();
4669#else /* VBOX_WITH_GUEST_PROPS */
4670 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4671 return E_POINTER;
4672 if (ComSafeArrayOutIsNull(aNames))
4673 return E_POINTER;
4674 if (ComSafeArrayOutIsNull(aValues))
4675 return E_POINTER;
4676 if (ComSafeArrayOutIsNull(aTimestamps))
4677 return E_POINTER;
4678 if (ComSafeArrayOutIsNull(aFlags))
4679 return E_POINTER;
4680
4681 AutoCaller autoCaller(this);
4682 AssertComRCReturnRC(autoCaller.rc());
4683
4684 /* protect mpVM (if not NULL) */
4685 AutoVMCallerWeak autoVMCaller(this);
4686 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4687
4688 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4689 * autoVMCaller, so there is no need to hold a lock of this */
4690
4691 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
4692 ComSafeArrayOutArg(aValues),
4693 ComSafeArrayOutArg(aTimestamps),
4694 ComSafeArrayOutArg(aFlags));
4695#endif /* VBOX_WITH_GUEST_PROPS */
4696}
4697
4698
4699/*
4700 * Internal: helper function for connecting progress reporting
4701 */
4702static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
4703{
4704 HRESULT rc = S_OK;
4705 IProgress *pProgress = static_cast<IProgress *>(pvUser);
4706 if (pProgress)
4707 rc = pProgress->SetCurrentOperationProgress(uPercentage);
4708 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
4709}
4710
4711/**
4712 * @note Temporarily locks this object for writing. bird: And/or reading?
4713 */
4714HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
4715 ULONG aSourceIdx, ULONG aTargetIdx,
4716 IMedium *aSource, IMedium *aTarget,
4717 BOOL aMergeForward,
4718 IMedium *aParentForTarget,
4719 ComSafeArrayIn(IMedium *, aChildrenToReparent),
4720 IProgress *aProgress)
4721{
4722 AutoCaller autoCaller(this);
4723 AssertComRCReturnRC(autoCaller.rc());
4724
4725 HRESULT rc = S_OK;
4726 int vrc = VINF_SUCCESS;
4727
4728 /* Get the VM - must be done before the read-locking. */
4729 SafeVMPtr ptrVM(this);
4730 if (!ptrVM.isOk())
4731 return ptrVM.rc();
4732
4733 /* We will need to release the lock before doing the actual merge */
4734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4735
4736 /* paranoia - we don't want merges to happen while teleporting etc. */
4737 switch (mMachineState)
4738 {
4739 case MachineState_DeletingSnapshotOnline:
4740 case MachineState_DeletingSnapshotPaused:
4741 break;
4742
4743 default:
4744 return setInvalidMachineStateError();
4745 }
4746
4747 /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up
4748 * using uninitialized variables here. */
4749 BOOL fBuiltinIoCache;
4750 rc = mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
4751 AssertComRC(rc);
4752 SafeIfaceArray<IStorageController> ctrls;
4753 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4754 AssertComRC(rc);
4755 LONG lDev;
4756 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
4757 AssertComRC(rc);
4758 LONG lPort;
4759 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
4760 AssertComRC(rc);
4761 IMedium *pMedium;
4762 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4763 AssertComRC(rc);
4764 Bstr mediumLocation;
4765 if (pMedium)
4766 {
4767 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4768 AssertComRC(rc);
4769 }
4770
4771 Bstr attCtrlName;
4772 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4773 AssertComRC(rc);
4774 ComPtr<IStorageController> pStorageController;
4775 for (size_t i = 0; i < ctrls.size(); ++i)
4776 {
4777 Bstr ctrlName;
4778 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4779 AssertComRC(rc);
4780 if (attCtrlName == ctrlName)
4781 {
4782 pStorageController = ctrls[i];
4783 break;
4784 }
4785 }
4786 if (pStorageController.isNull())
4787 return setError(E_FAIL,
4788 tr("Could not find storage controller '%ls'"),
4789 attCtrlName.raw());
4790
4791 StorageControllerType_T enmCtrlType;
4792 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
4793 AssertComRC(rc);
4794 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
4795
4796 StorageBus_T enmBus;
4797 rc = pStorageController->COMGETTER(Bus)(&enmBus);
4798 AssertComRC(rc);
4799 ULONG uInstance;
4800 rc = pStorageController->COMGETTER(Instance)(&uInstance);
4801 AssertComRC(rc);
4802 BOOL fUseHostIOCache;
4803 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
4804 AssertComRC(rc);
4805
4806 unsigned uLUN;
4807 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
4808 AssertComRCReturnRC(rc);
4809
4810 alock.release();
4811
4812 /* Pause the VM, as it might have pending IO on this drive */
4813 VMSTATE enmVMState = VMR3GetState(ptrVM);
4814 if (mMachineState == MachineState_DeletingSnapshotOnline)
4815 {
4816 LogFlowFunc(("Suspending the VM...\n"));
4817 /* disable the callback to prevent Console-level state change */
4818 mVMStateChangeCallbackDisabled = true;
4819 int vrc2 = VMR3Suspend(ptrVM);
4820 mVMStateChangeCallbackDisabled = false;
4821 AssertRCReturn(vrc2, E_FAIL);
4822 }
4823
4824 vrc = VMR3ReqCallWait(ptrVM,
4825 VMCPUID_ANY,
4826 (PFNRT)reconfigureMediumAttachment,
4827 13,
4828 this,
4829 ptrVM.raw(),
4830 pcszDevice,
4831 uInstance,
4832 enmBus,
4833 fUseHostIOCache,
4834 fBuiltinIoCache,
4835 true /* fSetupMerge */,
4836 aSourceIdx,
4837 aTargetIdx,
4838 aMediumAttachment,
4839 mMachineState,
4840 &rc);
4841 /* error handling is after resuming the VM */
4842
4843 if (mMachineState == MachineState_DeletingSnapshotOnline)
4844 {
4845 LogFlowFunc(("Resuming the VM...\n"));
4846 /* disable the callback to prevent Console-level state change */
4847 mVMStateChangeCallbackDisabled = true;
4848 int vrc2 = VMR3Resume(ptrVM);
4849 mVMStateChangeCallbackDisabled = false;
4850 if (RT_FAILURE(vrc2))
4851 {
4852 /* too bad, we failed. try to sync the console state with the VMM state */
4853 AssertLogRelRC(vrc2);
4854 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
4855 }
4856 }
4857
4858 if (RT_FAILURE(vrc))
4859 return setError(E_FAIL, tr("%Rrc"), vrc);
4860 if (FAILED(rc))
4861 return rc;
4862
4863 PPDMIBASE pIBase = NULL;
4864 PPDMIMEDIA pIMedium = NULL;
4865 vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
4866 if (RT_SUCCESS(vrc))
4867 {
4868 if (pIBase)
4869 {
4870 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
4871 if (!pIMedium)
4872 return setError(E_FAIL, tr("could not query medium interface of controller"));
4873 }
4874 else
4875 return setError(E_FAIL, tr("could not query base interface of controller"));
4876 }
4877
4878 /* Finally trigger the merge. */
4879 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
4880 if (RT_FAILURE(vrc))
4881 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
4882
4883 /* Pause the VM, as it might have pending IO on this drive */
4884 enmVMState = VMR3GetState(ptrVM);
4885 if (mMachineState == MachineState_DeletingSnapshotOnline)
4886 {
4887 LogFlowFunc(("Suspending the VM...\n"));
4888 /* disable the callback to prevent Console-level state change */
4889 mVMStateChangeCallbackDisabled = true;
4890 int vrc2 = VMR3Suspend(ptrVM);
4891 mVMStateChangeCallbackDisabled = false;
4892 AssertRCReturn(vrc2, E_FAIL);
4893 }
4894
4895 /* Update medium chain and state now, so that the VM can continue. */
4896 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
4897 aMergeForward, aParentForTarget,
4898 ComSafeArrayInArg(aChildrenToReparent));
4899
4900 vrc = VMR3ReqCallWait(ptrVM,
4901 VMCPUID_ANY,
4902 (PFNRT)reconfigureMediumAttachment,
4903 13,
4904 this,
4905 ptrVM.raw(),
4906 pcszDevice,
4907 uInstance,
4908 enmBus,
4909 fUseHostIOCache,
4910 fBuiltinIoCache,
4911 false /* fSetupMerge */,
4912 0 /* uMergeSource */,
4913 0 /* uMergeTarget */,
4914 aMediumAttachment,
4915 mMachineState,
4916 &rc);
4917 /* error handling is after resuming the VM */
4918
4919 if (mMachineState == MachineState_DeletingSnapshotOnline)
4920 {
4921 LogFlowFunc(("Resuming the VM...\n"));
4922 /* disable the callback to prevent Console-level state change */
4923 mVMStateChangeCallbackDisabled = true;
4924 int vrc2 = VMR3Resume(ptrVM);
4925 mVMStateChangeCallbackDisabled = false;
4926 AssertRC(vrc2);
4927 if (RT_FAILURE(vrc2))
4928 {
4929 /* too bad, we failed. try to sync the console state with the VMM state */
4930 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
4931 }
4932 }
4933
4934 if (RT_FAILURE(vrc))
4935 return setError(E_FAIL, tr("%Rrc"), vrc);
4936 if (FAILED(rc))
4937 return rc;
4938
4939 return rc;
4940}
4941
4942
4943/**
4944 * Gets called by Session::UpdateMachineState()
4945 * (IInternalSessionControl::updateMachineState()).
4946 *
4947 * Must be called only in certain cases (see the implementation).
4948 *
4949 * @note Locks this object for writing.
4950 */
4951HRESULT Console::updateMachineState(MachineState_T aMachineState)
4952{
4953 AutoCaller autoCaller(this);
4954 AssertComRCReturnRC(autoCaller.rc());
4955
4956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4957
4958 AssertReturn( mMachineState == MachineState_Saving
4959 || mMachineState == MachineState_LiveSnapshotting
4960 || mMachineState == MachineState_RestoringSnapshot
4961 || mMachineState == MachineState_DeletingSnapshot
4962 || mMachineState == MachineState_DeletingSnapshotOnline
4963 || mMachineState == MachineState_DeletingSnapshotPaused
4964 , E_FAIL);
4965
4966 return setMachineStateLocally(aMachineState);
4967}
4968
4969/**
4970 * @note Locks this object for writing.
4971 */
4972void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4973 uint32_t xHot, uint32_t yHot,
4974 uint32_t width, uint32_t height,
4975 ComSafeArrayIn(BYTE,pShape))
4976{
4977#if 0
4978 LogFlowThisFuncEnter();
4979 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
4980 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4981#endif
4982
4983 AutoCaller autoCaller(this);
4984 AssertComRCReturnVoid(autoCaller.rc());
4985
4986 /* We need a write lock because we alter the cached callback data */
4987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 /* Save the callback arguments */
4990 mCallbackData.mpsc.visible = fVisible;
4991 mCallbackData.mpsc.alpha = fAlpha;
4992 mCallbackData.mpsc.xHot = xHot;
4993 mCallbackData.mpsc.yHot = yHot;
4994 mCallbackData.mpsc.width = width;
4995 mCallbackData.mpsc.height = height;
4996
4997 /* start with not valid */
4998 bool wasValid = mCallbackData.mpsc.valid;
4999 mCallbackData.mpsc.valid = false;
5000
5001 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
5002 if (aShape.size() != 0)
5003 mCallbackData.mpsc.shape.initFrom(aShape);
5004 else
5005 mCallbackData.mpsc.shape.resize(0);
5006 mCallbackData.mpsc.valid = true;
5007
5008 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
5009
5010#if 0
5011 LogFlowThisFuncLeave();
5012#endif
5013}
5014
5015/**
5016 * @note Locks this object for writing.
5017 */
5018void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
5019{
5020 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
5021 supportsAbsolute, supportsRelative, needsHostCursor));
5022
5023 AutoCaller autoCaller(this);
5024 AssertComRCReturnVoid(autoCaller.rc());
5025
5026 /* We need a write lock because we alter the cached callback data */
5027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5028
5029 /* save the callback arguments */
5030 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
5031 mCallbackData.mcc.supportsRelative = supportsRelative;
5032 mCallbackData.mcc.needsHostCursor = needsHostCursor;
5033 mCallbackData.mcc.valid = true;
5034
5035 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
5036}
5037
5038/**
5039 * @note Locks this object for reading.
5040 */
5041void Console::onStateChange(MachineState_T machineState)
5042{
5043 AutoCaller autoCaller(this);
5044 AssertComRCReturnVoid(autoCaller.rc());
5045
5046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5047 fireStateChangedEvent(mEventSource, machineState);
5048}
5049
5050/**
5051 * @note Locks this object for reading.
5052 */
5053void Console::onAdditionsStateChange()
5054{
5055 AutoCaller autoCaller(this);
5056 AssertComRCReturnVoid(autoCaller.rc());
5057
5058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5059 fireAdditionsStateChangedEvent(mEventSource);
5060}
5061
5062/**
5063 * @note Locks this object for reading.
5064 * This notification only is for reporting an incompatible
5065 * Guest Additions interface, *not* the Guest Additions version!
5066 *
5067 * The user will be notified inside the guest if new Guest
5068 * Additions are available (via VBoxTray/VBoxClient).
5069 */
5070void Console::onAdditionsOutdated()
5071{
5072 AutoCaller autoCaller(this);
5073 AssertComRCReturnVoid(autoCaller.rc());
5074
5075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5076}
5077
5078/**
5079 * @note Locks this object for writing.
5080 */
5081void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
5082{
5083 AutoCaller autoCaller(this);
5084 AssertComRCReturnVoid(autoCaller.rc());
5085
5086 /* We need a write lock because we alter the cached callback data */
5087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5088
5089 /* save the callback arguments */
5090 mCallbackData.klc.numLock = fNumLock;
5091 mCallbackData.klc.capsLock = fCapsLock;
5092 mCallbackData.klc.scrollLock = fScrollLock;
5093 mCallbackData.klc.valid = true;
5094
5095 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
5096}
5097
5098/**
5099 * @note Locks this object for reading.
5100 */
5101void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
5102 IVirtualBoxErrorInfo *aError)
5103{
5104 AutoCaller autoCaller(this);
5105 AssertComRCReturnVoid(autoCaller.rc());
5106
5107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5108 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
5109}
5110
5111/**
5112 * @note Locks this object for reading.
5113 */
5114void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
5115{
5116 AutoCaller autoCaller(this);
5117 AssertComRCReturnVoid(autoCaller.rc());
5118
5119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5120 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
5121}
5122
5123/**
5124 * @note Locks this object for reading.
5125 */
5126HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
5127{
5128 AssertReturn(aCanShow, E_POINTER);
5129 AssertReturn(aWinId, E_POINTER);
5130
5131 *aCanShow = FALSE;
5132 *aWinId = 0;
5133
5134 AutoCaller autoCaller(this);
5135 AssertComRCReturnRC(autoCaller.rc());
5136
5137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5138 VBoxEventDesc evDesc;
5139
5140 if (aCheck)
5141 {
5142 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
5143 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5144 //Assert(fDelivered);
5145 if (fDelivered)
5146 {
5147 ComPtr<IEvent> pEvent;
5148 evDesc.getEvent(pEvent.asOutParam());
5149 // bit clumsy
5150 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
5151 if (pCanShowEvent)
5152 {
5153 BOOL fVetoed = FALSE;
5154 pCanShowEvent->IsVetoed(&fVetoed);
5155 *aCanShow = !fVetoed;
5156 }
5157 else
5158 {
5159 Assert(FALSE);
5160 *aCanShow = TRUE;
5161 }
5162 }
5163 else
5164 *aCanShow = TRUE;
5165 }
5166 else
5167 {
5168 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
5169 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5170 //Assert(fDelivered);
5171 if (fDelivered)
5172 {
5173 ComPtr<IEvent> pEvent;
5174 evDesc.getEvent(pEvent.asOutParam());
5175 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
5176 LONG64 aEvWinId = 0;
5177 if (pShowEvent)
5178 {
5179 pShowEvent->COMGETTER(WinId)(&aEvWinId);
5180 if ((aEvWinId != 0) && (*aWinId == 0))
5181 *aWinId = aEvWinId;
5182 }
5183 else
5184 Assert(FALSE);
5185 }
5186 }
5187
5188 return S_OK;
5189}
5190
5191// private methods
5192////////////////////////////////////////////////////////////////////////////////
5193
5194/**
5195 * Increases the usage counter of the mpVM pointer. Guarantees that
5196 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
5197 * is called.
5198 *
5199 * If this method returns a failure, the caller is not allowed to use mpVM
5200 * and may return the failed result code to the upper level. This method sets
5201 * the extended error info on failure if \a aQuiet is false.
5202 *
5203 * Setting \a aQuiet to true is useful for methods that don't want to return
5204 * the failed result code to the caller when this method fails (e.g. need to
5205 * silently check for the mpVM availability).
5206 *
5207 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
5208 * returned instead of asserting. Having it false is intended as a sanity check
5209 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
5210 *
5211 * @param aQuiet true to suppress setting error info
5212 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
5213 * (otherwise this method will assert if mpVM is NULL)
5214 *
5215 * @note Locks this object for writing.
5216 */
5217HRESULT Console::addVMCaller(bool aQuiet /* = false */,
5218 bool aAllowNullVM /* = false */)
5219{
5220 AutoCaller autoCaller(this);
5221 AssertComRCReturnRC(autoCaller.rc());
5222
5223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5224
5225 if (mVMDestroying)
5226 {
5227 /* powerDown() is waiting for all callers to finish */
5228 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5229 tr("The virtual machine is being powered down"));
5230 }
5231
5232 if (mpUVM == NULL)
5233 {
5234 Assert(aAllowNullVM == true);
5235
5236 /* The machine is not powered up */
5237 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5238 tr("The virtual machine is not powered up"));
5239 }
5240
5241 ++mVMCallers;
5242
5243 return S_OK;
5244}
5245
5246/**
5247 * Decreases the usage counter of the mpVM pointer. Must always complete
5248 * the addVMCaller() call after the mpVM pointer is no more necessary.
5249 *
5250 * @note Locks this object for writing.
5251 */
5252void Console::releaseVMCaller()
5253{
5254 AutoCaller autoCaller(this);
5255 AssertComRCReturnVoid(autoCaller.rc());
5256
5257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5258
5259 AssertReturnVoid(mpUVM != NULL);
5260
5261 Assert(mVMCallers > 0);
5262 --mVMCallers;
5263
5264 if (mVMCallers == 0 && mVMDestroying)
5265 {
5266 /* inform powerDown() there are no more callers */
5267 RTSemEventSignal(mVMZeroCallersSem);
5268 }
5269}
5270
5271
5272HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
5273{
5274 *a_ppVM = NULL;
5275 *a_ppUVM = NULL;
5276
5277 AutoCaller autoCaller(this);
5278 AssertComRCReturnRC(autoCaller.rc());
5279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5280
5281 /*
5282 * Repeat the checks done by addVMCaller.
5283 */
5284 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
5285 return a_Quiet
5286 ? E_ACCESSDENIED
5287 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
5288 PUVM pUVM = mpUVM;
5289 if (!pUVM)
5290 return a_Quiet
5291 ? E_ACCESSDENIED
5292 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5293
5294 /*
5295 * Retain a reference to the user mode VM handle and get the global handle.
5296 */
5297 uint32_t cRefs = VMR3RetainUVM(pUVM);
5298 if (cRefs == UINT32_MAX)
5299 return a_Quiet
5300 ? E_ACCESSDENIED
5301 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5302
5303 PVM pVM = VMR3GetVM(pUVM);
5304 if (!pVM)
5305 {
5306 VMR3ReleaseUVM(pUVM);
5307 return a_Quiet
5308 ? E_ACCESSDENIED
5309 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
5310 }
5311
5312 /* done */
5313 *a_ppVM = pVM;
5314 *a_ppUVM = pUVM;
5315 return S_OK;
5316}
5317
5318void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
5319{
5320 if (*a_ppVM && *a_ppUVM)
5321 VMR3ReleaseUVM(*a_ppUVM);
5322 *a_ppVM = NULL;
5323 *a_ppUVM = NULL;
5324}
5325
5326
5327/**
5328 * Initialize the release logging facility. In case something
5329 * goes wrong, there will be no release logging. Maybe in the future
5330 * we can add some logic to use different file names in this case.
5331 * Note that the logic must be in sync with Machine::DeleteSettings().
5332 */
5333HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
5334{
5335 HRESULT hrc = S_OK;
5336
5337 Bstr logFolder;
5338 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
5339 if (FAILED(hrc)) return hrc;
5340
5341 Utf8Str logDir = logFolder;
5342
5343 /* make sure the Logs folder exists */
5344 Assert(logDir.length());
5345 if (!RTDirExists(logDir.c_str()))
5346 RTDirCreateFullPath(logDir.c_str(), 0777);
5347
5348 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
5349 logDir.c_str(), RTPATH_DELIMITER);
5350 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
5351 logDir.c_str(), RTPATH_DELIMITER);
5352
5353 /*
5354 * Age the old log files
5355 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
5356 * Overwrite target files in case they exist.
5357 */
5358 ComPtr<IVirtualBox> pVirtualBox;
5359 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
5360 ComPtr<ISystemProperties> pSystemProperties;
5361 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
5362 ULONG cHistoryFiles = 3;
5363 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
5364 if (cHistoryFiles)
5365 {
5366 for (int i = cHistoryFiles-1; i >= 0; i--)
5367 {
5368 Utf8Str *files[] = { &logFile, &pngFile };
5369 Utf8Str oldName, newName;
5370
5371 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
5372 {
5373 if (i > 0)
5374 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
5375 else
5376 oldName = *files[j];
5377 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
5378 /* If the old file doesn't exist, delete the new file (if it
5379 * exists) to provide correct rotation even if the sequence is
5380 * broken */
5381 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
5382 == VERR_FILE_NOT_FOUND)
5383 RTFileDelete(newName.c_str());
5384 }
5385 }
5386 }
5387
5388 PRTLOGGER loggerRelease;
5389 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
5390 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
5391#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5392 fFlags |= RTLOGFLAGS_USECRLF;
5393#endif
5394 char szError[RTPATH_MAX + 128] = "";
5395 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
5396 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_FILE,
5397 NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */,
5398 szError, sizeof(szError), logFile.c_str());
5399 if (RT_SUCCESS(vrc))
5400 {
5401 /* some introductory information */
5402 RTTIMESPEC timeSpec;
5403 char szTmp[256];
5404 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
5405 RTLogRelLogger(loggerRelease, 0, ~0U,
5406 "VirtualBox %s r%u %s (%s %s) release log\n"
5407#ifdef VBOX_BLEEDING_EDGE
5408 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
5409#endif
5410 "Log opened %s\n",
5411 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
5412 __DATE__, __TIME__, szTmp);
5413
5414 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
5415 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5416 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
5417 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
5418 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5419 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
5420 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
5421 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5422 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
5423 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
5424 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5425 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
5426 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
5427 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5428 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp);
5429 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
5430 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5431 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp);
5432
5433 ComPtr<IHost> pHost;
5434 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
5435 ULONG cMbHostRam = 0;
5436 ULONG cMbHostRamAvail = 0;
5437 pHost->COMGETTER(MemorySize)(&cMbHostRam);
5438 pHost->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5439 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5440 cMbHostRam, cMbHostRamAvail);
5441
5442 /* the package type is interesting for Linux distributions */
5443 char szExecName[RTPATH_MAX];
5444 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
5445 RTLogRelLogger(loggerRelease, 0, ~0U,
5446 "Executable: %s\n"
5447 "Process ID: %u\n"
5448 "Package type: %s"
5449#ifdef VBOX_OSE
5450 " (OSE)"
5451#endif
5452 "\n",
5453 pszExecName ? pszExecName : "unknown",
5454 RTProcSelf(),
5455 VBOX_PACKAGE_STRING);
5456
5457 /* register this logger as the release logger */
5458 RTLogRelSetDefaultInstance(loggerRelease);
5459 hrc = S_OK;
5460
5461 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5462 RTLogFlush(loggerRelease);
5463 }
5464 else
5465 hrc = setError(E_FAIL,
5466 tr("Failed to open release log (%s, %Rrc)"),
5467 szError, vrc);
5468
5469 /* If we've made any directory changes, flush the directory to increase
5470 the likelihood that the log file will be usable after a system panic.
5471
5472 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5473 is missing. Just don't have too high hopes for this to help. */
5474 if (SUCCEEDED(hrc) || cHistoryFiles)
5475 RTDirFlush(logDir.c_str());
5476
5477 return hrc;
5478}
5479
5480/**
5481 * Common worker for PowerUp and PowerUpPaused.
5482 *
5483 * @returns COM status code.
5484 *
5485 * @param aProgress Where to return the progress object.
5486 * @param aPaused true if PowerUpPaused called.
5487 */
5488HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5489{
5490 LogFlowThisFuncEnter();
5491 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5492
5493 CheckComArgOutPointerValid(aProgress);
5494
5495 AutoCaller autoCaller(this);
5496 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5497
5498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5499
5500 HRESULT rc = S_OK;
5501 ComObjPtr<Progress> pPowerupProgress;
5502 bool fBeganPoweringUp = false;
5503
5504 try
5505 {
5506 if (Global::IsOnlineOrTransient(mMachineState))
5507 throw setError(VBOX_E_INVALID_VM_STATE,
5508 tr("The virtual machine is already running or busy (machine state: %s)"),
5509 Global::stringifyMachineState(mMachineState));
5510
5511 /* test and clear the TeleporterEnabled property */
5512 BOOL fTeleporterEnabled;
5513 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5514 if (FAILED(rc))
5515 throw rc;
5516#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5517 if (fTeleporterEnabled)
5518 {
5519 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5520 if (FAILED(rc))
5521 throw rc;
5522 }
5523#endif
5524
5525 /* test the FaultToleranceState property */
5526 FaultToleranceState_T enmFaultToleranceState;
5527 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
5528 if (FAILED(rc))
5529 throw rc;
5530 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
5531
5532 /* Create a progress object to track progress of this operation. Must
5533 * be done as early as possible (together with BeginPowerUp()) as this
5534 * is vital for communicating as much as possible early powerup
5535 * failure information to the API caller */
5536 pPowerupProgress.createObject();
5537 Bstr progressDesc;
5538 if (mMachineState == MachineState_Saved)
5539 progressDesc = tr("Restoring virtual machine");
5540 else if (fTeleporterEnabled)
5541 progressDesc = tr("Teleporting virtual machine");
5542 else if (fFaultToleranceSyncEnabled)
5543 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
5544 else
5545 progressDesc = tr("Starting virtual machine");
5546 if ( mMachineState == MachineState_Saved
5547 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
5548 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
5549 progressDesc.raw(),
5550 FALSE /* aCancelable */);
5551 else
5552 if (fTeleporterEnabled)
5553 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
5554 progressDesc.raw(),
5555 TRUE /* aCancelable */,
5556 3 /* cOperations */,
5557 10 /* ulTotalOperationsWeight */,
5558 Bstr(tr("Teleporting virtual machine")).raw(),
5559 1 /* ulFirstOperationWeight */,
5560 NULL);
5561 else
5562 if (fFaultToleranceSyncEnabled)
5563 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
5564 progressDesc.raw(),
5565 TRUE /* aCancelable */,
5566 3 /* cOperations */,
5567 10 /* ulTotalOperationsWeight */,
5568 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
5569 1 /* ulFirstOperationWeight */,
5570 NULL);
5571
5572 if (FAILED(rc))
5573 throw rc;
5574
5575 /* Tell VBoxSVC and Machine about the progress object so they can
5576 combine/proxy it to any openRemoteSession caller. */
5577 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
5578 rc = mControl->BeginPowerUp(pPowerupProgress);
5579 if (FAILED(rc))
5580 {
5581 LogFlowThisFunc(("BeginPowerUp failed\n"));
5582 throw rc;
5583 }
5584 fBeganPoweringUp = true;
5585
5586 /** @todo this code prevents starting a VM with unavailable bridged
5587 * networking interface. The only benefit is a slightly better error
5588 * message, which should be moved to the driver code. This is the
5589 * only reason why I left the code in for now. The driver allows
5590 * unavailable bridged networking interfaces in certain circumstances,
5591 * and this is sabotaged by this check. The VM will initially have no
5592 * network connectivity, but the user can fix this at runtime. */
5593#if 0
5594 /* the network cards will undergo a quick consistency check */
5595 for (ULONG slot = 0;
5596 slot < SchemaDefs::NetworkAdapterCount;
5597 ++slot)
5598 {
5599 ComPtr<INetworkAdapter> pNetworkAdapter;
5600 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
5601 BOOL enabled = FALSE;
5602 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
5603 if (!enabled)
5604 continue;
5605
5606 NetworkAttachmentType_T netattach;
5607 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
5608 switch (netattach)
5609 {
5610 case NetworkAttachmentType_Bridged:
5611 {
5612 /* a valid host interface must have been set */
5613 Bstr hostif;
5614 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
5615 if (hostif.isEmpty())
5616 {
5617 throw setError(VBOX_E_HOST_ERROR,
5618 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5619 }
5620 ComPtr<IVirtualBox> pVirtualBox;
5621 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
5622 ComPtr<IHost> pHost;
5623 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
5624 ComPtr<IHostNetworkInterface> pHostInterface;
5625 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
5626 pHostInterface.asOutParam())))
5627 {
5628 throw setError(VBOX_E_HOST_ERROR,
5629 tr("VM cannot start because the host interface '%ls' does not exist"),
5630 hostif.raw());
5631 }
5632 break;
5633 }
5634 default:
5635 break;
5636 }
5637 }
5638#endif // 0
5639
5640 /* Read console data stored in the saved state file (if not yet done) */
5641 rc = loadDataFromSavedState();
5642 if (FAILED(rc))
5643 throw rc;
5644
5645 /* Check all types of shared folders and compose a single list */
5646 SharedFolderDataMap sharedFolders;
5647 {
5648 /* first, insert global folders */
5649 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
5650 it != m_mapGlobalSharedFolders.end();
5651 ++it)
5652 {
5653 const SharedFolderData &d = it->second;
5654 sharedFolders[it->first] = d;
5655 }
5656
5657 /* second, insert machine folders */
5658 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
5659 it != m_mapMachineSharedFolders.end();
5660 ++it)
5661 {
5662 const SharedFolderData &d = it->second;
5663 sharedFolders[it->first] = d;
5664 }
5665
5666 /* third, insert console folders */
5667 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
5668 it != m_mapSharedFolders.end();
5669 ++it)
5670 {
5671 SharedFolder *pSF = it->second;
5672 AutoCaller sfCaller(pSF);
5673 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
5674 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
5675 pSF->isWritable(),
5676 pSF->isAutoMounted());
5677 }
5678 }
5679
5680 Bstr savedStateFile;
5681
5682 /*
5683 * Saved VMs will have to prove that their saved states seem kosher.
5684 */
5685 if (mMachineState == MachineState_Saved)
5686 {
5687 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5688 if (FAILED(rc))
5689 throw rc;
5690 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
5691 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5692 if (RT_FAILURE(vrc))
5693 throw setError(VBOX_E_FILE_ERROR,
5694 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5695 savedStateFile.raw(), vrc);
5696 }
5697
5698 LogFlowThisFunc(("Checking if canceled...\n"));
5699 BOOL fCanceled;
5700 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
5701 if (FAILED(rc))
5702 throw rc;
5703 if (fCanceled)
5704 {
5705 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5706 throw setError(E_FAIL, tr("Powerup was canceled"));
5707 }
5708 LogFlowThisFunc(("Not canceled yet.\n"));
5709
5710 /* setup task object and thread to carry out the operation
5711 * asynchronously */
5712
5713 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
5714 ComAssertComRCRetRC(task->rc());
5715
5716 task->mConfigConstructor = configConstructor;
5717 task->mSharedFolders = sharedFolders;
5718 task->mStartPaused = aPaused;
5719 if (mMachineState == MachineState_Saved)
5720 task->mSavedStateFile = savedStateFile;
5721 task->mTeleporterEnabled = fTeleporterEnabled;
5722 task->mEnmFaultToleranceState = enmFaultToleranceState;
5723
5724 /* Reset differencing hard disks for which autoReset is true,
5725 * but only if the machine has no snapshots OR the current snapshot
5726 * is an OFFLINE snapshot; otherwise we would reset the current
5727 * differencing image of an ONLINE snapshot which contains the disk
5728 * state of the machine while it was previously running, but without
5729 * the corresponding machine state, which is equivalent to powering
5730 * off a running machine and not good idea
5731 */
5732 ComPtr<ISnapshot> pCurrentSnapshot;
5733 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5734 if (FAILED(rc))
5735 throw rc;
5736
5737 BOOL fCurrentSnapshotIsOnline = false;
5738 if (pCurrentSnapshot)
5739 {
5740 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5741 if (FAILED(rc))
5742 throw rc;
5743 }
5744
5745 if (!fCurrentSnapshotIsOnline)
5746 {
5747 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5748
5749 com::SafeIfaceArray<IMediumAttachment> atts;
5750 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5751 if (FAILED(rc))
5752 throw rc;
5753
5754 for (size_t i = 0;
5755 i < atts.size();
5756 ++i)
5757 {
5758 DeviceType_T devType;
5759 rc = atts[i]->COMGETTER(Type)(&devType);
5760 /** @todo later applies to floppies as well */
5761 if (devType == DeviceType_HardDisk)
5762 {
5763 ComPtr<IMedium> pMedium;
5764 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
5765 if (FAILED(rc))
5766 throw rc;
5767
5768 /* needs autoreset? */
5769 BOOL autoReset = FALSE;
5770 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
5771 if (FAILED(rc))
5772 throw rc;
5773
5774 if (autoReset)
5775 {
5776 ComPtr<IProgress> pResetProgress;
5777 rc = pMedium->Reset(pResetProgress.asOutParam());
5778 if (FAILED(rc))
5779 throw rc;
5780
5781 /* save for later use on the powerup thread */
5782 task->hardDiskProgresses.push_back(pResetProgress);
5783 }
5784 }
5785 }
5786 }
5787 else
5788 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5789
5790 rc = consoleInitReleaseLog(mMachine);
5791 if (FAILED(rc))
5792 throw rc;
5793
5794#ifdef RT_OS_SOLARIS
5795 /* setup host core dumper for the VM */
5796 Bstr value;
5797 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
5798 if (SUCCEEDED(hrc) && value == "1")
5799 {
5800 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
5801 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
5802 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
5803 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
5804
5805 uint32_t fCoreFlags = 0;
5806 if ( coreDumpReplaceSys.isEmpty() == false
5807 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
5808 {
5809 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
5810 }
5811
5812 if ( coreDumpLive.isEmpty() == false
5813 && Utf8Str(coreDumpLive).toUInt32() == 1)
5814 {
5815 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
5816 }
5817
5818 Utf8Str strDumpDir(coreDumpDir);
5819 const char *pszDumpDir = strDumpDir.c_str();
5820 if ( pszDumpDir
5821 && *pszDumpDir == '\0')
5822 pszDumpDir = NULL;
5823
5824 int vrc;
5825 if ( pszDumpDir
5826 && !RTDirExists(pszDumpDir))
5827 {
5828 /*
5829 * Try create the directory.
5830 */
5831 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
5832 if (RT_FAILURE(vrc))
5833 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
5834 }
5835
5836 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
5837 if (RT_FAILURE(vrc))
5838 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
5839 else
5840 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
5841 }
5842#endif
5843
5844 /* pass the progress object to the caller if requested */
5845 if (aProgress)
5846 {
5847 if (task->hardDiskProgresses.size() == 0)
5848 {
5849 /* there are no other operations to track, return the powerup
5850 * progress only */
5851 pPowerupProgress.queryInterfaceTo(aProgress);
5852 }
5853 else
5854 {
5855 /* create a combined progress object */
5856 ComObjPtr<CombinedProgress> pProgress;
5857 pProgress.createObject();
5858 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5859 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
5860 rc = pProgress->init(static_cast<IConsole *>(this),
5861 progressDesc.raw(), progresses.begin(),
5862 progresses.end());
5863 AssertComRCReturnRC(rc);
5864 pProgress.queryInterfaceTo(aProgress);
5865 }
5866 }
5867
5868 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
5869 (void *)task.get(), 0,
5870 RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5871 if (RT_FAILURE(vrc))
5872 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
5873
5874 /* task is now owned by powerUpThread(), so release it */
5875 task.release();
5876
5877 /* finally, set the state: no right to fail in this method afterwards
5878 * since we've already started the thread and it is now responsible for
5879 * any error reporting and appropriate state change! */
5880 if (mMachineState == MachineState_Saved)
5881 setMachineState(MachineState_Restoring);
5882 else if (fTeleporterEnabled)
5883 setMachineState(MachineState_TeleportingIn);
5884 else if (enmFaultToleranceState == FaultToleranceState_Standby)
5885 setMachineState(MachineState_FaultTolerantSyncing);
5886 else
5887 setMachineState(MachineState_Starting);
5888 }
5889 catch (HRESULT aRC) { rc = aRC; }
5890
5891 if (FAILED(rc) && fBeganPoweringUp)
5892 {
5893
5894 /* The progress object will fetch the current error info */
5895 if (!pPowerupProgress.isNull())
5896 pPowerupProgress->notifyComplete(rc);
5897
5898 /* Save the error info across the IPC below. Can't be done before the
5899 * progress notification above, as saving the error info deletes it
5900 * from the current context, and thus the progress object wouldn't be
5901 * updated correctly. */
5902 ErrorInfoKeeper eik;
5903
5904 /* signal end of operation */
5905 mControl->EndPowerUp(rc);
5906 }
5907
5908 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
5909 LogFlowThisFuncLeave();
5910 return rc;
5911}
5912
5913/**
5914 * Internal power off worker routine.
5915 *
5916 * This method may be called only at certain places with the following meaning
5917 * as shown below:
5918 *
5919 * - if the machine state is either Running or Paused, a normal
5920 * Console-initiated powerdown takes place (e.g. PowerDown());
5921 * - if the machine state is Saving, saveStateThread() has successfully done its
5922 * job;
5923 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5924 * to start/load the VM;
5925 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5926 * as a result of the powerDown() call).
5927 *
5928 * Calling it in situations other than the above will cause unexpected behavior.
5929 *
5930 * Note that this method should be the only one that destroys mpVM and sets it
5931 * to NULL.
5932 *
5933 * @param aProgress Progress object to run (may be NULL).
5934 *
5935 * @note Locks this object for writing.
5936 *
5937 * @note Never call this method from a thread that called addVMCaller() or
5938 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5939 * release(). Otherwise it will deadlock.
5940 */
5941HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
5942{
5943 LogFlowThisFuncEnter();
5944
5945 AutoCaller autoCaller(this);
5946 AssertComRCReturnRC(autoCaller.rc());
5947
5948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5949
5950 /* Total # of steps for the progress object. Must correspond to the
5951 * number of "advance percent count" comments in this method! */
5952 enum { StepCount = 7 };
5953 /* current step */
5954 ULONG step = 0;
5955
5956 HRESULT rc = S_OK;
5957 int vrc = VINF_SUCCESS;
5958
5959 /* sanity */
5960 Assert(mVMDestroying == false);
5961
5962 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
5963 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
5964
5965 AssertMsg( mMachineState == MachineState_Running
5966 || mMachineState == MachineState_Paused
5967 || mMachineState == MachineState_Stuck
5968 || mMachineState == MachineState_Starting
5969 || mMachineState == MachineState_Stopping
5970 || mMachineState == MachineState_Saving
5971 || mMachineState == MachineState_Restoring
5972 || mMachineState == MachineState_TeleportingPausedVM
5973 || mMachineState == MachineState_FaultTolerantSyncing
5974 || mMachineState == MachineState_TeleportingIn
5975 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5976
5977 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5978 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5979
5980 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5981 * VM has already powered itself off in vmstateChangeCallback() and is just
5982 * notifying Console about that. In case of Starting or Restoring,
5983 * powerUpThread() is calling us on failure, so the VM is already off at
5984 * that point. */
5985 if ( !mVMPoweredOff
5986 && ( mMachineState == MachineState_Starting
5987 || mMachineState == MachineState_Restoring
5988 || mMachineState == MachineState_FaultTolerantSyncing
5989 || mMachineState == MachineState_TeleportingIn)
5990 )
5991 mVMPoweredOff = true;
5992
5993 /*
5994 * Go to Stopping state if not already there.
5995 *
5996 * Note that we don't go from Saving/Restoring to Stopping because
5997 * vmstateChangeCallback() needs it to set the state to Saved on
5998 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5999 * while leaving the lock below, Saving or Restoring should be fine too.
6000 * Ditto for TeleportingPausedVM -> Teleported.
6001 */
6002 if ( mMachineState != MachineState_Saving
6003 && mMachineState != MachineState_Restoring
6004 && mMachineState != MachineState_Stopping
6005 && mMachineState != MachineState_TeleportingIn
6006 && mMachineState != MachineState_TeleportingPausedVM
6007 && mMachineState != MachineState_FaultTolerantSyncing
6008 )
6009 setMachineState(MachineState_Stopping);
6010
6011 /* ----------------------------------------------------------------------
6012 * DONE with necessary state changes, perform the power down actions (it's
6013 * safe to leave the object lock now if needed)
6014 * ---------------------------------------------------------------------- */
6015
6016 /* Stop the VRDP server to prevent new clients connection while VM is being
6017 * powered off. */
6018 if (mConsoleVRDPServer)
6019 {
6020 LogFlowThisFunc(("Stopping VRDP server...\n"));
6021
6022 /* Leave the lock since EMT will call us back as addVMCaller()
6023 * in updateDisplayData(). */
6024 alock.leave();
6025
6026 mConsoleVRDPServer->Stop();
6027
6028 alock.enter();
6029 }
6030
6031 /* advance percent count */
6032 if (aProgress)
6033 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6034
6035
6036 /* ----------------------------------------------------------------------
6037 * Now, wait for all mpVM callers to finish their work if there are still
6038 * some on other threads. NO methods that need mpVM (or initiate other calls
6039 * that need it) may be called after this point
6040 * ---------------------------------------------------------------------- */
6041
6042 /* go to the destroying state to prevent from adding new callers */
6043 mVMDestroying = true;
6044
6045 if (mVMCallers > 0)
6046 {
6047 /* lazy creation */
6048 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6049 RTSemEventCreate(&mVMZeroCallersSem);
6050
6051 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6052 mVMCallers));
6053
6054 alock.leave();
6055
6056 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6057
6058 alock.enter();
6059 }
6060
6061 /* advance percent count */
6062 if (aProgress)
6063 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6064
6065 vrc = VINF_SUCCESS;
6066
6067 /*
6068 * Power off the VM if not already done that.
6069 * Leave the lock since EMT will call vmstateChangeCallback.
6070 *
6071 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6072 * VM-(guest-)initiated power off happened in parallel a ms before this
6073 * call. So far, we let this error pop up on the user's side.
6074 */
6075 if (!mVMPoweredOff)
6076 {
6077 LogFlowThisFunc(("Powering off the VM...\n"));
6078 alock.leave();
6079 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6080#ifdef VBOX_WITH_EXTPACK
6081 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6082#endif
6083 alock.enter();
6084 }
6085
6086 /* advance percent count */
6087 if (aProgress)
6088 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6089
6090#ifdef VBOX_WITH_HGCM
6091 /* Shutdown HGCM services before destroying the VM. */
6092 if (m_pVMMDev)
6093 {
6094 LogFlowThisFunc(("Shutdown HGCM...\n"));
6095
6096 /* Leave the lock since EMT will call us back as addVMCaller() */
6097 alock.leave();
6098
6099 m_pVMMDev->hgcmShutdown();
6100
6101 alock.enter();
6102 }
6103
6104 /* advance percent count */
6105 if (aProgress)
6106 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6107
6108#endif /* VBOX_WITH_HGCM */
6109
6110 LogFlowThisFunc(("Ready for VM destruction.\n"));
6111
6112 /* If we are called from Console::uninit(), then try to destroy the VM even
6113 * on failure (this will most likely fail too, but what to do?..) */
6114 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6115 {
6116 /* If the machine has an USB controller, release all USB devices
6117 * (symmetric to the code in captureUSBDevices()) */
6118 bool fHasUSBController = false;
6119 {
6120 PPDMIBASE pBase;
6121 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6122 if (RT_SUCCESS(vrc))
6123 {
6124 fHasUSBController = true;
6125 detachAllUSBDevices(false /* aDone */);
6126 }
6127 }
6128
6129 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6130 * this point). We leave the lock before calling VMR3Destroy() because
6131 * it will result into calling destructors of drivers associated with
6132 * Console children which may in turn try to lock Console (e.g. by
6133 * instantiating SafeVMPtr to access mpVM). It's safe here because
6134 * mVMDestroying is set which should prevent any activity. */
6135
6136 /* Set mpUVM to NULL early just in case if some old code is not using
6137 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6138 VMR3ReleaseUVM(mpUVM);
6139 mpUVM = NULL;
6140
6141 LogFlowThisFunc(("Destroying the VM...\n"));
6142
6143 alock.leave();
6144
6145 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6146
6147 /* take the lock again */
6148 alock.enter();
6149
6150 /* advance percent count */
6151 if (aProgress)
6152 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6153
6154 if (RT_SUCCESS(vrc))
6155 {
6156 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6157 mMachineState));
6158 /* Note: the Console-level machine state change happens on the
6159 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6160 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6161 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6162 * occurred yet. This is okay, because mMachineState is already
6163 * Stopping in this case, so any other attempt to call PowerDown()
6164 * will be rejected. */
6165 }
6166 else
6167 {
6168 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6169 mpUVM = pUVM;
6170 pUVM = NULL;
6171 rc = setError(VBOX_E_VM_ERROR,
6172 tr("Could not destroy the machine. (Error: %Rrc)"),
6173 vrc);
6174 }
6175
6176 /* Complete the detaching of the USB devices. */
6177 if (fHasUSBController)
6178 detachAllUSBDevices(true /* aDone */);
6179
6180 /* advance percent count */
6181 if (aProgress)
6182 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
6183 }
6184 else
6185 {
6186 rc = setError(VBOX_E_VM_ERROR,
6187 tr("Could not power off the machine. (Error: %Rrc)"),
6188 vrc);
6189 }
6190
6191 /*
6192 * Finished with the destruction.
6193 *
6194 * Note that if something impossible happened and we've failed to destroy
6195 * the VM, mVMDestroying will remain true and mMachineState will be
6196 * something like Stopping, so most Console methods will return an error
6197 * to the caller.
6198 */
6199 if (mpUVM != NULL)
6200 VMR3ReleaseUVM(pUVM);
6201 else
6202 mVMDestroying = false;
6203
6204 if (SUCCEEDED(rc))
6205 mCallbackData.clear();
6206
6207 LogFlowThisFuncLeave();
6208 return rc;
6209}
6210
6211/**
6212 * @note Locks this object for writing.
6213 */
6214HRESULT Console::setMachineState(MachineState_T aMachineState,
6215 bool aUpdateServer /* = true */)
6216{
6217 AutoCaller autoCaller(this);
6218 AssertComRCReturnRC(autoCaller.rc());
6219
6220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6221
6222 HRESULT rc = S_OK;
6223
6224 if (mMachineState != aMachineState)
6225 {
6226 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6227 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6228 mMachineState = aMachineState;
6229
6230 /// @todo (dmik)
6231 // possibly, we need to redo onStateChange() using the dedicated
6232 // Event thread, like it is done in VirtualBox. This will make it
6233 // much safer (no deadlocks possible if someone tries to use the
6234 // console from the callback), however, listeners will lose the
6235 // ability to synchronously react to state changes (is it really
6236 // necessary??)
6237 LogFlowThisFunc(("Doing onStateChange()...\n"));
6238 onStateChange(aMachineState);
6239 LogFlowThisFunc(("Done onStateChange()\n"));
6240
6241 if (aUpdateServer)
6242 {
6243 /* Server notification MUST be done from under the lock; otherwise
6244 * the machine state here and on the server might go out of sync
6245 * which can lead to various unexpected results (like the machine
6246 * state being >= MachineState_Running on the server, while the
6247 * session state is already SessionState_Unlocked at the same time
6248 * there).
6249 *
6250 * Cross-lock conditions should be carefully watched out: calling
6251 * UpdateState we will require Machine and SessionMachine locks
6252 * (remember that here we're holding the Console lock here, and also
6253 * all locks that have been entered by the thread before calling
6254 * this method).
6255 */
6256 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
6257 rc = mControl->UpdateState(aMachineState);
6258 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
6259 }
6260 }
6261
6262 return rc;
6263}
6264
6265/**
6266 * Searches for a shared folder with the given logical name
6267 * in the collection of shared folders.
6268 *
6269 * @param aName logical name of the shared folder
6270 * @param aSharedFolder where to return the found object
6271 * @param aSetError whether to set the error info if the folder is
6272 * not found
6273 * @return
6274 * S_OK when found or E_INVALIDARG when not found
6275 *
6276 * @note The caller must lock this object for writing.
6277 */
6278HRESULT Console::findSharedFolder(const Utf8Str &strName,
6279 ComObjPtr<SharedFolder> &aSharedFolder,
6280 bool aSetError /* = false */)
6281{
6282 /* sanity check */
6283 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6284
6285 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
6286 if (it != m_mapSharedFolders.end())
6287 {
6288 aSharedFolder = it->second;
6289 return S_OK;
6290 }
6291
6292 if (aSetError)
6293 setError(VBOX_E_FILE_ERROR,
6294 tr("Could not find a shared folder named '%s'."),
6295 strName.c_str());
6296
6297 return VBOX_E_FILE_ERROR;
6298}
6299
6300/**
6301 * Fetches the list of global or machine shared folders from the server.
6302 *
6303 * @param aGlobal true to fetch global folders.
6304 *
6305 * @note The caller must lock this object for writing.
6306 */
6307HRESULT Console::fetchSharedFolders(BOOL aGlobal)
6308{
6309 /* sanity check */
6310 AssertReturn(AutoCaller(this).state() == InInit ||
6311 isWriteLockOnCurrentThread(), E_FAIL);
6312
6313 LogFlowThisFunc(("Entering\n"));
6314
6315 /* Check if we're online and keep it that way. */
6316 SafeVMPtrQuiet ptrVM(this);
6317 AutoVMCallerQuietWeak autoVMCaller(this);
6318 bool const online = ptrVM.isOk()
6319 && m_pVMMDev
6320 && m_pVMMDev->isShFlActive();
6321
6322 HRESULT rc = S_OK;
6323
6324 try
6325 {
6326 if (aGlobal)
6327 {
6328 /// @todo grab & process global folders when they are done
6329 }
6330 else
6331 {
6332 SharedFolderDataMap oldFolders;
6333 if (online)
6334 oldFolders = m_mapMachineSharedFolders;
6335
6336 m_mapMachineSharedFolders.clear();
6337
6338 SafeIfaceArray<ISharedFolder> folders;
6339 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6340 if (FAILED(rc)) throw rc;
6341
6342 for (size_t i = 0; i < folders.size(); ++i)
6343 {
6344 ComPtr<ISharedFolder> pSharedFolder = folders[i];
6345
6346 Bstr bstrName;
6347 Bstr bstrHostPath;
6348 BOOL writable;
6349 BOOL autoMount;
6350
6351 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
6352 if (FAILED(rc)) throw rc;
6353 Utf8Str strName(bstrName);
6354
6355 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
6356 if (FAILED(rc)) throw rc;
6357 Utf8Str strHostPath(bstrHostPath);
6358
6359 rc = pSharedFolder->COMGETTER(Writable)(&writable);
6360 if (FAILED(rc)) throw rc;
6361
6362 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
6363 if (FAILED(rc)) throw rc;
6364
6365 m_mapMachineSharedFolders.insert(std::make_pair(strName,
6366 SharedFolderData(strHostPath, writable, autoMount)));
6367
6368 /* send changes to HGCM if the VM is running */
6369 if (online)
6370 {
6371 SharedFolderDataMap::iterator it = oldFolders.find(strName);
6372 if ( it == oldFolders.end()
6373 || it->second.m_strHostPath != strHostPath)
6374 {
6375 /* a new machine folder is added or
6376 * the existing machine folder is changed */
6377 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
6378 ; /* the console folder exists, nothing to do */
6379 else
6380 {
6381 /* remove the old machine folder (when changed)
6382 * or the global folder if any (when new) */
6383 if ( it != oldFolders.end()
6384 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
6385 )
6386 {
6387 rc = removeSharedFolder(strName);
6388 if (FAILED(rc)) throw rc;
6389 }
6390
6391 /* create the new machine folder */
6392 rc = createSharedFolder(strName,
6393 SharedFolderData(strHostPath,
6394 writable,
6395 autoMount));
6396 if (FAILED(rc)) throw rc;
6397 }
6398 }
6399 /* forget the processed (or identical) folder */
6400 if (it != oldFolders.end())
6401 oldFolders.erase(it);
6402 }
6403 }
6404
6405 /* process outdated (removed) folders */
6406 if (online)
6407 {
6408 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
6409 it != oldFolders.end(); ++ it)
6410 {
6411 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
6412 ; /* the console folder exists, nothing to do */
6413 else
6414 {
6415 /* remove the outdated machine folder */
6416 rc = removeSharedFolder(it->first);
6417 if (FAILED(rc)) throw rc;
6418
6419 /* create the global folder if there is any */
6420 SharedFolderDataMap::const_iterator git =
6421 m_mapGlobalSharedFolders.find(it->first);
6422 if (git != m_mapGlobalSharedFolders.end())
6423 {
6424 rc = createSharedFolder(git->first, git->second);
6425 if (FAILED(rc)) throw rc;
6426 }
6427 }
6428 }
6429 }
6430 }
6431 }
6432 catch (HRESULT rc2)
6433 {
6434 if (online)
6435 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
6436 N_("Broken shared folder!"));
6437 }
6438
6439 LogFlowThisFunc(("Leaving\n"));
6440
6441 return rc;
6442}
6443
6444/**
6445 * Searches for a shared folder with the given name in the list of machine
6446 * shared folders and then in the list of the global shared folders.
6447 *
6448 * @param aName Name of the folder to search for.
6449 * @param aIt Where to store the pointer to the found folder.
6450 * @return @c true if the folder was found and @c false otherwise.
6451 *
6452 * @note The caller must lock this object for reading.
6453 */
6454bool Console::findOtherSharedFolder(const Utf8Str &strName,
6455 SharedFolderDataMap::const_iterator &aIt)
6456{
6457 /* sanity check */
6458 AssertReturn(isWriteLockOnCurrentThread(), false);
6459
6460 /* first, search machine folders */
6461 aIt = m_mapMachineSharedFolders.find(strName);
6462 if (aIt != m_mapMachineSharedFolders.end())
6463 return true;
6464
6465 /* second, search machine folders */
6466 aIt = m_mapGlobalSharedFolders.find(strName);
6467 if (aIt != m_mapGlobalSharedFolders.end())
6468 return true;
6469
6470 return false;
6471}
6472
6473/**
6474 * Calls the HGCM service to add a shared folder definition.
6475 *
6476 * @param aName Shared folder name.
6477 * @param aHostPath Shared folder path.
6478 *
6479 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6480 * @note Doesn't lock anything.
6481 */
6482HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
6483{
6484 ComAssertRet(strName.isNotEmpty(), E_FAIL);
6485 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
6486
6487 /* sanity checks */
6488 AssertReturn(mpUVM, E_FAIL);
6489 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
6490
6491 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
6492 SHFLSTRING *pFolderName, *pMapName;
6493 size_t cbString;
6494
6495 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
6496
6497 // check whether the path is valid and exists
6498 /* Check whether the path is full (absolute) */
6499 char hostPathFull[RTPATH_MAX];
6500 int vrc = RTPathAbsEx(NULL,
6501 aData.m_strHostPath.c_str(),
6502 hostPathFull,
6503 sizeof(hostPathFull));
6504 if (RT_FAILURE(vrc))
6505 return setError(E_INVALIDARG,
6506 tr("Invalid shared folder path: '%s' (%Rrc)"),
6507 aData.m_strHostPath.c_str(), vrc);
6508
6509 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
6510 return setError(E_INVALIDARG,
6511 tr("Shared folder path '%s' is not absolute"),
6512 aData.m_strHostPath.c_str());
6513 if (!RTPathExists(hostPathFull))
6514 return setError(E_INVALIDARG,
6515 tr("Shared folder path '%s' does not exist on the host"),
6516 aData.m_strHostPath.c_str());
6517
6518 // now that we know the path is good, give it to HGCM
6519
6520 Bstr bstrName(strName);
6521 Bstr bstrHostPath(aData.m_strHostPath);
6522
6523 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
6524 if (cbString >= UINT16_MAX)
6525 return setError(E_INVALIDARG, tr("The name is too long"));
6526 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6527 Assert(pFolderName);
6528 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
6529
6530 pFolderName->u16Size = (uint16_t)cbString;
6531 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6532
6533 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
6534 parms[0].u.pointer.addr = pFolderName;
6535 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6536
6537 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
6538 if (cbString >= UINT16_MAX)
6539 {
6540 RTMemFree(pFolderName);
6541 return setError(E_INVALIDARG, tr("The host path is too long"));
6542 }
6543 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6544 Assert(pMapName);
6545 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
6546
6547 pMapName->u16Size = (uint16_t)cbString;
6548 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6549
6550 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
6551 parms[1].u.pointer.addr = pMapName;
6552 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6553
6554 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
6555 parms[2].u.uint32 = aData.m_fWritable;
6556
6557 /*
6558 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
6559 * define below. This shows the host service that we have supplied
6560 * an additional parameter (auto-mount) and keeps the actual command
6561 * backwards compatible.
6562 */
6563 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
6564 parms[3].u.uint32 = aData.m_fAutoMount;
6565
6566 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
6567 SHFL_FN_ADD_MAPPING,
6568 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
6569 RTMemFree(pFolderName);
6570 RTMemFree(pMapName);
6571
6572 if (RT_FAILURE(vrc))
6573 return setError(E_FAIL,
6574 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
6575 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
6576
6577 return S_OK;
6578}
6579
6580/**
6581 * Calls the HGCM service to remove the shared folder definition.
6582 *
6583 * @param aName Shared folder name.
6584 *
6585 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6586 * @note Doesn't lock anything.
6587 */
6588HRESULT Console::removeSharedFolder(const Utf8Str &strName)
6589{
6590 ComAssertRet(strName.isNotEmpty(), E_FAIL);
6591
6592 /* sanity checks */
6593 AssertReturn(mpUVM, E_FAIL);
6594 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
6595
6596 VBOXHGCMSVCPARM parms;
6597 SHFLSTRING *pMapName;
6598 size_t cbString;
6599
6600 Log(("Removing shared folder '%s'\n", strName.c_str()));
6601
6602 Bstr bstrName(strName);
6603 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
6604 if (cbString >= UINT16_MAX)
6605 return setError(E_INVALIDARG, tr("The name is too long"));
6606 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6607 Assert(pMapName);
6608 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
6609
6610 pMapName->u16Size = (uint16_t)cbString;
6611 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6612
6613 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6614 parms.u.pointer.addr = pMapName;
6615 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6616
6617 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
6618 SHFL_FN_REMOVE_MAPPING,
6619 1, &parms);
6620 RTMemFree(pMapName);
6621 if (RT_FAILURE(vrc))
6622 return setError(E_FAIL,
6623 tr("Could not remove the shared folder '%s' (%Rrc)"),
6624 strName.c_str(), vrc);
6625
6626 return S_OK;
6627}
6628
6629/**
6630 * VM state callback function. Called by the VMM
6631 * using its state machine states.
6632 *
6633 * Primarily used to handle VM initiated power off, suspend and state saving,
6634 * but also for doing termination completed work (VMSTATE_TERMINATE).
6635 *
6636 * In general this function is called in the context of the EMT.
6637 *
6638 * @param aVM The VM handle.
6639 * @param aState The new state.
6640 * @param aOldState The old state.
6641 * @param aUser The user argument (pointer to the Console object).
6642 *
6643 * @note Locks the Console object for writing.
6644 */
6645DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6646 VMSTATE aState,
6647 VMSTATE aOldState,
6648 void *aUser)
6649{
6650 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6651 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6652
6653 Console *that = static_cast<Console *>(aUser);
6654 AssertReturnVoid(that);
6655
6656 AutoCaller autoCaller(that);
6657
6658 /* Note that we must let this method proceed even if Console::uninit() has
6659 * been already called. In such case this VMSTATE change is a result of:
6660 * 1) powerDown() called from uninit() itself, or
6661 * 2) VM-(guest-)initiated power off. */
6662 AssertReturnVoid( autoCaller.isOk()
6663 || autoCaller.state() == InUninit);
6664
6665 switch (aState)
6666 {
6667 /*
6668 * The VM has terminated
6669 */
6670 case VMSTATE_OFF:
6671 {
6672 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6673
6674 if (that->mVMStateChangeCallbackDisabled)
6675 break;
6676
6677 /* Do we still think that it is running? It may happen if this is a
6678 * VM-(guest-)initiated shutdown/poweroff.
6679 */
6680 if ( that->mMachineState != MachineState_Stopping
6681 && that->mMachineState != MachineState_Saving
6682 && that->mMachineState != MachineState_Restoring
6683 && that->mMachineState != MachineState_TeleportingIn
6684 && that->mMachineState != MachineState_FaultTolerantSyncing
6685 && that->mMachineState != MachineState_TeleportingPausedVM
6686 && !that->mVMIsAlreadyPoweringOff
6687 )
6688 {
6689 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6690
6691 /* prevent powerDown() from calling VMR3PowerOff() again */
6692 Assert(that->mVMPoweredOff == false);
6693 that->mVMPoweredOff = true;
6694
6695 /*
6696 * request a progress object from the server
6697 * (this will set the machine state to Stopping on the server
6698 * to block others from accessing this machine)
6699 */
6700 ComPtr<IProgress> pProgress;
6701 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
6702 AssertComRC(rc);
6703
6704 /* sync the state with the server */
6705 that->setMachineStateLocally(MachineState_Stopping);
6706
6707 /* Setup task object and thread to carry out the operation
6708 * asynchronously (if we call powerDown() right here but there
6709 * is one or more mpVM callers (added with addVMCaller()) we'll
6710 * deadlock).
6711 */
6712 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
6713 pProgress));
6714
6715 /* If creating a task failed, this can currently mean one of
6716 * two: either Console::uninit() has been called just a ms
6717 * before (so a powerDown() call is already on the way), or
6718 * powerDown() itself is being already executed. Just do
6719 * nothing.
6720 */
6721 if (!task->isOk())
6722 {
6723 LogFlowFunc(("Console is already being uninitialized.\n"));
6724 break;
6725 }
6726
6727 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6728 (void *) task.get(), 0,
6729 RTTHREADTYPE_MAIN_WORKER, 0,
6730 "VMPowerDown");
6731 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6732
6733 /* task is now owned by powerDownThread(), so release it */
6734 task.release();
6735 }
6736 break;
6737 }
6738
6739 /* The VM has been completely destroyed.
6740 *
6741 * Note: This state change can happen at two points:
6742 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6743 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6744 * called by EMT.
6745 */
6746 case VMSTATE_TERMINATED:
6747 {
6748 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6749
6750 if (that->mVMStateChangeCallbackDisabled)
6751 break;
6752
6753 /* Terminate host interface networking. If aVM is NULL, we've been
6754 * manually called from powerUpThread() either before calling
6755 * VMR3Create() or after VMR3Create() failed, so no need to touch
6756 * networking.
6757 */
6758 if (aVM)
6759 that->powerDownHostInterfaces();
6760
6761 /* From now on the machine is officially powered down or remains in
6762 * the Saved state.
6763 */
6764 switch (that->mMachineState)
6765 {
6766 default:
6767 AssertFailed();
6768 /* fall through */
6769 case MachineState_Stopping:
6770 /* successfully powered down */
6771 that->setMachineState(MachineState_PoweredOff);
6772 break;
6773 case MachineState_Saving:
6774 /* successfully saved */
6775 that->setMachineState(MachineState_Saved);
6776 break;
6777 case MachineState_Starting:
6778 /* failed to start, but be patient: set back to PoweredOff
6779 * (for similarity with the below) */
6780 that->setMachineState(MachineState_PoweredOff);
6781 break;
6782 case MachineState_Restoring:
6783 /* failed to load the saved state file, but be patient: set
6784 * back to Saved (to preserve the saved state file) */
6785 that->setMachineState(MachineState_Saved);
6786 break;
6787 case MachineState_TeleportingIn:
6788 /* Teleportation failed or was canceled. Back to powered off. */
6789 that->setMachineState(MachineState_PoweredOff);
6790 break;
6791 case MachineState_TeleportingPausedVM:
6792 /* Successfully teleported the VM. */
6793 that->setMachineState(MachineState_Teleported);
6794 break;
6795 case MachineState_FaultTolerantSyncing:
6796 /* Fault tolerant sync failed or was canceled. Back to powered off. */
6797 that->setMachineState(MachineState_PoweredOff);
6798 break;
6799 }
6800 break;
6801 }
6802
6803 case VMSTATE_RESETTING:
6804 {
6805 #ifdef VBOX_WITH_GUEST_PROPS
6806 /* Do not take any read/write locks here! */
6807 that->guestPropertiesHandleVMReset();
6808 #endif
6809 break;
6810 }
6811
6812 case VMSTATE_SUSPENDED:
6813 {
6814 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6815
6816 if (that->mVMStateChangeCallbackDisabled)
6817 break;
6818
6819 switch (that->mMachineState)
6820 {
6821 case MachineState_Teleporting:
6822 that->setMachineState(MachineState_TeleportingPausedVM);
6823 break;
6824
6825 case MachineState_LiveSnapshotting:
6826 that->setMachineState(MachineState_Saving);
6827 break;
6828
6829 case MachineState_TeleportingPausedVM:
6830 case MachineState_Saving:
6831 case MachineState_Restoring:
6832 case MachineState_Stopping:
6833 case MachineState_TeleportingIn:
6834 case MachineState_FaultTolerantSyncing:
6835 /* The worker thread handles the transition. */
6836 break;
6837
6838 default:
6839 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6840 case MachineState_Running:
6841 that->setMachineState(MachineState_Paused);
6842 break;
6843
6844 case MachineState_Paused:
6845 /* Nothing to do. */
6846 break;
6847 }
6848 break;
6849 }
6850
6851 case VMSTATE_SUSPENDED_LS:
6852 case VMSTATE_SUSPENDED_EXT_LS:
6853 {
6854 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6855 if (that->mVMStateChangeCallbackDisabled)
6856 break;
6857 switch (that->mMachineState)
6858 {
6859 case MachineState_Teleporting:
6860 that->setMachineState(MachineState_TeleportingPausedVM);
6861 break;
6862
6863 case MachineState_LiveSnapshotting:
6864 that->setMachineState(MachineState_Saving);
6865 break;
6866
6867 case MachineState_TeleportingPausedVM:
6868 case MachineState_Saving:
6869 /* ignore */
6870 break;
6871
6872 default:
6873 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6874 that->setMachineState(MachineState_Paused);
6875 break;
6876 }
6877 break;
6878 }
6879
6880 case VMSTATE_RUNNING:
6881 {
6882 if ( aOldState == VMSTATE_POWERING_ON
6883 || aOldState == VMSTATE_RESUMING
6884 || aOldState == VMSTATE_RUNNING_FT)
6885 {
6886 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6887
6888 if (that->mVMStateChangeCallbackDisabled)
6889 break;
6890
6891 Assert( ( ( that->mMachineState == MachineState_Starting
6892 || that->mMachineState == MachineState_Paused)
6893 && aOldState == VMSTATE_POWERING_ON)
6894 || ( ( that->mMachineState == MachineState_Restoring
6895 || that->mMachineState == MachineState_TeleportingIn
6896 || that->mMachineState == MachineState_Paused
6897 || that->mMachineState == MachineState_Saving
6898 )
6899 && aOldState == VMSTATE_RESUMING)
6900 || ( that->mMachineState == MachineState_FaultTolerantSyncing
6901 && aOldState == VMSTATE_RUNNING_FT));
6902
6903 that->setMachineState(MachineState_Running);
6904 }
6905
6906 break;
6907 }
6908
6909 case VMSTATE_RUNNING_LS:
6910 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6911 || that->mMachineState == MachineState_Teleporting,
6912 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6913 break;
6914
6915 case VMSTATE_RUNNING_FT:
6916 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
6917 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6918 break;
6919
6920 case VMSTATE_FATAL_ERROR:
6921 {
6922 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6923
6924 if (that->mVMStateChangeCallbackDisabled)
6925 break;
6926
6927 /* Fatal errors are only for running VMs. */
6928 Assert(Global::IsOnline(that->mMachineState));
6929
6930 /* Note! 'Pause' is used here in want of something better. There
6931 * are currently only two places where fatal errors might be
6932 * raised, so it is not worth adding a new externally
6933 * visible state for this yet. */
6934 that->setMachineState(MachineState_Paused);
6935 break;
6936 }
6937
6938 case VMSTATE_GURU_MEDITATION:
6939 {
6940 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6941
6942 if (that->mVMStateChangeCallbackDisabled)
6943 break;
6944
6945 /* Guru are only for running VMs */
6946 Assert(Global::IsOnline(that->mMachineState));
6947
6948 that->setMachineState(MachineState_Stuck);
6949 break;
6950 }
6951
6952 default: /* shut up gcc */
6953 break;
6954 }
6955}
6956
6957#ifdef VBOX_WITH_USB
6958/**
6959 * Sends a request to VMM to attach the given host device.
6960 * After this method succeeds, the attached device will appear in the
6961 * mUSBDevices collection.
6962 *
6963 * @param aHostDevice device to attach
6964 *
6965 * @note Synchronously calls EMT.
6966 * @note Must be called from under this object's lock.
6967 */
6968HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6969{
6970 AssertReturn(aHostDevice, E_FAIL);
6971 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6972
6973 /* still want a lock object because we need to leave it */
6974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6975
6976 HRESULT hrc;
6977
6978 /*
6979 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6980 * method in EMT (using usbAttachCallback()).
6981 */
6982 Bstr BstrAddress;
6983 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6984 ComAssertComRCRetRC(hrc);
6985
6986 Utf8Str Address(BstrAddress);
6987
6988 Bstr id;
6989 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6990 ComAssertComRCRetRC(hrc);
6991 Guid uuid(id);
6992
6993 BOOL fRemote = FALSE;
6994 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6995 ComAssertComRCRetRC(hrc);
6996
6997 /* Get the VM handle. */
6998 SafeVMPtr ptrVM(this);
6999 if (!ptrVM.isOk())
7000 return ptrVM.rc();
7001
7002 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7003 Address.c_str(), uuid.raw()));
7004
7005 /* leave the lock before a VMR3* call (EMT will call us back)! */
7006 alock.leave();
7007
7008/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7009 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7010 (PFNRT)usbAttachCallback, 7,
7011 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
7012
7013 /* restore the lock */
7014 alock.enter();
7015
7016 /* hrc is S_OK here */
7017
7018 if (RT_FAILURE(vrc))
7019 {
7020 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7021 Address.c_str(), uuid.raw(), vrc));
7022
7023 switch (vrc)
7024 {
7025 case VERR_VUSB_NO_PORTS:
7026 hrc = setError(E_FAIL,
7027 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7028 break;
7029 case VERR_VUSB_USBFS_PERMISSION:
7030 hrc = setError(E_FAIL,
7031 tr("Not permitted to open the USB device, check usbfs options"));
7032 break;
7033 default:
7034 hrc = setError(E_FAIL,
7035 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7036 vrc);
7037 break;
7038 }
7039 }
7040
7041 return hrc;
7042}
7043
7044/**
7045 * USB device attach callback used by AttachUSBDevice().
7046 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7047 * so we don't use AutoCaller and don't care about reference counters of
7048 * interface pointers passed in.
7049 *
7050 * @thread EMT
7051 * @note Locks the console object for writing.
7052 */
7053//static
7054DECLCALLBACK(int)
7055Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
7056{
7057 LogFlowFuncEnter();
7058 LogFlowFunc(("that={%p}\n", that));
7059
7060 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7061
7062 void *pvRemoteBackend = NULL;
7063 if (aRemote)
7064 {
7065 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7066 Guid guid(*aUuid);
7067
7068 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
7069 if (!pvRemoteBackend)
7070 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
7071 }
7072
7073 USHORT portVersion = 1;
7074 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7075 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
7076 Assert(portVersion == 1 || portVersion == 2);
7077
7078 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7079 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7080 if (RT_SUCCESS(vrc))
7081 {
7082 /* Create a OUSBDevice and add it to the device list */
7083 ComObjPtr<OUSBDevice> pUSBDevice;
7084 pUSBDevice.createObject();
7085 hrc = pUSBDevice->init(aHostDevice);
7086 AssertComRC(hrc);
7087
7088 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7089 that->mUSBDevices.push_back(pUSBDevice);
7090 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7091
7092 /* notify callbacks */
7093 that->onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7094 }
7095
7096 LogFlowFunc(("vrc=%Rrc\n", vrc));
7097 LogFlowFuncLeave();
7098 return vrc;
7099}
7100
7101/**
7102 * Sends a request to VMM to detach the given host device. After this method
7103 * succeeds, the detached device will disappear from the mUSBDevices
7104 * collection.
7105 *
7106 * @param aIt Iterator pointing to the device to detach.
7107 *
7108 * @note Synchronously calls EMT.
7109 * @note Must be called from under this object's lock.
7110 */
7111HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
7112{
7113 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7114
7115 /* still want a lock object because we need to leave it */
7116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7117
7118 /* Get the VM handle. */
7119 SafeVMPtr ptrVM(this);
7120 if (!ptrVM.isOk())
7121 return ptrVM.rc();
7122
7123 /* if the device is attached, then there must at least one USB hub. */
7124 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7125
7126 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7127 (*aIt)->id().raw()));
7128
7129 /* leave the lock before a VMR3* call (EMT will call us back)! */
7130 alock.leave();
7131
7132/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
7133 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7134 (PFNRT)usbDetachCallback, 5,
7135 this, ptrVM.raw(), &aIt, (*aIt)->id().raw());
7136 ComAssertRCRet(vrc, E_FAIL);
7137
7138 return S_OK;
7139}
7140
7141/**
7142 * USB device detach callback used by DetachUSBDevice().
7143 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7144 * so we don't use AutoCaller and don't care about reference counters of
7145 * interface pointers passed in.
7146 *
7147 * @thread EMT
7148 * @note Locks the console object for writing.
7149 */
7150//static
7151DECLCALLBACK(int)
7152Console::usbDetachCallback(Console *that, PVM pVM, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
7153{
7154 LogFlowFuncEnter();
7155 LogFlowFunc(("that={%p}\n", that));
7156
7157 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7158 ComObjPtr<OUSBDevice> pUSBDevice = **aIt;
7159
7160 /*
7161 * If that was a remote device, release the backend pointer.
7162 * The pointer was requested in usbAttachCallback.
7163 */
7164 BOOL fRemote = FALSE;
7165
7166 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
7167 if (FAILED(hrc2))
7168 setErrorStatic(hrc2, "GetRemote() failed");
7169
7170 if (fRemote)
7171 {
7172 Guid guid(*aUuid);
7173 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
7174 }
7175
7176 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
7177
7178 if (RT_SUCCESS(vrc))
7179 {
7180 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7181
7182 /* Remove the device from the collection */
7183 that->mUSBDevices.erase(*aIt);
7184 LogFlowFunc(("Detached device {%RTuuid}\n", pUSBDevice->id().raw()));
7185
7186 /* notify callbacks */
7187 that->onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, NULL);
7188 }
7189
7190 LogFlowFunc(("vrc=%Rrc\n", vrc));
7191 LogFlowFuncLeave();
7192 return vrc;
7193}
7194#endif /* VBOX_WITH_USB */
7195
7196/* Note: FreeBSD needs this whether netflt is used or not. */
7197#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
7198/**
7199 * Helper function to handle host interface device creation and attachment.
7200 *
7201 * @param networkAdapter the network adapter which attachment should be reset
7202 * @return COM status code
7203 *
7204 * @note The caller must lock this object for writing.
7205 *
7206 * @todo Move this back into the driver!
7207 */
7208HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
7209{
7210 LogFlowThisFunc(("\n"));
7211 /* sanity check */
7212 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7213
7214# ifdef VBOX_STRICT
7215 /* paranoia */
7216 NetworkAttachmentType_T attachment;
7217 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7218 Assert(attachment == NetworkAttachmentType_Bridged);
7219# endif /* VBOX_STRICT */
7220
7221 HRESULT rc = S_OK;
7222
7223 ULONG slot = 0;
7224 rc = networkAdapter->COMGETTER(Slot)(&slot);
7225 AssertComRC(rc);
7226
7227# ifdef RT_OS_LINUX
7228 /*
7229 * Allocate a host interface device
7230 */
7231 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
7232 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
7233 if (RT_SUCCESS(rcVBox))
7234 {
7235 /*
7236 * Set/obtain the tap interface.
7237 */
7238 struct ifreq IfReq;
7239 memset(&IfReq, 0, sizeof(IfReq));
7240 /* The name of the TAP interface we are using */
7241 Bstr tapDeviceName;
7242 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7243 if (FAILED(rc))
7244 tapDeviceName.setNull(); /* Is this necessary? */
7245 if (tapDeviceName.isEmpty())
7246 {
7247 LogRel(("No TAP device name was supplied.\n"));
7248 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7249 }
7250
7251 if (SUCCEEDED(rc))
7252 {
7253 /* If we are using a static TAP device then try to open it. */
7254 Utf8Str str(tapDeviceName);
7255 if (str.length() <= sizeof(IfReq.ifr_name))
7256 strcpy(IfReq.ifr_name, str.c_str());
7257 else
7258 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
7259 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
7260 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
7261 if (rcVBox != 0)
7262 {
7263 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
7264 rc = setError(E_FAIL,
7265 tr("Failed to open the host network interface %ls"),
7266 tapDeviceName.raw());
7267 }
7268 }
7269 if (SUCCEEDED(rc))
7270 {
7271 /*
7272 * Make it pollable.
7273 */
7274 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
7275 {
7276 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
7277 /*
7278 * Here is the right place to communicate the TAP file descriptor and
7279 * the host interface name to the server if/when it becomes really
7280 * necessary.
7281 */
7282 maTAPDeviceName[slot] = tapDeviceName;
7283 rcVBox = VINF_SUCCESS;
7284 }
7285 else
7286 {
7287 int iErr = errno;
7288
7289 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
7290 rcVBox = VERR_HOSTIF_BLOCKING;
7291 rc = setError(E_FAIL,
7292 tr("could not set up the host networking device for non blocking access: %s"),
7293 strerror(errno));
7294 }
7295 }
7296 }
7297 else
7298 {
7299 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
7300 switch (rcVBox)
7301 {
7302 case VERR_ACCESS_DENIED:
7303 /* will be handled by our caller */
7304 rc = rcVBox;
7305 break;
7306 default:
7307 rc = setError(E_FAIL,
7308 tr("Could not set up the host networking device: %Rrc"),
7309 rcVBox);
7310 break;
7311 }
7312 }
7313
7314# elif defined(RT_OS_FREEBSD)
7315 /*
7316 * Set/obtain the tap interface.
7317 */
7318 /* The name of the TAP interface we are using */
7319 Bstr tapDeviceName;
7320 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7321 if (FAILED(rc))
7322 tapDeviceName.setNull(); /* Is this necessary? */
7323 if (tapDeviceName.isEmpty())
7324 {
7325 LogRel(("No TAP device name was supplied.\n"));
7326 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
7327 }
7328 char szTapdev[1024] = "/dev/";
7329 /* If we are using a static TAP device then try to open it. */
7330 Utf8Str str(tapDeviceName);
7331 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
7332 strcat(szTapdev, str.c_str());
7333 else
7334 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
7335 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
7336 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
7337 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
7338
7339 if (RT_SUCCESS(rcVBox))
7340 maTAPDeviceName[slot] = tapDeviceName;
7341 else
7342 {
7343 switch (rcVBox)
7344 {
7345 case VERR_ACCESS_DENIED:
7346 /* will be handled by our caller */
7347 rc = rcVBox;
7348 break;
7349 default:
7350 rc = setError(E_FAIL,
7351 tr("Failed to open the host network interface %ls"),
7352 tapDeviceName.raw());
7353 break;
7354 }
7355 }
7356# else
7357# error "huh?"
7358# endif
7359 /* in case of failure, cleanup. */
7360 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
7361 {
7362 LogRel(("General failure attaching to host interface\n"));
7363 rc = setError(E_FAIL,
7364 tr("General failure attaching to host interface"));
7365 }
7366 LogFlowThisFunc(("rc=%d\n", rc));
7367 return rc;
7368}
7369
7370
7371/**
7372 * Helper function to handle detachment from a host interface
7373 *
7374 * @param networkAdapter the network adapter which attachment should be reset
7375 * @return COM status code
7376 *
7377 * @note The caller must lock this object for writing.
7378 *
7379 * @todo Move this back into the driver!
7380 */
7381HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
7382{
7383 /* sanity check */
7384 LogFlowThisFunc(("\n"));
7385 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7386
7387 HRESULT rc = S_OK;
7388# ifdef VBOX_STRICT
7389 /* paranoia */
7390 NetworkAttachmentType_T attachment;
7391 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7392 Assert(attachment == NetworkAttachmentType_Bridged);
7393# endif /* VBOX_STRICT */
7394
7395 ULONG slot = 0;
7396 rc = networkAdapter->COMGETTER(Slot)(&slot);
7397 AssertComRC(rc);
7398
7399 /* is there an open TAP device? */
7400 if (maTapFD[slot] != NIL_RTFILE)
7401 {
7402 /*
7403 * Close the file handle.
7404 */
7405 Bstr tapDeviceName, tapTerminateApplication;
7406 bool isStatic = true;
7407 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
7408 if (FAILED(rc) || tapDeviceName.isEmpty())
7409 {
7410 /* If the name is empty, this is a dynamic TAP device, so close it now,
7411 so that the termination script can remove the interface. Otherwise we still
7412 need the FD to pass to the termination script. */
7413 isStatic = false;
7414 int rcVBox = RTFileClose(maTapFD[slot]);
7415 AssertRC(rcVBox);
7416 maTapFD[slot] = NIL_RTFILE;
7417 }
7418 if (isStatic)
7419 {
7420 /* If we are using a static TAP device, we close it now, after having called the
7421 termination script. */
7422 int rcVBox = RTFileClose(maTapFD[slot]);
7423 AssertRC(rcVBox);
7424 }
7425 /* the TAP device name and handle are no longer valid */
7426 maTapFD[slot] = NIL_RTFILE;
7427 maTAPDeviceName[slot] = "";
7428 }
7429 LogFlowThisFunc(("returning %d\n", rc));
7430 return rc;
7431}
7432#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
7433
7434/**
7435 * Called at power down to terminate host interface networking.
7436 *
7437 * @note The caller must lock this object for writing.
7438 */
7439HRESULT Console::powerDownHostInterfaces()
7440{
7441 LogFlowThisFunc(("\n"));
7442
7443 /* sanity check */
7444 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7445
7446 /*
7447 * host interface termination handling
7448 */
7449 HRESULT rc;
7450 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
7451 {
7452 ComPtr<INetworkAdapter> pNetworkAdapter;
7453 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
7454 if (FAILED(rc)) break;
7455
7456 BOOL enabled = FALSE;
7457 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
7458 if (!enabled)
7459 continue;
7460
7461 NetworkAttachmentType_T attachment;
7462 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
7463 if (attachment == NetworkAttachmentType_Bridged)
7464 {
7465#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
7466 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
7467 if (FAILED(rc2) && SUCCEEDED(rc))
7468 rc = rc2;
7469#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
7470 }
7471 }
7472
7473 return rc;
7474}
7475
7476
7477/**
7478 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
7479 * and VMR3Teleport.
7480 *
7481 * @param pVM The VM handle.
7482 * @param uPercent Completion percentage (0-100).
7483 * @param pvUser Pointer to an IProgress instance.
7484 * @return VINF_SUCCESS.
7485 */
7486/*static*/
7487DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
7488{
7489 IProgress *pProgress = static_cast<IProgress *>(pvUser);
7490
7491 /* update the progress object */
7492 if (pProgress)
7493 pProgress->SetCurrentOperationProgress(uPercent);
7494
7495 return VINF_SUCCESS;
7496}
7497
7498/**
7499 * @copydoc FNVMATERROR
7500 *
7501 * @remarks Might be some tiny serialization concerns with access to the string
7502 * object here...
7503 */
7504/*static*/ DECLCALLBACK(void)
7505Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
7506 const char *pszErrorFmt, va_list va)
7507{
7508 Utf8Str *pErrorText = (Utf8Str *)pvUser;
7509 AssertPtr(pErrorText);
7510
7511 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
7512 va_list va2;
7513 va_copy(va2, va);
7514
7515 /* Append to any the existing error message. */
7516 if (pErrorText->length())
7517 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
7518 pszErrorFmt, &va2, rc, rc);
7519 else
7520 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
7521
7522 va_end(va2);
7523}
7524
7525/**
7526 * VM runtime error callback function.
7527 * See VMSetRuntimeError for the detailed description of parameters.
7528 *
7529 * @param pVM The VM handle.
7530 * @param pvUser The user argument.
7531 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
7532 * @param pszErrorId Error ID string.
7533 * @param pszFormat Error message format string.
7534 * @param va Error message arguments.
7535 * @thread EMT.
7536 */
7537/* static */ DECLCALLBACK(void)
7538Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
7539 const char *pszErrorId,
7540 const char *pszFormat, va_list va)
7541{
7542 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
7543 LogFlowFuncEnter();
7544
7545 Console *that = static_cast<Console *>(pvUser);
7546 AssertReturnVoid(that);
7547
7548 Utf8Str message(pszFormat, va);
7549
7550 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
7551 fFatal, pszErrorId, message.c_str()));
7552
7553 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
7554 Bstr(message).raw());
7555
7556 LogFlowFuncLeave();
7557}
7558
7559/**
7560 * Captures USB devices that match filters of the VM.
7561 * Called at VM startup.
7562 *
7563 * @param pVM The VM handle.
7564 *
7565 * @note The caller must lock this object for writing.
7566 */
7567HRESULT Console::captureUSBDevices(PVM pVM)
7568{
7569 LogFlowThisFunc(("\n"));
7570
7571 /* sanity check */
7572 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
7573
7574 /* If the machine has an USB controller, ask the USB proxy service to
7575 * capture devices */
7576 PPDMIBASE pBase;
7577 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7578 if (RT_SUCCESS(vrc))
7579 {
7580 /* leave the lock before calling Host in VBoxSVC since Host may call
7581 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7582 * produce an inter-process dead-lock otherwise. */
7583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7584 alock.leave();
7585
7586 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7587 ComAssertComRCRetRC(hrc);
7588 }
7589 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7590 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7591 vrc = VINF_SUCCESS;
7592 else
7593 AssertRC(vrc);
7594
7595 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7596}
7597
7598
7599/**
7600 * Detach all USB device which are attached to the VM for the
7601 * purpose of clean up and such like.
7602 *
7603 * @note The caller must lock this object for writing.
7604 */
7605void Console::detachAllUSBDevices(bool aDone)
7606{
7607 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7608
7609 /* sanity check */
7610 AssertReturnVoid(isWriteLockOnCurrentThread());
7611
7612 mUSBDevices.clear();
7613
7614 /* leave the lock before calling Host in VBoxSVC since Host may call
7615 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7616 * produce an inter-process dead-lock otherwise. */
7617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7618 alock.leave();
7619
7620 mControl->DetachAllUSBDevices(aDone);
7621}
7622
7623/**
7624 * @note Locks this object for writing.
7625 */
7626void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7627{
7628 LogFlowThisFuncEnter();
7629 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7630
7631 AutoCaller autoCaller(this);
7632 if (!autoCaller.isOk())
7633 {
7634 /* Console has been already uninitialized, deny request */
7635 AssertMsgFailed(("Console is already uninitialized\n"));
7636 LogFlowThisFunc(("Console is already uninitialized\n"));
7637 LogFlowThisFuncLeave();
7638 return;
7639 }
7640
7641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7642
7643 /*
7644 * Mark all existing remote USB devices as dirty.
7645 */
7646 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7647 it != mRemoteUSBDevices.end();
7648 ++it)
7649 {
7650 (*it)->dirty(true);
7651 }
7652
7653 /*
7654 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7655 */
7656 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7657 VRDEUSBDEVICEDESC *e = pDevList;
7658
7659 /* The cbDevList condition must be checked first, because the function can
7660 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7661 */
7662 while (cbDevList >= 2 && e->oNext)
7663 {
7664 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7665 e->idVendor, e->idProduct,
7666 e->oProduct? (char *)e + e->oProduct: ""));
7667
7668 bool fNewDevice = true;
7669
7670 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7671 it != mRemoteUSBDevices.end();
7672 ++it)
7673 {
7674 if ((*it)->devId() == e->id
7675 && (*it)->clientId() == u32ClientId)
7676 {
7677 /* The device is already in the list. */
7678 (*it)->dirty(false);
7679 fNewDevice = false;
7680 break;
7681 }
7682 }
7683
7684 if (fNewDevice)
7685 {
7686 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7687 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7688
7689 /* Create the device object and add the new device to list. */
7690 ComObjPtr<RemoteUSBDevice> pUSBDevice;
7691 pUSBDevice.createObject();
7692 pUSBDevice->init(u32ClientId, e);
7693
7694 mRemoteUSBDevices.push_back(pUSBDevice);
7695
7696 /* Check if the device is ok for current USB filters. */
7697 BOOL fMatched = FALSE;
7698 ULONG fMaskedIfs = 0;
7699
7700 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
7701
7702 AssertComRC(hrc);
7703
7704 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7705
7706 if (fMatched)
7707 {
7708 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
7709
7710 /// @todo (r=dmik) warning reporting subsystem
7711
7712 if (hrc == S_OK)
7713 {
7714 LogFlowThisFunc(("Device attached\n"));
7715 pUSBDevice->captured(true);
7716 }
7717 }
7718 }
7719
7720 if (cbDevList < e->oNext)
7721 {
7722 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7723 cbDevList, e->oNext));
7724 break;
7725 }
7726
7727 cbDevList -= e->oNext;
7728
7729 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7730 }
7731
7732 /*
7733 * Remove dirty devices, that is those which are not reported by the server anymore.
7734 */
7735 for (;;)
7736 {
7737 ComObjPtr<RemoteUSBDevice> pUSBDevice;
7738
7739 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7740 while (it != mRemoteUSBDevices.end())
7741 {
7742 if ((*it)->dirty())
7743 {
7744 pUSBDevice = *it;
7745 break;
7746 }
7747
7748 ++ it;
7749 }
7750
7751 if (!pUSBDevice)
7752 {
7753 break;
7754 }
7755
7756 USHORT vendorId = 0;
7757 pUSBDevice->COMGETTER(VendorId)(&vendorId);
7758
7759 USHORT productId = 0;
7760 pUSBDevice->COMGETTER(ProductId)(&productId);
7761
7762 Bstr product;
7763 pUSBDevice->COMGETTER(Product)(product.asOutParam());
7764
7765 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7766 vendorId, productId, product.raw()));
7767
7768 /* Detach the device from VM. */
7769 if (pUSBDevice->captured())
7770 {
7771 Bstr uuid;
7772 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
7773 onUSBDeviceDetach(uuid.raw(), NULL);
7774 }
7775
7776 /* And remove it from the list. */
7777 mRemoteUSBDevices.erase(it);
7778 }
7779
7780 LogFlowThisFuncLeave();
7781}
7782
7783/**
7784 * Progress cancelation callback for fault tolerance VM poweron
7785 */
7786static void faultToleranceProgressCancelCallback(void *pvUser)
7787{
7788 PVM pVM = (PVM)pvUser;
7789
7790 if (pVM)
7791 FTMR3CancelStandby(pVM);
7792}
7793
7794/**
7795 * Thread function which starts the VM (also from saved state) and
7796 * track progress.
7797 *
7798 * @param Thread The thread id.
7799 * @param pvUser Pointer to a VMPowerUpTask structure.
7800 * @return VINF_SUCCESS (ignored).
7801 *
7802 * @note Locks the Console object for writing.
7803 */
7804/*static*/
7805DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7806{
7807 LogFlowFuncEnter();
7808
7809 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7810 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7811
7812 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7813 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7814
7815#if defined(RT_OS_WINDOWS)
7816 {
7817 /* initialize COM */
7818 HRESULT hrc = CoInitializeEx(NULL,
7819 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7820 COINIT_SPEED_OVER_MEMORY);
7821 LogFlowFunc(("CoInitializeEx()=%Rhrc\n", hrc));
7822 }
7823#endif
7824
7825 HRESULT rc = S_OK;
7826 int vrc = VINF_SUCCESS;
7827
7828 /* Set up a build identifier so that it can be seen from core dumps what
7829 * exact build was used to produce the core. */
7830 static char saBuildID[40];
7831 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7832 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7833
7834 ComObjPtr<Console> pConsole = task->mConsole;
7835
7836 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7837
7838 /* The lock is also used as a signal from the task initiator (which
7839 * releases it only after RTThreadCreate()) that we can start the job */
7840 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
7841
7842 /* sanity */
7843 Assert(pConsole->mpUVM == NULL);
7844
7845 try
7846 {
7847 // Create the VMM device object, which starts the HGCM thread; do this only
7848 // once for the console, for the pathological case that the same console
7849 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
7850 // here instead of the Console constructor (see Console::init())
7851 if (!pConsole->m_pVMMDev)
7852 {
7853 pConsole->m_pVMMDev = new VMMDev(pConsole);
7854 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
7855 }
7856
7857 /* wait for auto reset ops to complete so that we can successfully lock
7858 * the attached hard disks by calling LockMedia() below */
7859 for (VMPowerUpTask::ProgressList::const_iterator
7860 it = task->hardDiskProgresses.begin();
7861 it != task->hardDiskProgresses.end(); ++ it)
7862 {
7863 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7864 AssertComRC(rc2);
7865 }
7866
7867 /*
7868 * Lock attached media. This method will also check their accessibility.
7869 * If we're a teleporter, we'll have to postpone this action so we can
7870 * migrate between local processes.
7871 *
7872 * Note! The media will be unlocked automatically by
7873 * SessionMachine::setMachineState() when the VM is powered down.
7874 */
7875 if ( !task->mTeleporterEnabled
7876 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
7877 {
7878 rc = pConsole->mControl->LockMedia();
7879 if (FAILED(rc)) throw rc;
7880 }
7881
7882 /* Create the VRDP server. In case of headless operation, this will
7883 * also create the framebuffer, required at VM creation.
7884 */
7885 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
7886 Assert(server);
7887
7888 /* Does VRDP server call Console from the other thread?
7889 * Not sure (and can change), so leave the lock just in case.
7890 */
7891 alock.leave();
7892 vrc = server->Launch();
7893 alock.enter();
7894
7895 if (vrc == VERR_NET_ADDRESS_IN_USE)
7896 {
7897 Utf8Str errMsg;
7898 Bstr bstr;
7899 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
7900 Utf8Str ports = bstr;
7901 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
7902 ports.c_str());
7903 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
7904 vrc, errMsg.c_str()));
7905 }
7906 else if (vrc == VINF_NOT_SUPPORTED)
7907 {
7908 /* This means that the VRDE is not installed. */
7909 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
7910 }
7911 else if (RT_FAILURE(vrc))
7912 {
7913 /* Fail, if the server is installed but can't start. */
7914 Utf8Str errMsg;
7915 switch (vrc)
7916 {
7917 case VERR_FILE_NOT_FOUND:
7918 {
7919 /* VRDE library file is missing. */
7920 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
7921 break;
7922 }
7923 default:
7924 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
7925 vrc);
7926 }
7927 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
7928 vrc, errMsg.c_str()));
7929 throw setErrorStatic(E_FAIL, errMsg.c_str());
7930 }
7931
7932 ComPtr<IMachine> pMachine = pConsole->machine();
7933 ULONG cCpus = 1;
7934 pMachine->COMGETTER(CPUCount)(&cCpus);
7935
7936 /*
7937 * Create the VM
7938 */
7939 PVM pVM;
7940 /*
7941 * leave the lock since EMT will call Console. It's safe because
7942 * mMachineState is either Starting or Restoring state here.
7943 */
7944 alock.leave();
7945
7946 vrc = VMR3Create(cCpus,
7947 pConsole->mpVmm2UserMethods,
7948 Console::genericVMSetErrorCallback,
7949 &task->mErrorMsg,
7950 task->mConfigConstructor,
7951 static_cast<Console *>(pConsole),
7952 &pVM);
7953
7954 alock.enter();
7955
7956 /* Enable client connections to the server. */
7957 pConsole->consoleVRDPServer()->EnableConnections();
7958
7959 if (RT_SUCCESS(vrc))
7960 {
7961 do
7962 {
7963 /*
7964 * Register our load/save state file handlers
7965 */
7966 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7967 NULL, NULL, NULL,
7968 NULL, saveStateFileExec, NULL,
7969 NULL, loadStateFileExec, NULL,
7970 static_cast<Console *>(pConsole));
7971 AssertRCBreak(vrc);
7972
7973 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
7974 AssertRC(vrc);
7975 if (RT_FAILURE(vrc))
7976 break;
7977
7978 /*
7979 * Synchronize debugger settings
7980 */
7981 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
7982 if (machineDebugger)
7983 machineDebugger->flushQueuedSettings();
7984
7985 /*
7986 * Shared Folders
7987 */
7988 if (pConsole->m_pVMMDev->isShFlActive())
7989 {
7990 /* Does the code below call Console from the other thread?
7991 * Not sure, so leave the lock just in case. */
7992 alock.leave();
7993
7994 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
7995 it != task->mSharedFolders.end();
7996 ++it)
7997 {
7998 const SharedFolderData &d = it->second;
7999 rc = pConsole->createSharedFolder(it->first, d);
8000 if (FAILED(rc))
8001 {
8002 ErrorInfoKeeper eik;
8003 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8004 N_("The shared folder '%s' could not be set up: %ls.\n"
8005 "The shared folder setup will not be complete. It is recommended to power down the virtual machine and "
8006 "fix the shared folder settings while the machine is not running."),
8007 it->first.c_str(), eik.getText().raw());
8008 break;
8009 }
8010 }
8011 if (FAILED(rc))
8012 rc = S_OK; // do not fail with broken shared folders
8013
8014 /* enter the lock again */
8015 alock.enter();
8016 }
8017
8018 /*
8019 * Capture USB devices.
8020 */
8021 rc = pConsole->captureUSBDevices(pVM);
8022 if (FAILED(rc)) break;
8023
8024 /* leave the lock before a lengthy operation */
8025 alock.leave();
8026
8027 /* Load saved state? */
8028 if (task->mSavedStateFile.length())
8029 {
8030 LogFlowFunc(("Restoring saved state from '%s'...\n",
8031 task->mSavedStateFile.c_str()));
8032
8033 vrc = VMR3LoadFromFile(pVM,
8034 task->mSavedStateFile.c_str(),
8035 Console::stateProgressCallback,
8036 static_cast<IProgress *>(task->mProgress));
8037
8038 if (RT_SUCCESS(vrc))
8039 {
8040 if (task->mStartPaused)
8041 /* done */
8042 pConsole->setMachineState(MachineState_Paused);
8043 else
8044 {
8045 /* Start/Resume the VM execution */
8046#ifdef VBOX_WITH_EXTPACK
8047 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8048#endif
8049 if (RT_SUCCESS(vrc))
8050 vrc = VMR3Resume(pVM);
8051 AssertLogRelRC(vrc);
8052 }
8053 }
8054
8055 /* Power off in case we failed loading or resuming the VM */
8056 if (RT_FAILURE(vrc))
8057 {
8058 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8059#ifdef VBOX_WITH_EXTPACK
8060 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8061#endif
8062 }
8063 }
8064 else if (task->mTeleporterEnabled)
8065 {
8066 /* -> ConsoleImplTeleporter.cpp */
8067 bool fPowerOffOnFailure;
8068 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8069 task->mProgress, &fPowerOffOnFailure);
8070 if (FAILED(rc) && fPowerOffOnFailure)
8071 {
8072 ErrorInfoKeeper eik;
8073 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8074#ifdef VBOX_WITH_EXTPACK
8075 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8076#endif
8077 }
8078 }
8079 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8080 {
8081 /*
8082 * Get the config.
8083 */
8084 ULONG uPort;
8085 ULONG uInterval;
8086 Bstr bstrAddress, bstrPassword;
8087
8088 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8089 if (SUCCEEDED(rc))
8090 {
8091 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8092 if (SUCCEEDED(rc))
8093 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8094 if (SUCCEEDED(rc))
8095 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8096 }
8097 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8098 {
8099 if (SUCCEEDED(rc))
8100 {
8101 Utf8Str strAddress(bstrAddress);
8102 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8103 Utf8Str strPassword(bstrPassword);
8104 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8105
8106 /* Power on the FT enabled VM. */
8107#ifdef VBOX_WITH_EXTPACK
8108 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8109#endif
8110 if (RT_SUCCESS(vrc))
8111 vrc = FTMR3PowerOn(pVM,
8112 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8113 uInterval,
8114 pszAddress,
8115 uPort,
8116 pszPassword);
8117 AssertLogRelRC(vrc);
8118 }
8119 task->mProgress->setCancelCallback(NULL, NULL);
8120 }
8121 else
8122 rc = E_FAIL;
8123 }
8124 else if (task->mStartPaused)
8125 /* done */
8126 pConsole->setMachineState(MachineState_Paused);
8127 else
8128 {
8129 /* Power on the VM (i.e. start executing) */
8130#ifdef VBOX_WITH_EXTPACK
8131 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8132#endif
8133 if (RT_SUCCESS(vrc))
8134 vrc = VMR3PowerOn(pVM);
8135 AssertLogRelRC(vrc);
8136 }
8137
8138 /* enter the lock again */
8139 alock.enter();
8140 }
8141 while (0);
8142
8143 /* On failure, destroy the VM */
8144 if (FAILED(rc) || RT_FAILURE(vrc))
8145 {
8146 /* preserve existing error info */
8147 ErrorInfoKeeper eik;
8148
8149 /* powerDown() will call VMR3Destroy() and do all necessary
8150 * cleanup (VRDP, USB devices) */
8151 HRESULT rc2 = pConsole->powerDown();
8152 AssertComRC(rc2);
8153 }
8154 else
8155 {
8156 /*
8157 * Deregister the VMSetError callback. This is necessary as the
8158 * pfnVMAtError() function passed to VMR3Create() is supposed to
8159 * be sticky but our error callback isn't.
8160 */
8161 alock.leave();
8162 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8163 /** @todo register another VMSetError callback? */
8164 alock.enter();
8165 }
8166 }
8167 else
8168 {
8169 /*
8170 * If VMR3Create() failed it has released the VM memory.
8171 */
8172 VMR3ReleaseUVM(pConsole->mpUVM);
8173 pConsole->mpUVM = NULL;
8174 }
8175
8176 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8177 {
8178 /* If VMR3Create() or one of the other calls in this function fail,
8179 * an appropriate error message has been set in task->mErrorMsg.
8180 * However since that happens via a callback, the rc status code in
8181 * this function is not updated.
8182 */
8183 if (!task->mErrorMsg.length())
8184 {
8185 /* If the error message is not set but we've got a failure,
8186 * convert the VBox status code into a meaningful error message.
8187 * This becomes unused once all the sources of errors set the
8188 * appropriate error message themselves.
8189 */
8190 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8191 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8192 vrc);
8193 }
8194
8195 /* Set the error message as the COM error.
8196 * Progress::notifyComplete() will pick it up later. */
8197 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8198 }
8199 }
8200 catch (HRESULT aRC) { rc = aRC; }
8201
8202 if ( pConsole->mMachineState == MachineState_Starting
8203 || pConsole->mMachineState == MachineState_Restoring
8204 || pConsole->mMachineState == MachineState_TeleportingIn
8205 )
8206 {
8207 /* We are still in the Starting/Restoring state. This means one of:
8208 *
8209 * 1) we failed before VMR3Create() was called;
8210 * 2) VMR3Create() failed.
8211 *
8212 * In both cases, there is no need to call powerDown(), but we still
8213 * need to go back to the PoweredOff/Saved state. Reuse
8214 * vmstateChangeCallback() for that purpose.
8215 */
8216
8217 /* preserve existing error info */
8218 ErrorInfoKeeper eik;
8219
8220 Assert(pConsole->mpUVM == NULL);
8221 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8222 pConsole);
8223 }
8224
8225 /*
8226 * Evaluate the final result. Note that the appropriate mMachineState value
8227 * is already set by vmstateChangeCallback() in all cases.
8228 */
8229
8230 /* leave the lock, don't need it any more */
8231 alock.leave();
8232
8233 if (SUCCEEDED(rc))
8234 {
8235 /* Notify the progress object of the success */
8236 task->mProgress->notifyComplete(S_OK);
8237 }
8238 else
8239 {
8240 /* The progress object will fetch the current error info */
8241 task->mProgress->notifyComplete(rc);
8242 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8243 }
8244
8245 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8246 pConsole->mControl->EndPowerUp(rc);
8247
8248#if defined(RT_OS_WINDOWS)
8249 /* uninitialize COM */
8250 CoUninitialize();
8251#endif
8252
8253 LogFlowFuncLeave();
8254
8255 return VINF_SUCCESS;
8256}
8257
8258
8259/**
8260 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8261 *
8262 * @param pConsole Reference to the console object.
8263 * @param pVM The VM handle.
8264 * @param lInstance The instance of the controller.
8265 * @param pcszDevice The name of the controller type.
8266 * @param enmBus The storage bus type of the controller.
8267 * @param fSetupMerge Whether to set up a medium merge
8268 * @param uMergeSource Merge source image index
8269 * @param uMergeTarget Merge target image index
8270 * @param aMediumAtt The medium attachment.
8271 * @param aMachineState The current machine state.
8272 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8273 * @return VBox status code.
8274 */
8275/* static */
8276DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8277 PVM pVM,
8278 const char *pcszDevice,
8279 unsigned uInstance,
8280 StorageBus_T enmBus,
8281 bool fUseHostIOCache,
8282 bool fBuiltinIoCache,
8283 bool fSetupMerge,
8284 unsigned uMergeSource,
8285 unsigned uMergeTarget,
8286 IMediumAttachment *aMediumAtt,
8287 MachineState_T aMachineState,
8288 HRESULT *phrc)
8289{
8290 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8291
8292 int rc;
8293 HRESULT hrc;
8294 Bstr bstr;
8295 *phrc = S_OK;
8296#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8297#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8298
8299 /* Ignore attachments other than hard disks, since at the moment they are
8300 * not subject to snapshotting in general. */
8301 DeviceType_T lType;
8302 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
8303 if (lType != DeviceType_HardDisk)
8304 return VINF_SUCCESS;
8305
8306 /* Determine the base path for the device instance. */
8307 PCFGMNODE pCtlInst;
8308 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
8309 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8310
8311 /* Update the device instance configuration. */
8312 rc = pConsole->configMediumAttachment(pCtlInst,
8313 pcszDevice,
8314 uInstance,
8315 enmBus,
8316 fUseHostIOCache,
8317 fBuiltinIoCache,
8318 fSetupMerge,
8319 uMergeSource,
8320 uMergeTarget,
8321 aMediumAtt,
8322 aMachineState,
8323 phrc,
8324 true /* fAttachDetach */,
8325 false /* fForceUnmount */,
8326 pVM,
8327 NULL /* paLedDevType */);
8328 /** @todo this dumps everything attached to this device instance, which
8329 * is more than necessary. Dumping the changed LUN would be enough. */
8330 CFGMR3Dump(pCtlInst);
8331 RC_CHECK();
8332
8333#undef RC_CHECK
8334#undef H
8335
8336 LogFlowFunc(("Returns success\n"));
8337 return VINF_SUCCESS;
8338}
8339
8340/**
8341 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8342 */
8343static void takesnapshotProgressCancelCallback(void *pvUser)
8344{
8345 PUVM pUVM = (PUVM)pvUser;
8346 SSMR3Cancel(VMR3GetVM(pUVM));
8347}
8348
8349/**
8350 * Worker thread created by Console::TakeSnapshot.
8351 * @param Thread The current thread (ignored).
8352 * @param pvUser The task.
8353 * @return VINF_SUCCESS (ignored).
8354 */
8355/*static*/
8356DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
8357{
8358 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
8359
8360 // taking a snapshot consists of the following:
8361
8362 // 1) creating a diff image for each virtual hard disk, into which write operations go after
8363 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
8364 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
8365 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
8366 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
8367
8368 Console *that = pTask->mConsole;
8369 bool fBeganTakingSnapshot = false;
8370 bool fSuspenededBySave = false;
8371
8372 AutoCaller autoCaller(that);
8373 if (FAILED(autoCaller.rc()))
8374 {
8375 that->mptrCancelableProgress.setNull();
8376 return autoCaller.rc();
8377 }
8378
8379 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
8380
8381 HRESULT rc = S_OK;
8382
8383 try
8384 {
8385 /* STEP 1 + 2:
8386 * request creating the diff images on the server and create the snapshot object
8387 * (this will set the machine state to Saving on the server to block
8388 * others from accessing this machine)
8389 */
8390 rc = that->mControl->BeginTakingSnapshot(that,
8391 pTask->bstrName.raw(),
8392 pTask->bstrDescription.raw(),
8393 pTask->mProgress,
8394 pTask->fTakingSnapshotOnline,
8395 pTask->bstrSavedStateFile.asOutParam());
8396 if (FAILED(rc))
8397 throw rc;
8398
8399 fBeganTakingSnapshot = true;
8400
8401 /*
8402 * state file is non-null only when the VM is paused
8403 * (i.e. creating a snapshot online)
8404 */
8405 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
8406 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
8407 if (!f)
8408 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
8409
8410 /* sync the state with the server */
8411 if (pTask->lastMachineState == MachineState_Running)
8412 that->setMachineStateLocally(MachineState_LiveSnapshotting);
8413 else
8414 that->setMachineStateLocally(MachineState_Saving);
8415
8416 // STEP 3: save the VM state (if online)
8417 if (pTask->fTakingSnapshotOnline)
8418 {
8419 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
8420
8421 SafeVMPtr ptrVM(that);
8422 if (!ptrVM.isOk())
8423 throw ptrVM.rc();
8424
8425 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
8426 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
8427 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
8428
8429 alock.leave();
8430 LogFlowFunc(("VMR3Save...\n"));
8431 int vrc = VMR3Save(ptrVM,
8432 strSavedStateFile.c_str(),
8433 true /*fContinueAfterwards*/,
8434 Console::stateProgressCallback,
8435 static_cast<IProgress *>(pTask->mProgress),
8436 &fSuspenededBySave);
8437 alock.enter();
8438 if (RT_FAILURE(vrc))
8439 throw setErrorStatic(E_FAIL,
8440 tr("Failed to save the machine state to '%s' (%Rrc)"),
8441 strSavedStateFile.c_str(), vrc);
8442
8443 pTask->mProgress->setCancelCallback(NULL, NULL);
8444 if (!pTask->mProgress->notifyPointOfNoReturn())
8445 throw setErrorStatic(E_FAIL, tr("Canceled"));
8446 that->mptrCancelableProgress.setNull();
8447
8448 // STEP 4: reattach hard disks
8449 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
8450
8451 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
8452 1); // operation weight, same as computed when setting up progress object
8453
8454 com::SafeIfaceArray<IMediumAttachment> atts;
8455 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
8456 if (FAILED(rc))
8457 throw rc;
8458
8459 for (size_t i = 0;
8460 i < atts.size();
8461 ++i)
8462 {
8463 ComPtr<IStorageController> pStorageController;
8464 Bstr controllerName;
8465 ULONG lInstance;
8466 StorageControllerType_T enmController;
8467 StorageBus_T enmBus;
8468 BOOL fUseHostIOCache;
8469
8470 /*
8471 * We can't pass a storage controller object directly
8472 * (g++ complains about not being able to pass non POD types through '...')
8473 * so we have to query needed values here and pass them.
8474 */
8475 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
8476 if (FAILED(rc))
8477 throw rc;
8478
8479 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
8480 pStorageController.asOutParam());
8481 if (FAILED(rc))
8482 throw rc;
8483
8484 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
8485 if (FAILED(rc))
8486 throw rc;
8487 rc = pStorageController->COMGETTER(Instance)(&lInstance);
8488 if (FAILED(rc))
8489 throw rc;
8490 rc = pStorageController->COMGETTER(Bus)(&enmBus);
8491 if (FAILED(rc))
8492 throw rc;
8493 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
8494 if (FAILED(rc))
8495 throw rc;
8496
8497 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
8498
8499 BOOL fBuiltinIoCache;
8500 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
8501 if (FAILED(rc))
8502 throw rc;
8503
8504 /*
8505 * don't leave the lock since reconfigureMediumAttachment
8506 * isn't going to need the Console lock.
8507 */
8508 vrc = VMR3ReqCallWait(ptrVM,
8509 VMCPUID_ANY,
8510 (PFNRT)reconfigureMediumAttachment,
8511 13,
8512 that,
8513 ptrVM.raw(),
8514 pcszDevice,
8515 lInstance,
8516 enmBus,
8517 fUseHostIOCache,
8518 fBuiltinIoCache,
8519 false /* fSetupMerge */,
8520 0 /* uMergeSource */,
8521 0 /* uMergeTarget */,
8522 atts[i],
8523 that->mMachineState,
8524 &rc);
8525 if (RT_FAILURE(vrc))
8526 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
8527 if (FAILED(rc))
8528 throw rc;
8529 }
8530 }
8531
8532 /*
8533 * finalize the requested snapshot object.
8534 * This will reset the machine state to the state it had right
8535 * before calling mControl->BeginTakingSnapshot().
8536 */
8537 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
8538 // do not throw rc here because we can't call EndTakingSnapshot() twice
8539 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8540 }
8541 catch (HRESULT rcThrown)
8542 {
8543 /* preserve existing error info */
8544 ErrorInfoKeeper eik;
8545
8546 if (fBeganTakingSnapshot)
8547 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
8548
8549 rc = rcThrown;
8550 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8551 }
8552 Assert(alock.isWriteLockOnCurrentThread());
8553
8554 if (FAILED(rc)) /* Must come before calling setMachineState. */
8555 pTask->mProgress->notifyComplete(rc);
8556
8557 /*
8558 * Fix up the machine state.
8559 *
8560 * For live snapshots we do all the work, for the two other variations we
8561 * just update the local copy.
8562 */
8563 MachineState_T enmMachineState;
8564 that->mMachine->COMGETTER(State)(&enmMachineState);
8565 if ( that->mMachineState == MachineState_LiveSnapshotting
8566 || that->mMachineState == MachineState_Saving)
8567 {
8568
8569 if (!pTask->fTakingSnapshotOnline)
8570 that->setMachineStateLocally(pTask->lastMachineState);
8571 else if (SUCCEEDED(rc))
8572 {
8573 Assert( pTask->lastMachineState == MachineState_Running
8574 || pTask->lastMachineState == MachineState_Paused);
8575 Assert(that->mMachineState == MachineState_Saving);
8576 if (pTask->lastMachineState == MachineState_Running)
8577 {
8578 LogFlowFunc(("VMR3Resume...\n"));
8579 SafeVMPtr ptrVM(that);
8580 alock.leave();
8581 int vrc = VMR3Resume(ptrVM);
8582 alock.enter();
8583 if (RT_FAILURE(vrc))
8584 {
8585 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
8586 pTask->mProgress->notifyComplete(rc);
8587 if (that->mMachineState == MachineState_Saving)
8588 that->setMachineStateLocally(MachineState_Paused);
8589 }
8590 }
8591 else
8592 that->setMachineStateLocally(MachineState_Paused);
8593 }
8594 else
8595 {
8596 /** @todo this could probably be made more generic and reused elsewhere. */
8597 /* paranoid cleanup on for a failed online snapshot. */
8598 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
8599 switch (enmVMState)
8600 {
8601 case VMSTATE_RUNNING:
8602 case VMSTATE_RUNNING_LS:
8603 case VMSTATE_DEBUGGING:
8604 case VMSTATE_DEBUGGING_LS:
8605 case VMSTATE_POWERING_OFF:
8606 case VMSTATE_POWERING_OFF_LS:
8607 case VMSTATE_RESETTING:
8608 case VMSTATE_RESETTING_LS:
8609 Assert(!fSuspenededBySave);
8610 that->setMachineState(MachineState_Running);
8611 break;
8612
8613 case VMSTATE_GURU_MEDITATION:
8614 case VMSTATE_GURU_MEDITATION_LS:
8615 that->setMachineState(MachineState_Stuck);
8616 break;
8617
8618 case VMSTATE_FATAL_ERROR:
8619 case VMSTATE_FATAL_ERROR_LS:
8620 if (pTask->lastMachineState == MachineState_Paused)
8621 that->setMachineStateLocally(pTask->lastMachineState);
8622 else
8623 that->setMachineState(MachineState_Paused);
8624 break;
8625
8626 default:
8627 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8628 case VMSTATE_SUSPENDED:
8629 case VMSTATE_SUSPENDED_LS:
8630 case VMSTATE_SUSPENDING:
8631 case VMSTATE_SUSPENDING_LS:
8632 case VMSTATE_SUSPENDING_EXT_LS:
8633 if (fSuspenededBySave)
8634 {
8635 Assert(pTask->lastMachineState == MachineState_Running);
8636 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8637 SafeVMPtr ptrVM(that);
8638 alock.leave();
8639 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
8640 alock.enter();
8641 if (RT_FAILURE(vrc))
8642 that->setMachineState(MachineState_Paused);
8643 }
8644 else if (pTask->lastMachineState == MachineState_Paused)
8645 that->setMachineStateLocally(pTask->lastMachineState);
8646 else
8647 that->setMachineState(MachineState_Paused);
8648 break;
8649 }
8650
8651 }
8652 }
8653 /*else: somebody else has change the state... Leave it. */
8654
8655 /* check the remote state to see that we got it right. */
8656 that->mMachine->COMGETTER(State)(&enmMachineState);
8657 AssertLogRelMsg(that->mMachineState == enmMachineState,
8658 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8659 Global::stringifyMachineState(enmMachineState) ));
8660
8661
8662 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8663 pTask->mProgress->notifyComplete(rc);
8664
8665 delete pTask;
8666
8667 LogFlowFuncLeave();
8668 return VINF_SUCCESS;
8669}
8670
8671/**
8672 * Thread for executing the saved state operation.
8673 *
8674 * @param Thread The thread handle.
8675 * @param pvUser Pointer to a VMSaveTask structure.
8676 * @return VINF_SUCCESS (ignored).
8677 *
8678 * @note Locks the Console object for writing.
8679 */
8680/*static*/
8681DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8682{
8683 LogFlowFuncEnter();
8684
8685 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8686 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8687
8688 Assert(task->mSavedStateFile.length());
8689 Assert(task->mProgress.isNull());
8690 Assert(!task->mServerProgress.isNull());
8691
8692 const ComObjPtr<Console> &that = task->mConsole;
8693 Utf8Str errMsg;
8694 HRESULT rc = S_OK;
8695
8696 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
8697
8698 bool fSuspenededBySave;
8699 int vrc = VMR3Save(task->mpVM,
8700 task->mSavedStateFile.c_str(),
8701 false, /*fContinueAfterwards*/
8702 Console::stateProgressCallback,
8703 static_cast<IProgress *>(task->mServerProgress),
8704 &fSuspenededBySave);
8705 if (RT_FAILURE(vrc))
8706 {
8707 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8708 task->mSavedStateFile.c_str(), vrc);
8709 rc = E_FAIL;
8710 }
8711 Assert(!fSuspenededBySave);
8712
8713 /* lock the console once we're going to access it */
8714 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8715
8716 /* synchronize the state with the server */
8717 if (SUCCEEDED(rc))
8718 {
8719 /*
8720 * The machine has been successfully saved, so power it down
8721 * (vmstateChangeCallback() will set state to Saved on success).
8722 * Note: we release the task's VM caller, otherwise it will
8723 * deadlock.
8724 */
8725 task->releaseVMCaller();
8726 rc = that->powerDown();
8727 }
8728
8729 /*
8730 * Finalize the requested save state procedure. In case of failure it will
8731 * reset the machine state to the state it had right before calling
8732 * mControl->BeginSavingState(). This must be the last thing because it
8733 * will set the progress to completed, and that means that the frontend
8734 * can immediately uninit the associated console object.
8735 */
8736 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
8737
8738 LogFlowFuncLeave();
8739 return VINF_SUCCESS;
8740}
8741
8742/**
8743 * Thread for powering down the Console.
8744 *
8745 * @param Thread The thread handle.
8746 * @param pvUser Pointer to the VMTask structure.
8747 * @return VINF_SUCCESS (ignored).
8748 *
8749 * @note Locks the Console object for writing.
8750 */
8751/*static*/
8752DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8753{
8754 LogFlowFuncEnter();
8755
8756 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
8757 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8758
8759 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8760
8761 Assert(task->mProgress.isNull());
8762
8763 const ComObjPtr<Console> &that = task->mConsole;
8764
8765 /* Note: no need to use addCaller() to protect Console because VMTask does
8766 * that */
8767
8768 /* wait until the method tat started us returns */
8769 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8770
8771 /* release VM caller to avoid the powerDown() deadlock */
8772 task->releaseVMCaller();
8773
8774 that->powerDown(task->mServerProgress);
8775
8776 /* complete the operation */
8777 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
8778
8779 LogFlowFuncLeave();
8780 return VINF_SUCCESS;
8781}
8782
8783
8784/**
8785 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
8786 */
8787/*static*/
8788DECLCALLBACK(int) Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PVM pVM)
8789{
8790 Console *pConsole = *(Console **)(pThis + 1); /* lazy bird */
8791
8792 /*
8793 * For now, just call SaveState. We should probably try notify the GUI so
8794 * it can pop up a progress object and stuff.
8795 */
8796 HRESULT hrc = pConsole->SaveState(NULL);
8797 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
8798}
8799
8800
8801
8802/**
8803 * The Main status driver instance data.
8804 */
8805typedef struct DRVMAINSTATUS
8806{
8807 /** The LED connectors. */
8808 PDMILEDCONNECTORS ILedConnectors;
8809 /** Pointer to the LED ports interface above us. */
8810 PPDMILEDPORTS pLedPorts;
8811 /** Pointer to the array of LED pointers. */
8812 PPDMLED *papLeds;
8813 /** The unit number corresponding to the first entry in the LED array. */
8814 RTUINT iFirstLUN;
8815 /** The unit number corresponding to the last entry in the LED array.
8816 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8817 RTUINT iLastLUN;
8818} DRVMAINSTATUS, *PDRVMAINSTATUS;
8819
8820
8821/**
8822 * Notification about a unit which have been changed.
8823 *
8824 * The driver must discard any pointers to data owned by
8825 * the unit and requery it.
8826 *
8827 * @param pInterface Pointer to the interface structure containing the called function pointer.
8828 * @param iLUN The unit number.
8829 */
8830DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8831{
8832 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8833 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8834 {
8835 PPDMLED pLed;
8836 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8837 if (RT_FAILURE(rc))
8838 pLed = NULL;
8839 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8840 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8841 }
8842}
8843
8844
8845/**
8846 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8847 */
8848DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8849{
8850 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8851 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8852 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8853 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8854 return NULL;
8855}
8856
8857
8858/**
8859 * Destruct a status driver instance.
8860 *
8861 * @returns VBox status.
8862 * @param pDrvIns The driver instance data.
8863 */
8864DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8865{
8866 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8867 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8868 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8869
8870 if (pData->papLeds)
8871 {
8872 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8873 while (iLed-- > 0)
8874 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8875 }
8876}
8877
8878
8879/**
8880 * Construct a status driver instance.
8881 *
8882 * @copydoc FNPDMDRVCONSTRUCT
8883 */
8884DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8885{
8886 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8887 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8888 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8889
8890 /*
8891 * Validate configuration.
8892 */
8893 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8894 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8895 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8896 ("Configuration error: Not possible to attach anything to this driver!\n"),
8897 VERR_PDM_DRVINS_NO_ATTACH);
8898
8899 /*
8900 * Data.
8901 */
8902 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8903 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8904
8905 /*
8906 * Read config.
8907 */
8908 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8909 if (RT_FAILURE(rc))
8910 {
8911 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8912 return rc;
8913 }
8914
8915 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8916 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8917 pData->iFirstLUN = 0;
8918 else if (RT_FAILURE(rc))
8919 {
8920 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8921 return rc;
8922 }
8923
8924 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8925 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8926 pData->iLastLUN = 0;
8927 else if (RT_FAILURE(rc))
8928 {
8929 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8930 return rc;
8931 }
8932 if (pData->iFirstLUN > pData->iLastLUN)
8933 {
8934 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8935 return VERR_GENERAL_FAILURE;
8936 }
8937
8938 /*
8939 * Get the ILedPorts interface of the above driver/device and
8940 * query the LEDs we want.
8941 */
8942 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8943 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8944 VERR_PDM_MISSING_INTERFACE_ABOVE);
8945
8946 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8947 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8948
8949 return VINF_SUCCESS;
8950}
8951
8952
8953/**
8954 * Keyboard driver registration record.
8955 */
8956const PDMDRVREG Console::DrvStatusReg =
8957{
8958 /* u32Version */
8959 PDM_DRVREG_VERSION,
8960 /* szName */
8961 "MainStatus",
8962 /* szRCMod */
8963 "",
8964 /* szR0Mod */
8965 "",
8966 /* pszDescription */
8967 "Main status driver (Main as in the API).",
8968 /* fFlags */
8969 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8970 /* fClass. */
8971 PDM_DRVREG_CLASS_STATUS,
8972 /* cMaxInstances */
8973 ~0,
8974 /* cbInstance */
8975 sizeof(DRVMAINSTATUS),
8976 /* pfnConstruct */
8977 Console::drvStatus_Construct,
8978 /* pfnDestruct */
8979 Console::drvStatus_Destruct,
8980 /* pfnRelocate */
8981 NULL,
8982 /* pfnIOCtl */
8983 NULL,
8984 /* pfnPowerOn */
8985 NULL,
8986 /* pfnReset */
8987 NULL,
8988 /* pfnSuspend */
8989 NULL,
8990 /* pfnResume */
8991 NULL,
8992 /* pfnAttach */
8993 NULL,
8994 /* pfnDetach */
8995 NULL,
8996 /* pfnPowerOff */
8997 NULL,
8998 /* pfnSoftReset */
8999 NULL,
9000 /* u32EndVersion */
9001 PDM_DRVREG_VERSION
9002};
9003
9004/* 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