VirtualBox

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

Last change on this file since 43064 was 43064, checked in by vboxsync, 13 years ago

Main/Console: fix incorrect condition which prevented cancelling online snapshots from the GUI, was there for a very long time

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette