VirtualBox

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

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

OSE fix

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