VirtualBox

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

Last change on this file since 43060 was 42907, checked in by vboxsync, 12 years ago

Main: typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 329.0 KB
Line 
1/* $Id: ConsoleImpl.cpp 42907 2012-08-21 13:53:54Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @todo Move the TAP mess back into the driver! */
19#if defined(RT_OS_WINDOWS)
20#elif defined(RT_OS_LINUX)
21# include <errno.h>
22# include <sys/ioctl.h>
23# include <sys/poll.h>
24# include <sys/fcntl.h>
25# include <sys/types.h>
26# include <sys/wait.h>
27# include <net/if.h>
28# include <linux/if_tun.h>
29# include <stdio.h>
30# include <stdlib.h>
31# include <string.h>
32#elif defined(RT_OS_FREEBSD)
33# include <errno.h>
34# include <sys/ioctl.h>
35# include <sys/poll.h>
36# include <sys/fcntl.h>
37# include <sys/types.h>
38# include <sys/wait.h>
39# include <stdio.h>
40# include <stdlib.h>
41# include <string.h>
42#elif defined(RT_OS_SOLARIS)
43# include <iprt/coredumper.h>
44#endif
45
46#include "ConsoleImpl.h"
47
48#include "Global.h"
49#include "VirtualBoxErrorInfoImpl.h"
50#include "GuestImpl.h"
51#include "KeyboardImpl.h"
52#include "MouseImpl.h"
53#include "DisplayImpl.h"
54#include "MachineDebuggerImpl.h"
55#include "USBDeviceImpl.h"
56#include "RemoteUSBDeviceImpl.h"
57#include "SharedFolderImpl.h"
58#include "AudioSnifferInterface.h"
59#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_Running /* aCancelable */,
3340 cOperations,
3341 ulTotalOperationsWeight,
3342 Bstr(tr("Setting up snapshot operation")).raw(), // first sub-op description
3343 1); // ulFirstOperationWeight
3344
3345 if (FAILED(rc))
3346 return rc;
3347
3348 VMTakeSnapshotTask *pTask;
3349 if (!(pTask = new VMTakeSnapshotTask(this, pProgress, aName, aDescription)))
3350 return E_OUTOFMEMORY;
3351
3352 Assert(pTask->mProgress);
3353
3354 try
3355 {
3356 mptrCancelableProgress = pProgress;
3357
3358 /*
3359 * If we fail here it means a PowerDown() call happened on another
3360 * thread while we were doing Pause() (which releases the Console lock).
3361 * We assign PowerDown() a higher precedence than TakeSnapshot(),
3362 * therefore just return the error to the caller.
3363 */
3364 rc = pTask->rc();
3365 if (FAILED(rc)) throw rc;
3366
3367 pTask->ulMemSize = ulMemSize;
3368
3369 /* memorize the current machine state */
3370 pTask->lastMachineState = mMachineState;
3371 pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
3372
3373 int vrc = RTThreadCreate(NULL,
3374 Console::fntTakeSnapshotWorker,
3375 (void *)pTask,
3376 0,
3377 RTTHREADTYPE_MAIN_WORKER,
3378 0,
3379 "TakeSnap");
3380 if (FAILED(vrc))
3381 throw setError(E_FAIL,
3382 tr("Could not create VMTakeSnap thread (%Rrc)"),
3383 vrc);
3384
3385 pTask->mProgress.queryInterfaceTo(aProgress);
3386 }
3387 catch (HRESULT erc)
3388 {
3389 delete pTask;
3390 rc = erc;
3391 mptrCancelableProgress.setNull();
3392 }
3393
3394 LogFlowThisFunc(("rc=%Rhrc\n", rc));
3395 LogFlowThisFuncLeave();
3396 return rc;
3397}
3398
3399STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
3400{
3401 CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
3402 CheckComArgOutPointerValid(aProgress);
3403
3404 AutoCaller autoCaller(this);
3405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3406
3407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3408
3409 if (Global::IsTransient(mMachineState))
3410 return setError(VBOX_E_INVALID_VM_STATE,
3411 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3412 Global::stringifyMachineState(mMachineState));
3413
3414 MachineState_T machineState = MachineState_Null;
3415 HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3416 if (FAILED(rc)) return rc;
3417
3418 setMachineStateLocally(machineState);
3419 return S_OK;
3420}
3421
3422STDMETHODIMP Console::DeleteSnapshotAndAllChildren(IN_BSTR aId, IProgress **aProgress)
3423{
3424 CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
3425 CheckComArgOutPointerValid(aProgress);
3426
3427 AutoCaller autoCaller(this);
3428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3429
3430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3431
3432 if (Global::IsTransient(mMachineState))
3433 return setError(VBOX_E_INVALID_VM_STATE,
3434 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3435 Global::stringifyMachineState(mMachineState));
3436
3437 MachineState_T machineState = MachineState_Null;
3438 HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, TRUE /* fDeleteAllChildren */, &machineState, aProgress);
3439 if (FAILED(rc)) return rc;
3440
3441 setMachineStateLocally(machineState);
3442 return S_OK;
3443}
3444
3445STDMETHODIMP Console::DeleteSnapshotRange(IN_BSTR aStartId, IN_BSTR aEndId, IProgress **aProgress)
3446{
3447 CheckComArgExpr(aStartId, Guid(aStartId).isEmpty() == false);
3448 CheckComArgExpr(aEndId, Guid(aEndId).isEmpty() == false);
3449 CheckComArgOutPointerValid(aProgress);
3450
3451 AutoCaller autoCaller(this);
3452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3453
3454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3455
3456 if (Global::IsTransient(mMachineState))
3457 return setError(VBOX_E_INVALID_VM_STATE,
3458 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
3459 Global::stringifyMachineState(mMachineState));
3460
3461 MachineState_T machineState = MachineState_Null;
3462 HRESULT rc = mControl->DeleteSnapshot(this, aStartId, aEndId, FALSE /* fDeleteAllChildren */, &machineState, aProgress);
3463 if (FAILED(rc)) return rc;
3464
3465 setMachineStateLocally(machineState);
3466 return S_OK;
3467}
3468
3469STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
3470{
3471 AutoCaller autoCaller(this);
3472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3473
3474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3475
3476 if (Global::IsOnlineOrTransient(mMachineState))
3477 return setError(VBOX_E_INVALID_VM_STATE,
3478 tr("Cannot delete the current state of the running machine (machine state: %s)"),
3479 Global::stringifyMachineState(mMachineState));
3480
3481 MachineState_T machineState = MachineState_Null;
3482 HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
3483 if (FAILED(rc)) return rc;
3484
3485 setMachineStateLocally(machineState);
3486 return S_OK;
3487}
3488
3489// Non-interface public methods
3490/////////////////////////////////////////////////////////////////////////////
3491
3492/*static*/
3493HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
3494{
3495 va_list args;
3496 va_start(args, pcsz);
3497 HRESULT rc = setErrorInternal(aResultCode,
3498 getStaticClassIID(),
3499 getStaticComponentName(),
3500 Utf8Str(pcsz, args),
3501 false /* aWarning */,
3502 true /* aLogIt */);
3503 va_end(args);
3504 return rc;
3505}
3506
3507HRESULT Console::setInvalidMachineStateError()
3508{
3509 return setError(VBOX_E_INVALID_VM_STATE,
3510 tr("Invalid machine state: %s"),
3511 Global::stringifyMachineState(mMachineState));
3512}
3513
3514
3515/* static */
3516const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3517{
3518 switch (enmCtrlType)
3519 {
3520 case StorageControllerType_LsiLogic:
3521 return "lsilogicscsi";
3522 case StorageControllerType_BusLogic:
3523 return "buslogic";
3524 case StorageControllerType_LsiLogicSas:
3525 return "lsilogicsas";
3526 case StorageControllerType_IntelAhci:
3527 return "ahci";
3528 case StorageControllerType_PIIX3:
3529 case StorageControllerType_PIIX4:
3530 case StorageControllerType_ICH6:
3531 return "piix3ide";
3532 case StorageControllerType_I82078:
3533 return "i82078";
3534 default:
3535 return NULL;
3536 }
3537}
3538
3539HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3540{
3541 switch (enmBus)
3542 {
3543 case StorageBus_IDE:
3544 case StorageBus_Floppy:
3545 {
3546 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3547 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3548 uLun = 2 * port + device;
3549 return S_OK;
3550 }
3551 case StorageBus_SATA:
3552 case StorageBus_SCSI:
3553 case StorageBus_SAS:
3554 {
3555 uLun = port;
3556 return S_OK;
3557 }
3558 default:
3559 uLun = 0;
3560 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3561 }
3562}
3563
3564// private methods
3565/////////////////////////////////////////////////////////////////////////////
3566
3567/**
3568 * Process a medium change.
3569 *
3570 * @param aMediumAttachment The medium attachment with the new medium state.
3571 * @param fForce Force medium chance, if it is locked or not.
3572 * @param pVM Safe VM handle.
3573 *
3574 * @note Locks this object for writing.
3575 */
3576HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PVM pVM)
3577{
3578 AutoCaller autoCaller(this);
3579 AssertComRCReturnRC(autoCaller.rc());
3580
3581 /* We will need to release the write lock before calling EMT */
3582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3583
3584 HRESULT rc = S_OK;
3585 const char *pszDevice = NULL;
3586
3587 SafeIfaceArray<IStorageController> ctrls;
3588 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3589 AssertComRC(rc);
3590 IMedium *pMedium;
3591 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3592 AssertComRC(rc);
3593 Bstr mediumLocation;
3594 if (pMedium)
3595 {
3596 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3597 AssertComRC(rc);
3598 }
3599
3600 Bstr attCtrlName;
3601 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3602 AssertComRC(rc);
3603 ComPtr<IStorageController> pStorageController;
3604 for (size_t i = 0; i < ctrls.size(); ++i)
3605 {
3606 Bstr ctrlName;
3607 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3608 AssertComRC(rc);
3609 if (attCtrlName == ctrlName)
3610 {
3611 pStorageController = ctrls[i];
3612 break;
3613 }
3614 }
3615 if (pStorageController.isNull())
3616 return setError(E_FAIL,
3617 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3618
3619 StorageControllerType_T enmCtrlType;
3620 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3621 AssertComRC(rc);
3622 pszDevice = convertControllerTypeToDev(enmCtrlType);
3623
3624 StorageBus_T enmBus;
3625 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3626 AssertComRC(rc);
3627 ULONG uInstance;
3628 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3629 AssertComRC(rc);
3630 BOOL fUseHostIOCache;
3631 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3632 AssertComRC(rc);
3633
3634 /*
3635 * Call worker in EMT, that's faster and safer than doing everything
3636 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3637 * here to make requests from under the lock in order to serialize them.
3638 */
3639 PVMREQ pReq;
3640 int vrc = VMR3ReqCall(pVM,
3641 VMCPUID_ANY,
3642 &pReq,
3643 0 /* no wait! */,
3644 VMREQFLAGS_VBOX_STATUS,
3645 (PFNRT)Console::changeRemovableMedium,
3646 8,
3647 this,
3648 pVM,
3649 pszDevice,
3650 uInstance,
3651 enmBus,
3652 fUseHostIOCache,
3653 aMediumAttachment,
3654 fForce);
3655
3656 /* release the lock before waiting for a result (EMT will call us back!) */
3657 alock.release();
3658
3659 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3660 {
3661 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3662 AssertRC(vrc);
3663 if (RT_SUCCESS(vrc))
3664 vrc = pReq->iStatus;
3665 }
3666 VMR3ReqFree(pReq);
3667
3668 if (RT_SUCCESS(vrc))
3669 {
3670 LogFlowThisFunc(("Returns S_OK\n"));
3671 return S_OK;
3672 }
3673
3674 if (pMedium)
3675 return setError(E_FAIL,
3676 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3677 mediumLocation.raw(), vrc);
3678
3679 return setError(E_FAIL,
3680 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3681 vrc);
3682}
3683
3684/**
3685 * Performs the medium change in EMT.
3686 *
3687 * @returns VBox status code.
3688 *
3689 * @param pThis Pointer to the Console object.
3690 * @param pVM The VM handle.
3691 * @param pcszDevice The PDM device name.
3692 * @param uInstance The PDM device instance.
3693 * @param uLun The PDM LUN number of the drive.
3694 * @param fHostDrive True if this is a host drive attachment.
3695 * @param pszPath The path to the media / drive which is now being mounted / captured.
3696 * If NULL no media or drive is attached and the LUN will be configured with
3697 * the default block driver with no media. This will also be the state if
3698 * mounting / capturing the specified media / drive fails.
3699 * @param pszFormat Medium format string, usually "RAW".
3700 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3701 *
3702 * @thread EMT
3703 */
3704DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3705 PVM pVM,
3706 const char *pcszDevice,
3707 unsigned uInstance,
3708 StorageBus_T enmBus,
3709 bool fUseHostIOCache,
3710 IMediumAttachment *aMediumAtt,
3711 bool fForce)
3712{
3713 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3714 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt, fForce));
3715
3716 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3717
3718 AutoCaller autoCaller(pConsole);
3719 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3720
3721 /*
3722 * Suspend the VM first.
3723 *
3724 * The VM must not be running since it might have pending I/O to
3725 * the drive which is being changed.
3726 */
3727 bool fResume;
3728 VMSTATE enmVMState = VMR3GetState(pVM);
3729 switch (enmVMState)
3730 {
3731 case VMSTATE_RESETTING:
3732 case VMSTATE_RUNNING:
3733 {
3734 LogFlowFunc(("Suspending the VM...\n"));
3735 /* disable the callback to prevent Console-level state change */
3736 pConsole->mVMStateChangeCallbackDisabled = true;
3737 int rc = VMR3Suspend(pVM);
3738 pConsole->mVMStateChangeCallbackDisabled = false;
3739 AssertRCReturn(rc, rc);
3740 fResume = true;
3741 break;
3742 }
3743
3744 case VMSTATE_SUSPENDED:
3745 case VMSTATE_CREATED:
3746 case VMSTATE_OFF:
3747 fResume = false;
3748 break;
3749
3750 case VMSTATE_RUNNING_LS:
3751 case VMSTATE_RUNNING_FT:
3752 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3753 COM_IIDOF(IConsole),
3754 getStaticComponentName(),
3755 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
3756 false /*aWarning*/,
3757 true /*aLogIt*/);
3758
3759 default:
3760 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3761 }
3762
3763 /* Determine the base path for the device instance. */
3764 PCFGMNODE pCtlInst;
3765 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
3766 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3767
3768 int rc = VINF_SUCCESS;
3769 int rcRet = VINF_SUCCESS;
3770
3771 rcRet = pConsole->configMediumAttachment(pCtlInst,
3772 pcszDevice,
3773 uInstance,
3774 enmBus,
3775 fUseHostIOCache,
3776 false /* fSetupMerge */,
3777 false /* fBuiltinIOCache */,
3778 0 /* uMergeSource */,
3779 0 /* uMergeTarget */,
3780 aMediumAtt,
3781 pConsole->mMachineState,
3782 NULL /* phrc */,
3783 true /* fAttachDetach */,
3784 fForce /* fForceUnmount */,
3785 false /* fHotplug */,
3786 pVM,
3787 NULL /* paLedDevType */);
3788 /** @todo this dumps everything attached to this device instance, which
3789 * is more than necessary. Dumping the changed LUN would be enough. */
3790 CFGMR3Dump(pCtlInst);
3791
3792 /*
3793 * Resume the VM if necessary.
3794 */
3795 if (fResume)
3796 {
3797 LogFlowFunc(("Resuming the VM...\n"));
3798 /* disable the callback to prevent Console-level state change */
3799 pConsole->mVMStateChangeCallbackDisabled = true;
3800 rc = VMR3Resume(pVM);
3801 pConsole->mVMStateChangeCallbackDisabled = false;
3802 AssertRC(rc);
3803 if (RT_FAILURE(rc))
3804 {
3805 /* too bad, we failed. try to sync the console state with the VMM state */
3806 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3807 }
3808 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3809 // error (if any) will be hidden from the caller. For proper reporting
3810 // of such multiple errors to the caller we need to enhance the
3811 // IVirtualBoxError interface. For now, give the first error the higher
3812 // priority.
3813 if (RT_SUCCESS(rcRet))
3814 rcRet = rc;
3815 }
3816
3817 LogFlowFunc(("Returning %Rrc\n", rcRet));
3818 return rcRet;
3819}
3820
3821
3822/**
3823 * Attach a new storage device to the VM.
3824 *
3825 * @param aMediumAttachment The medium attachment which is added.
3826 * @param pVM Safe VM handle.
3827 *
3828 * @note Locks this object for writing.
3829 */
3830HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM pVM)
3831{
3832 AutoCaller autoCaller(this);
3833 AssertComRCReturnRC(autoCaller.rc());
3834
3835 /* We will need to release the write lock before calling EMT */
3836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3837
3838 HRESULT rc = S_OK;
3839 const char *pszDevice = NULL;
3840
3841 SafeIfaceArray<IStorageController> ctrls;
3842 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3843 AssertComRC(rc);
3844 IMedium *pMedium;
3845 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3846 AssertComRC(rc);
3847 Bstr mediumLocation;
3848 if (pMedium)
3849 {
3850 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3851 AssertComRC(rc);
3852 }
3853
3854 Bstr attCtrlName;
3855 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3856 AssertComRC(rc);
3857 ComPtr<IStorageController> pStorageController;
3858 for (size_t i = 0; i < ctrls.size(); ++i)
3859 {
3860 Bstr ctrlName;
3861 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3862 AssertComRC(rc);
3863 if (attCtrlName == ctrlName)
3864 {
3865 pStorageController = ctrls[i];
3866 break;
3867 }
3868 }
3869 if (pStorageController.isNull())
3870 return setError(E_FAIL,
3871 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3872
3873 StorageControllerType_T enmCtrlType;
3874 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
3875 AssertComRC(rc);
3876 pszDevice = convertControllerTypeToDev(enmCtrlType);
3877
3878 StorageBus_T enmBus;
3879 rc = pStorageController->COMGETTER(Bus)(&enmBus);
3880 AssertComRC(rc);
3881 ULONG uInstance;
3882 rc = pStorageController->COMGETTER(Instance)(&uInstance);
3883 AssertComRC(rc);
3884 BOOL fUseHostIOCache;
3885 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3886 AssertComRC(rc);
3887
3888 /*
3889 * Call worker in EMT, that's faster and safer than doing everything
3890 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3891 * here to make requests from under the lock in order to serialize them.
3892 */
3893 PVMREQ pReq;
3894 int vrc = VMR3ReqCall(pVM,
3895 VMCPUID_ANY,
3896 &pReq,
3897 0 /* no wait! */,
3898 VMREQFLAGS_VBOX_STATUS,
3899 (PFNRT)Console::attachStorageDevice,
3900 7,
3901 this,
3902 pVM,
3903 pszDevice,
3904 uInstance,
3905 enmBus,
3906 fUseHostIOCache,
3907 aMediumAttachment);
3908
3909 /* release the lock before waiting for a result (EMT will call us back!) */
3910 alock.release();
3911
3912 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3913 {
3914 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3915 AssertRC(vrc);
3916 if (RT_SUCCESS(vrc))
3917 vrc = pReq->iStatus;
3918 }
3919 VMR3ReqFree(pReq);
3920
3921 if (RT_SUCCESS(vrc))
3922 {
3923 LogFlowThisFunc(("Returns S_OK\n"));
3924 return S_OK;
3925 }
3926
3927 if (!pMedium)
3928 return setError(E_FAIL,
3929 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3930 mediumLocation.raw(), vrc);
3931
3932 return setError(E_FAIL,
3933 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3934 vrc);
3935}
3936
3937
3938/**
3939 * Performs the storage attach operation in EMT.
3940 *
3941 * @returns VBox status code.
3942 *
3943 * @param pThis Pointer to the Console object.
3944 * @param pVM The VM handle.
3945 * @param pcszDevice The PDM device name.
3946 * @param uInstance The PDM device instance.
3947 *
3948 * @thread EMT
3949 */
3950DECLCALLBACK(int) Console::attachStorageDevice(Console *pConsole,
3951 PVM pVM,
3952 const char *pcszDevice,
3953 unsigned uInstance,
3954 StorageBus_T enmBus,
3955 bool fUseHostIOCache,
3956 IMediumAttachment *aMediumAtt)
3957{
3958 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n",
3959 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt));
3960
3961 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3962
3963 AutoCaller autoCaller(pConsole);
3964 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3965
3966 /*
3967 * Suspend the VM first.
3968 *
3969 * The VM must not be running since it might have pending I/O to
3970 * the drive which is being changed.
3971 */
3972 bool fResume;
3973 VMSTATE enmVMState = VMR3GetState(pVM);
3974 switch (enmVMState)
3975 {
3976 case VMSTATE_RESETTING:
3977 case VMSTATE_RUNNING:
3978 {
3979 LogFlowFunc(("Suspending the VM...\n"));
3980 /* disable the callback to prevent Console-level state change */
3981 pConsole->mVMStateChangeCallbackDisabled = true;
3982 int rc = VMR3Suspend(pVM);
3983 pConsole->mVMStateChangeCallbackDisabled = false;
3984 AssertRCReturn(rc, rc);
3985 fResume = true;
3986 break;
3987 }
3988
3989 case VMSTATE_SUSPENDED:
3990 case VMSTATE_CREATED:
3991 case VMSTATE_OFF:
3992 fResume = false;
3993 break;
3994
3995 case VMSTATE_RUNNING_LS:
3996 case VMSTATE_RUNNING_FT:
3997 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3998 COM_IIDOF(IConsole),
3999 getStaticComponentName(),
4000 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
4001 false /*aWarning*/,
4002 true /*aLogIt*/);
4003
4004 default:
4005 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4006 }
4007
4008 /* Determine the base path for the device instance. */
4009 PCFGMNODE pCtlInst;
4010 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
4011 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4012
4013 int rc = VINF_SUCCESS;
4014 int rcRet = VINF_SUCCESS;
4015
4016 rcRet = pConsole->configMediumAttachment(pCtlInst,
4017 pcszDevice,
4018 uInstance,
4019 enmBus,
4020 fUseHostIOCache,
4021 false /* fSetupMerge */,
4022 false /* fBuiltinIOCache */,
4023 0 /* uMergeSource */,
4024 0 /* uMergeTarget */,
4025 aMediumAtt,
4026 pConsole->mMachineState,
4027 NULL /* phrc */,
4028 true /* fAttachDetach */,
4029 false /* fForceUnmount */,
4030 true /* fHotplug */,
4031 pVM,
4032 NULL /* paLedDevType */);
4033 /** @todo this dumps everything attached to this device instance, which
4034 * is more than necessary. Dumping the changed LUN would be enough. */
4035 CFGMR3Dump(pCtlInst);
4036
4037 /*
4038 * Resume the VM if necessary.
4039 */
4040 if (fResume)
4041 {
4042 LogFlowFunc(("Resuming the VM...\n"));
4043 /* disable the callback to prevent Console-level state change */
4044 pConsole->mVMStateChangeCallbackDisabled = true;
4045 rc = VMR3Resume(pVM);
4046 pConsole->mVMStateChangeCallbackDisabled = false;
4047 AssertRC(rc);
4048 if (RT_FAILURE(rc))
4049 {
4050 /* too bad, we failed. try to sync the console state with the VMM state */
4051 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
4052 }
4053 /** @todo: if we failed with drive mount, then the VMR3Resume
4054 * error (if any) will be hidden from the caller. For proper reporting
4055 * of such multiple errors to the caller we need to enhance the
4056 * IVirtualBoxError interface. For now, give the first error the higher
4057 * priority.
4058 */
4059 if (RT_SUCCESS(rcRet))
4060 rcRet = rc;
4061 }
4062
4063 LogFlowFunc(("Returning %Rrc\n", rcRet));
4064 return rcRet;
4065}
4066
4067/**
4068 * Attach a new storage device to the VM.
4069 *
4070 * @param aMediumAttachment The medium attachment which is added.
4071 * @param pVM Safe VM handle.
4072 *
4073 * @note Locks this object for writing.
4074 */
4075HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM pVM)
4076{
4077 AutoCaller autoCaller(this);
4078 AssertComRCReturnRC(autoCaller.rc());
4079
4080 /* We will need to release the write lock before calling EMT */
4081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4082
4083 HRESULT rc = S_OK;
4084 const char *pszDevice = NULL;
4085
4086 SafeIfaceArray<IStorageController> ctrls;
4087 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4088 AssertComRC(rc);
4089 IMedium *pMedium;
4090 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4091 AssertComRC(rc);
4092 Bstr mediumLocation;
4093 if (pMedium)
4094 {
4095 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4096 AssertComRC(rc);
4097 }
4098
4099 Bstr attCtrlName;
4100 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4101 AssertComRC(rc);
4102 ComPtr<IStorageController> pStorageController;
4103 for (size_t i = 0; i < ctrls.size(); ++i)
4104 {
4105 Bstr ctrlName;
4106 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4107 AssertComRC(rc);
4108 if (attCtrlName == ctrlName)
4109 {
4110 pStorageController = ctrls[i];
4111 break;
4112 }
4113 }
4114 if (pStorageController.isNull())
4115 return setError(E_FAIL,
4116 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
4117
4118 StorageControllerType_T enmCtrlType;
4119 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
4120 AssertComRC(rc);
4121 pszDevice = convertControllerTypeToDev(enmCtrlType);
4122
4123 StorageBus_T enmBus;
4124 rc = pStorageController->COMGETTER(Bus)(&enmBus);
4125 AssertComRC(rc);
4126 ULONG uInstance;
4127 rc = pStorageController->COMGETTER(Instance)(&uInstance);
4128 AssertComRC(rc);
4129
4130 /*
4131 * Call worker in EMT, that's faster and safer than doing everything
4132 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4133 * here to make requests from under the lock in order to serialize them.
4134 */
4135 PVMREQ pReq;
4136 int vrc = VMR3ReqCall(pVM,
4137 VMCPUID_ANY,
4138 &pReq,
4139 0 /* no wait! */,
4140 VMREQFLAGS_VBOX_STATUS,
4141 (PFNRT)Console::detachStorageDevice,
4142 6,
4143 this,
4144 pVM,
4145 pszDevice,
4146 uInstance,
4147 enmBus,
4148 aMediumAttachment);
4149
4150 /* release the lock before waiting for a result (EMT will call us back!) */
4151 alock.release();
4152
4153 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4154 {
4155 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4156 AssertRC(vrc);
4157 if (RT_SUCCESS(vrc))
4158 vrc = pReq->iStatus;
4159 }
4160 VMR3ReqFree(pReq);
4161
4162 if (RT_SUCCESS(vrc))
4163 {
4164 LogFlowThisFunc(("Returns S_OK\n"));
4165 return S_OK;
4166 }
4167
4168 if (!pMedium)
4169 return setError(E_FAIL,
4170 tr("Could not mount the media/drive '%ls' (%Rrc)"),
4171 mediumLocation.raw(), vrc);
4172
4173 return setError(E_FAIL,
4174 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
4175 vrc);
4176}
4177
4178/**
4179 * Performs the storage detach operation in EMT.
4180 *
4181 * @returns VBox status code.
4182 *
4183 * @param pThis Pointer to the Console object.
4184 * @param pVM The VM handle.
4185 * @param pcszDevice The PDM device name.
4186 * @param uInstance The PDM device instance.
4187 *
4188 * @thread EMT
4189 */
4190DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole,
4191 PVM pVM,
4192 const char *pcszDevice,
4193 unsigned uInstance,
4194 StorageBus_T enmBus,
4195 IMediumAttachment *pMediumAtt)
4196{
4197 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n",
4198 pConsole, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt));
4199
4200 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
4201
4202 AutoCaller autoCaller(pConsole);
4203 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4204
4205 /*
4206 * Suspend the VM first.
4207 *
4208 * The VM must not be running since it might have pending I/O to
4209 * the drive which is being changed.
4210 */
4211 bool fResume;
4212 VMSTATE enmVMState = VMR3GetState(pVM);
4213 switch (enmVMState)
4214 {
4215 case VMSTATE_RESETTING:
4216 case VMSTATE_RUNNING:
4217 {
4218 LogFlowFunc(("Suspending the VM...\n"));
4219 /* disable the callback to prevent Console-level state change */
4220 pConsole->mVMStateChangeCallbackDisabled = true;
4221 int rc = VMR3Suspend(pVM);
4222 pConsole->mVMStateChangeCallbackDisabled = false;
4223 AssertRCReturn(rc, rc);
4224 fResume = true;
4225 break;
4226 }
4227
4228 case VMSTATE_SUSPENDED:
4229 case VMSTATE_CREATED:
4230 case VMSTATE_OFF:
4231 fResume = false;
4232 break;
4233
4234 case VMSTATE_RUNNING_LS:
4235 case VMSTATE_RUNNING_FT:
4236 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
4237 COM_IIDOF(IConsole),
4238 getStaticComponentName(),
4239 (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")),
4240 false /*aWarning*/,
4241 true /*aLogIt*/);
4242
4243 default:
4244 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4245 }
4246
4247 /* Determine the base path for the device instance. */
4248 PCFGMNODE pCtlInst;
4249 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
4250 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
4251
4252#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE)
4253
4254 HRESULT hrc;
4255 int rc = VINF_SUCCESS;
4256 int rcRet = VINF_SUCCESS;
4257 unsigned uLUN;
4258 LONG lDev;
4259 LONG lPort;
4260 DeviceType_T lType;
4261 PCFGMNODE pLunL0 = NULL;
4262 PCFGMNODE pCfg = NULL;
4263
4264 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
4265 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
4266 hrc = pMediumAtt->COMGETTER(Type)(&lType); H();
4267 hrc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
4268
4269#undef H
4270
4271 /* First check if the LUN really exists. */
4272 pLunL0 = CFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
4273 if (pLunL0)
4274 {
4275 rc = PDMR3DeviceDetach(pVM, pcszDevice, uInstance, uLUN, 0);
4276 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
4277 rc = VINF_SUCCESS;
4278 AssertRCReturn(rc, rc);
4279 CFGMR3RemoveNode(pLunL0);
4280
4281 Utf8Str devicePath = Utf8StrFmt("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
4282 pConsole->mapMediumAttachments.erase(devicePath);
4283
4284 }
4285 else
4286 AssertFailedReturn(VERR_INTERNAL_ERROR);
4287
4288 CFGMR3Dump(pCtlInst);
4289
4290 /*
4291 * Resume the VM if necessary.
4292 */
4293 if (fResume)
4294 {
4295 LogFlowFunc(("Resuming the VM...\n"));
4296 /* disable the callback to prevent Console-level state change */
4297 pConsole->mVMStateChangeCallbackDisabled = true;
4298 rc = VMR3Resume(pVM);
4299 pConsole->mVMStateChangeCallbackDisabled = false;
4300 AssertRC(rc);
4301 if (RT_FAILURE(rc))
4302 {
4303 /* too bad, we failed. try to sync the console state with the VMM state */
4304 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
4305 }
4306 /** @todo: if we failed with drive mount, then the VMR3Resume
4307 * error (if any) will be hidden from the caller. For proper reporting
4308 * of such multiple errors to the caller we need to enhance the
4309 * IVirtualBoxError interface. For now, give the first error the higher
4310 * priority.
4311 */
4312 if (RT_SUCCESS(rcRet))
4313 rcRet = rc;
4314 }
4315
4316 LogFlowFunc(("Returning %Rrc\n", rcRet));
4317 return rcRet;
4318}
4319
4320/**
4321 * Called by IInternalSessionControl::OnNetworkAdapterChange().
4322 *
4323 * @note Locks this object for writing.
4324 */
4325HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
4326{
4327 LogFlowThisFunc(("\n"));
4328
4329 AutoCaller autoCaller(this);
4330 AssertComRCReturnRC(autoCaller.rc());
4331
4332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4333
4334 HRESULT rc = S_OK;
4335
4336 /* don't trigger network change if the VM isn't running */
4337 SafeVMPtrQuiet ptrVM(this);
4338 if (ptrVM.isOk())
4339 {
4340 /* Get the properties we need from the adapter */
4341 BOOL fCableConnected, fTraceEnabled;
4342 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
4343 AssertComRC(rc);
4344 if (SUCCEEDED(rc))
4345 {
4346 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
4347 AssertComRC(rc);
4348 }
4349 if (SUCCEEDED(rc))
4350 {
4351 ULONG ulInstance;
4352 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
4353 AssertComRC(rc);
4354 if (SUCCEEDED(rc))
4355 {
4356 /*
4357 * Find the adapter instance, get the config interface and update
4358 * the link state.
4359 */
4360 NetworkAdapterType_T adapterType;
4361 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4362 AssertComRC(rc);
4363 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4364
4365 // prevent cross-thread deadlocks, don't need the lock any more
4366 alock.release();
4367
4368 PPDMIBASE pBase;
4369 int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4370 if (RT_SUCCESS(vrc))
4371 {
4372 Assert(pBase);
4373 PPDMINETWORKCONFIG pINetCfg;
4374 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
4375 if (pINetCfg)
4376 {
4377 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
4378 fCableConnected));
4379 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
4380 fCableConnected ? PDMNETWORKLINKSTATE_UP
4381 : PDMNETWORKLINKSTATE_DOWN);
4382 ComAssertRC(vrc);
4383 }
4384 if (RT_SUCCESS(vrc) && changeAdapter)
4385 {
4386 VMSTATE enmVMState = VMR3GetState(ptrVM);
4387 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
4388 || enmVMState == VMSTATE_SUSPENDED)
4389 {
4390 if (fTraceEnabled && fCableConnected && pINetCfg)
4391 {
4392 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
4393 ComAssertRC(vrc);
4394 }
4395
4396 rc = doNetworkAdapterChange(ptrVM, pszAdapterName, ulInstance, 0, aNetworkAdapter);
4397
4398 if (fTraceEnabled && fCableConnected && pINetCfg)
4399 {
4400 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
4401 ComAssertRC(vrc);
4402 }
4403 }
4404 }
4405 }
4406 else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
4407 return setError(E_FAIL,
4408 tr("The network adapter #%u is not enabled"), ulInstance);
4409 else
4410 ComAssertRC(vrc);
4411
4412 if (RT_FAILURE(vrc))
4413 rc = E_FAIL;
4414
4415 alock.acquire();
4416 }
4417 }
4418 ptrVM.release();
4419 }
4420
4421 // definitely don't need the lock any more
4422 alock.release();
4423
4424 /* notify console callbacks on success */
4425 if (SUCCEEDED(rc))
4426 fireNetworkAdapterChangedEvent(mEventSource, aNetworkAdapter);
4427
4428 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4429 return rc;
4430}
4431
4432/**
4433 * Called by IInternalSessionControl::OnNATEngineChange().
4434 *
4435 * @note Locks this object for writing.
4436 */
4437HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove,
4438 NATProtocol_T aProto, IN_BSTR aHostIP, LONG aHostPort, IN_BSTR aGuestIP, LONG aGuestPort)
4439{
4440 LogFlowThisFunc(("\n"));
4441
4442 AutoCaller autoCaller(this);
4443 AssertComRCReturnRC(autoCaller.rc());
4444
4445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4446
4447 HRESULT rc = S_OK;
4448
4449 /* don't trigger nat engine change if the VM isn't running */
4450 SafeVMPtrQuiet ptrVM(this);
4451 if (ptrVM.isOk())
4452 {
4453 do
4454 {
4455 ComPtr<INetworkAdapter> pNetworkAdapter;
4456 rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam());
4457 if ( FAILED(rc)
4458 || pNetworkAdapter.isNull())
4459 break;
4460
4461 /*
4462 * Find the adapter instance, get the config interface and update
4463 * the link state.
4464 */
4465 NetworkAdapterType_T adapterType;
4466 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4467 if (FAILED(rc))
4468 {
4469 AssertComRC(rc);
4470 rc = E_FAIL;
4471 break;
4472 }
4473
4474 const char *pszAdapterName = networkAdapterTypeToName(adapterType);
4475 PPDMIBASE pBase;
4476 int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase);
4477 if (RT_FAILURE(vrc))
4478 {
4479 ComAssertRC(vrc);
4480 rc = E_FAIL;
4481 break;
4482 }
4483
4484 NetworkAttachmentType_T attachmentType;
4485 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4486 if ( FAILED(rc)
4487 || attachmentType != NetworkAttachmentType_NAT)
4488 {
4489 rc = E_FAIL;
4490 break;
4491 }
4492
4493 /* look down for PDMINETWORKNATCONFIG interface */
4494 PPDMINETWORKNATCONFIG pNetNatCfg = NULL;
4495 while (pBase)
4496 {
4497 pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID);
4498 if (pNetNatCfg)
4499 break;
4500 /** @todo r=bird: This stinks! */
4501 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase);
4502 pBase = pDrvIns->pDownBase;
4503 }
4504 if (!pNetNatCfg)
4505 break;
4506
4507 bool fUdp = aProto == NATProtocol_UDP;
4508 vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, !!aNatRuleRemove, fUdp,
4509 Utf8Str(aHostIP).c_str(), aHostPort, Utf8Str(aGuestIP).c_str(),
4510 aGuestPort);
4511 if (RT_FAILURE(vrc))
4512 rc = E_FAIL;
4513 } while (0); /* break loop */
4514 ptrVM.release();
4515 }
4516
4517 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4518 return rc;
4519}
4520
4521
4522/**
4523 * Process a network adaptor change.
4524 *
4525 * @returns COM status code.
4526 *
4527 * @parma pVM The VM handle (caller hold this safely).
4528 * @param pszDevice The PDM device name.
4529 * @param uInstance The PDM device instance.
4530 * @param uLun The PDM LUN number of the drive.
4531 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4532 */
4533HRESULT Console::doNetworkAdapterChange(PVM pVM,
4534 const char *pszDevice,
4535 unsigned uInstance,
4536 unsigned uLun,
4537 INetworkAdapter *aNetworkAdapter)
4538{
4539 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4540 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4541
4542 AutoCaller autoCaller(this);
4543 AssertComRCReturnRC(autoCaller.rc());
4544
4545 /* Get the VM handle. */
4546 SafeVMPtr ptrVM(this);
4547 if (!ptrVM.isOk())
4548 return ptrVM.rc();
4549
4550 /*
4551 * Call worker in EMT, that's faster and safer than doing everything
4552 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
4553 * here to make requests from under the lock in order to serialize them.
4554 */
4555 PVMREQ pReq;
4556 int vrc = VMR3ReqCall(pVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
4557 (PFNRT) Console::changeNetworkAttachment, 6,
4558 this, ptrVM.raw(), pszDevice, uInstance, uLun, aNetworkAdapter);
4559
4560 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
4561 {
4562 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
4563 AssertRC(vrc);
4564 if (RT_SUCCESS(vrc))
4565 vrc = pReq->iStatus;
4566 }
4567 VMR3ReqFree(pReq);
4568
4569 if (RT_SUCCESS(vrc))
4570 {
4571 LogFlowThisFunc(("Returns S_OK\n"));
4572 return S_OK;
4573 }
4574
4575 return setError(E_FAIL,
4576 tr("Could not change the network adaptor attachement type (%Rrc)"),
4577 vrc);
4578}
4579
4580
4581/**
4582 * Performs the Network Adaptor change in EMT.
4583 *
4584 * @returns VBox status code.
4585 *
4586 * @param pThis Pointer to the Console object.
4587 * @param pVM The VM handle.
4588 * @param pszDevice The PDM device name.
4589 * @param uInstance The PDM device instance.
4590 * @param uLun The PDM LUN number of the drive.
4591 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
4592 *
4593 * @thread EMT
4594 * @note Locks the Console object for writing.
4595 */
4596DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
4597 PVM pVM,
4598 const char *pszDevice,
4599 unsigned uInstance,
4600 unsigned uLun,
4601 INetworkAdapter *aNetworkAdapter)
4602{
4603 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
4604 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
4605
4606 AssertReturn(pThis, VERR_INVALID_PARAMETER);
4607
4608 AutoCaller autoCaller(pThis);
4609 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
4610
4611 ComPtr<IVirtualBox> pVirtualBox;
4612 pThis->mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
4613 ComPtr<ISystemProperties> pSystemProperties;
4614 if (pVirtualBox)
4615 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
4616 ChipsetType_T chipsetType = ChipsetType_PIIX3;
4617 pThis->mMachine->COMGETTER(ChipsetType)(&chipsetType);
4618 ULONG maxNetworkAdapters = 0;
4619 if (pSystemProperties)
4620 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
4621 AssertMsg( ( !strcmp(pszDevice, "pcnet")
4622 || !strcmp(pszDevice, "e1000")
4623 || !strcmp(pszDevice, "virtio-net"))
4624 && uLun == 0
4625 && uInstance < maxNetworkAdapters,
4626 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4627 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
4628
4629 /*
4630 * Suspend the VM first.
4631 *
4632 * The VM must not be running since it might have pending I/O to
4633 * the drive which is being changed.
4634 */
4635 bool fResume;
4636 VMSTATE enmVMState = VMR3GetState(pVM);
4637 switch (enmVMState)
4638 {
4639 case VMSTATE_RESETTING:
4640 case VMSTATE_RUNNING:
4641 {
4642 LogFlowFunc(("Suspending the VM...\n"));
4643 /* disable the callback to prevent Console-level state change */
4644 pThis->mVMStateChangeCallbackDisabled = true;
4645 int rc = VMR3Suspend(pVM);
4646 pThis->mVMStateChangeCallbackDisabled = false;
4647 AssertRCReturn(rc, rc);
4648 fResume = true;
4649 break;
4650 }
4651
4652 case VMSTATE_SUSPENDED:
4653 case VMSTATE_CREATED:
4654 case VMSTATE_OFF:
4655 fResume = false;
4656 break;
4657
4658 default:
4659 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
4660 }
4661
4662 int rc = VINF_SUCCESS;
4663 int rcRet = VINF_SUCCESS;
4664
4665 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
4666 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
4667 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
4668 AssertRelease(pInst);
4669
4670 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst,
4671 true /*fAttachDetach*/, false /*fIgnoreConnectFailure*/);
4672
4673 /*
4674 * Resume the VM if necessary.
4675 */
4676 if (fResume)
4677 {
4678 LogFlowFunc(("Resuming the VM...\n"));
4679 /* disable the callback to prevent Console-level state change */
4680 pThis->mVMStateChangeCallbackDisabled = true;
4681 rc = VMR3Resume(pVM);
4682 pThis->mVMStateChangeCallbackDisabled = false;
4683 AssertRC(rc);
4684 if (RT_FAILURE(rc))
4685 {
4686 /* too bad, we failed. try to sync the console state with the VMM state */
4687 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
4688 }
4689 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
4690 // error (if any) will be hidden from the caller. For proper reporting
4691 // of such multiple errors to the caller we need to enhance the
4692 // IVirtualBoxError interface. For now, give the first error the higher
4693 // priority.
4694 if (RT_SUCCESS(rcRet))
4695 rcRet = rc;
4696 }
4697
4698 LogFlowFunc(("Returning %Rrc\n", rcRet));
4699 return rcRet;
4700}
4701
4702
4703/**
4704 * Called by IInternalSessionControl::OnSerialPortChange().
4705 */
4706HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
4707{
4708 LogFlowThisFunc(("\n"));
4709
4710 AutoCaller autoCaller(this);
4711 AssertComRCReturnRC(autoCaller.rc());
4712
4713 fireSerialPortChangedEvent(mEventSource, aSerialPort);
4714
4715 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4716 return S_OK;
4717}
4718
4719/**
4720 * Called by IInternalSessionControl::OnParallelPortChange().
4721 */
4722HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
4723{
4724 LogFlowThisFunc(("\n"));
4725
4726 AutoCaller autoCaller(this);
4727 AssertComRCReturnRC(autoCaller.rc());
4728
4729 fireParallelPortChangedEvent(mEventSource, aParallelPort);
4730
4731 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4732 return S_OK;
4733}
4734
4735/**
4736 * Called by IInternalSessionControl::OnStorageControllerChange().
4737 */
4738HRESULT Console::onStorageControllerChange()
4739{
4740 LogFlowThisFunc(("\n"));
4741
4742 AutoCaller autoCaller(this);
4743 AssertComRCReturnRC(autoCaller.rc());
4744
4745 fireStorageControllerChangedEvent(mEventSource);
4746
4747 LogFlowThisFunc(("Leaving rc=%#x\n", S_OK));
4748 return S_OK;
4749}
4750
4751/**
4752 * Called by IInternalSessionControl::OnMediumChange().
4753 */
4754HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
4755{
4756 LogFlowThisFunc(("\n"));
4757
4758 AutoCaller autoCaller(this);
4759 AssertComRCReturnRC(autoCaller.rc());
4760
4761 HRESULT rc = S_OK;
4762
4763 /* don't trigger medium change if the VM isn't running */
4764 SafeVMPtrQuiet ptrVM(this);
4765 if (ptrVM.isOk())
4766 {
4767 rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM);
4768 ptrVM.release();
4769 }
4770
4771 /* notify console callbacks on success */
4772 if (SUCCEEDED(rc))
4773 fireMediumChangedEvent(mEventSource, aMediumAttachment);
4774
4775 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4776 return rc;
4777}
4778
4779/**
4780 * Called by IInternalSessionControl::OnCPUChange().
4781 *
4782 * @note Locks this object for writing.
4783 */
4784HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
4785{
4786 LogFlowThisFunc(("\n"));
4787
4788 AutoCaller autoCaller(this);
4789 AssertComRCReturnRC(autoCaller.rc());
4790
4791 HRESULT rc = S_OK;
4792
4793 /* don't trigger CPU change if the VM isn't running */
4794 SafeVMPtrQuiet ptrVM(this);
4795 if (ptrVM.isOk())
4796 {
4797 if (aRemove)
4798 rc = doCPURemove(aCPU, ptrVM);
4799 else
4800 rc = doCPUAdd(aCPU, ptrVM);
4801 ptrVM.release();
4802 }
4803
4804 /* notify console callbacks on success */
4805 if (SUCCEEDED(rc))
4806 fireCPUChangedEvent(mEventSource, aCPU, aRemove);
4807
4808 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4809 return rc;
4810}
4811
4812/**
4813 * Called by IInternalSessionControl::OnCpuExecutionCapChange().
4814 *
4815 * @note Locks this object for writing.
4816 */
4817HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap)
4818{
4819 LogFlowThisFunc(("\n"));
4820
4821 AutoCaller autoCaller(this);
4822 AssertComRCReturnRC(autoCaller.rc());
4823
4824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 HRESULT rc = S_OK;
4827
4828 /* don't trigger the CPU priority change if the VM isn't running */
4829 SafeVMPtrQuiet ptrVM(this);
4830 if (ptrVM.isOk())
4831 {
4832 if ( mMachineState == MachineState_Running
4833 || mMachineState == MachineState_Teleporting
4834 || mMachineState == MachineState_LiveSnapshotting
4835 )
4836 {
4837 /* No need to call in the EMT thread. */
4838 rc = VMR3SetCpuExecutionCap(ptrVM, aExecutionCap);
4839 }
4840 else
4841 rc = setInvalidMachineStateError();
4842 ptrVM.release();
4843 }
4844
4845 /* notify console callbacks on success */
4846 if (SUCCEEDED(rc))
4847 {
4848 alock.release();
4849 fireCPUExecutionCapChangedEvent(mEventSource, aExecutionCap);
4850 }
4851
4852 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4853 return rc;
4854}
4855
4856/**
4857 * Called by IInternalSessionControl::OnClipboardModeChange().
4858 *
4859 * @note Locks this object for writing.
4860 */
4861HRESULT Console::onClipboardModeChange(ClipboardMode_T aClipboardMode)
4862{
4863 LogFlowThisFunc(("\n"));
4864
4865 AutoCaller autoCaller(this);
4866 AssertComRCReturnRC(autoCaller.rc());
4867
4868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4869
4870 HRESULT rc = S_OK;
4871
4872 /* don't trigger the Clipboard mode change if the VM isn't running */
4873 SafeVMPtrQuiet ptrVM(this);
4874 if (ptrVM.isOk())
4875 {
4876 if ( mMachineState == MachineState_Running
4877 || mMachineState == MachineState_Teleporting
4878 || mMachineState == MachineState_LiveSnapshotting)
4879 changeClipboardMode(aClipboardMode);
4880 else
4881 rc = setInvalidMachineStateError();
4882 ptrVM.release();
4883 }
4884
4885 /* notify console callbacks on success */
4886 if (SUCCEEDED(rc))
4887 {
4888 alock.release();
4889 fireClipboardModeChangedEvent(mEventSource, aClipboardMode);
4890 }
4891
4892 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4893 return rc;
4894}
4895
4896/**
4897 * Called by IInternalSessionControl::OnDragAndDropModeChange().
4898 *
4899 * @note Locks this object for writing.
4900 */
4901HRESULT Console::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
4902{
4903 LogFlowThisFunc(("\n"));
4904
4905 AutoCaller autoCaller(this);
4906 AssertComRCReturnRC(autoCaller.rc());
4907
4908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4909
4910 HRESULT rc = S_OK;
4911
4912 /* don't trigger the Drag'n'drop mode change if the VM isn't running */
4913 SafeVMPtrQuiet ptrVM(this);
4914 if (ptrVM.isOk())
4915 {
4916 if ( mMachineState == MachineState_Running
4917 || mMachineState == MachineState_Teleporting
4918 || mMachineState == MachineState_LiveSnapshotting)
4919 changeDragAndDropMode(aDragAndDropMode);
4920 else
4921 rc = setInvalidMachineStateError();
4922 ptrVM.release();
4923 }
4924
4925 /* notify console callbacks on success */
4926 if (SUCCEEDED(rc))
4927 {
4928 alock.release();
4929 fireDragAndDropModeChangedEvent(mEventSource, aDragAndDropMode);
4930 }
4931
4932 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
4933 return rc;
4934}
4935
4936/**
4937 * Called by IInternalSessionControl::OnVRDEServerChange().
4938 *
4939 * @note Locks this object for writing.
4940 */
4941HRESULT Console::onVRDEServerChange(BOOL aRestart)
4942{
4943 AutoCaller autoCaller(this);
4944 AssertComRCReturnRC(autoCaller.rc());
4945
4946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4947
4948 HRESULT rc = S_OK;
4949
4950 if ( mVRDEServer
4951 && ( mMachineState == MachineState_Running
4952 || mMachineState == MachineState_Teleporting
4953 || mMachineState == MachineState_LiveSnapshotting
4954 )
4955 )
4956 {
4957 BOOL vrdpEnabled = FALSE;
4958
4959 rc = mVRDEServer->COMGETTER(Enabled)(&vrdpEnabled);
4960 ComAssertComRCRetRC(rc);
4961
4962 if (aRestart)
4963 {
4964 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
4965 alock.release();
4966
4967 if (vrdpEnabled)
4968 {
4969 // If there was no VRDP server started the 'stop' will do nothing.
4970 // However if a server was started and this notification was called,
4971 // we have to restart the server.
4972 mConsoleVRDPServer->Stop();
4973
4974 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
4975 rc = E_FAIL;
4976 else
4977 mConsoleVRDPServer->EnableConnections();
4978 }
4979 else
4980 {
4981 mConsoleVRDPServer->Stop();
4982 }
4983
4984 alock.acquire();
4985 }
4986 }
4987
4988 /* notify console callbacks on success */
4989 if (SUCCEEDED(rc))
4990 {
4991 alock.release();
4992 fireVRDEServerChangedEvent(mEventSource);
4993 }
4994
4995 return rc;
4996}
4997
4998void Console::onVRDEServerInfoChange()
4999{
5000 AutoCaller autoCaller(this);
5001 AssertComRCReturnVoid(autoCaller.rc());
5002
5003 fireVRDEServerInfoChangedEvent(mEventSource);
5004}
5005
5006
5007/**
5008 * Called by IInternalSessionControl::OnUSBControllerChange().
5009 */
5010HRESULT Console::onUSBControllerChange()
5011{
5012 LogFlowThisFunc(("\n"));
5013
5014 AutoCaller autoCaller(this);
5015 AssertComRCReturnRC(autoCaller.rc());
5016
5017 fireUSBControllerChangedEvent(mEventSource);
5018
5019 return S_OK;
5020}
5021
5022/**
5023 * Called by IInternalSessionControl::OnSharedFolderChange().
5024 *
5025 * @note Locks this object for writing.
5026 */
5027HRESULT Console::onSharedFolderChange(BOOL aGlobal)
5028{
5029 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
5030
5031 AutoCaller autoCaller(this);
5032 AssertComRCReturnRC(autoCaller.rc());
5033
5034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 HRESULT rc = fetchSharedFolders(aGlobal);
5037
5038 /* notify console callbacks on success */
5039 if (SUCCEEDED(rc))
5040 {
5041 alock.release();
5042 fireSharedFolderChangedEvent(mEventSource, aGlobal ? (Scope_T)Scope_Global : (Scope_T)Scope_Machine);
5043 }
5044
5045 return rc;
5046}
5047
5048/**
5049 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
5050 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
5051 * returns TRUE for a given remote USB device.
5052 *
5053 * @return S_OK if the device was attached to the VM.
5054 * @return failure if not attached.
5055 *
5056 * @param aDevice
5057 * The device in question.
5058 * @param aMaskedIfs
5059 * The interfaces to hide from the guest.
5060 *
5061 * @note Locks this object for writing.
5062 */
5063HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
5064{
5065#ifdef VBOX_WITH_USB
5066 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
5067
5068 AutoCaller autoCaller(this);
5069 ComAssertComRCRetRC(autoCaller.rc());
5070
5071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5072
5073 /* Get the VM pointer (we don't need error info, since it's a callback). */
5074 SafeVMPtrQuiet ptrVM(this);
5075 if (!ptrVM.isOk())
5076 {
5077 /* The VM may be no more operational when this message arrives
5078 * (e.g. it may be Saving or Stopping or just PoweredOff) --
5079 * autoVMCaller.rc() will return a failure in this case. */
5080 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
5081 mMachineState));
5082 return ptrVM.rc();
5083 }
5084
5085 if (aError != NULL)
5086 {
5087 /* notify callbacks about the error */
5088 alock.release();
5089 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
5090 return S_OK;
5091 }
5092
5093 /* Don't proceed unless there's at least one USB hub. */
5094 if (!PDMR3USBHasHub(ptrVM))
5095 {
5096 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
5097 return E_FAIL;
5098 }
5099
5100 alock.release();
5101 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
5102 if (FAILED(rc))
5103 {
5104 /* take the current error info */
5105 com::ErrorInfoKeeper eik;
5106 /* the error must be a VirtualBoxErrorInfo instance */
5107 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5108 Assert(!pError.isNull());
5109 if (!pError.isNull())
5110 {
5111 /* notify callbacks about the error */
5112 onUSBDeviceStateChange(aDevice, true /* aAttached */, pError);
5113 }
5114 }
5115
5116 return rc;
5117
5118#else /* !VBOX_WITH_USB */
5119 return E_FAIL;
5120#endif /* !VBOX_WITH_USB */
5121}
5122
5123/**
5124 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
5125 * processRemoteUSBDevices().
5126 *
5127 * @note Locks this object for writing.
5128 */
5129HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
5130 IVirtualBoxErrorInfo *aError)
5131{
5132#ifdef VBOX_WITH_USB
5133 Guid Uuid(aId);
5134 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
5135
5136 AutoCaller autoCaller(this);
5137 AssertComRCReturnRC(autoCaller.rc());
5138
5139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5140
5141 /* Find the device. */
5142 ComObjPtr<OUSBDevice> pUSBDevice;
5143 USBDeviceList::iterator it = mUSBDevices.begin();
5144 while (it != mUSBDevices.end())
5145 {
5146 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
5147 if ((*it)->id() == Uuid)
5148 {
5149 pUSBDevice = *it;
5150 break;
5151 }
5152 ++it;
5153 }
5154
5155
5156 if (pUSBDevice.isNull())
5157 {
5158 LogFlowThisFunc(("USB device not found.\n"));
5159
5160 /* The VM may be no more operational when this message arrives
5161 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
5162 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
5163 * failure in this case. */
5164
5165 AutoVMCallerQuiet autoVMCaller(this);
5166 if (FAILED(autoVMCaller.rc()))
5167 {
5168 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
5169 mMachineState));
5170 return autoVMCaller.rc();
5171 }
5172
5173 /* the device must be in the list otherwise */
5174 AssertFailedReturn(E_FAIL);
5175 }
5176
5177 if (aError != NULL)
5178 {
5179 /* notify callback about an error */
5180 alock.release();
5181 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, aError);
5182 return S_OK;
5183 }
5184
5185 /* Remove the device from the collection, it is re-added below for failures */
5186 mUSBDevices.erase(it);
5187
5188 alock.release();
5189 HRESULT rc = detachUSBDevice(pUSBDevice);
5190 if (FAILED(rc))
5191 {
5192 /* Re-add the device to the collection */
5193 alock.acquire();
5194 mUSBDevices.push_back(pUSBDevice);
5195 alock.release();
5196 /* take the current error info */
5197 com::ErrorInfoKeeper eik;
5198 /* the error must be a VirtualBoxErrorInfo instance */
5199 ComPtr<IVirtualBoxErrorInfo> pError = eik.takeError();
5200 Assert(!pError.isNull());
5201 if (!pError.isNull())
5202 {
5203 /* notify callbacks about the error */
5204 onUSBDeviceStateChange(pUSBDevice, false /* aAttached */, pError);
5205 }
5206 }
5207
5208 return rc;
5209
5210#else /* !VBOX_WITH_USB */
5211 return E_FAIL;
5212#endif /* !VBOX_WITH_USB */
5213}
5214
5215/**
5216 * Called by IInternalSessionControl::OnBandwidthGroupChange().
5217 *
5218 * @note Locks this object for writing.
5219 */
5220HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
5221{
5222 LogFlowThisFunc(("\n"));
5223
5224 AutoCaller autoCaller(this);
5225 AssertComRCReturnRC(autoCaller.rc());
5226
5227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5228
5229 HRESULT rc = S_OK;
5230
5231 /* don't trigger the CPU priority change if the VM isn't running */
5232 SafeVMPtrQuiet ptrVM(this);
5233 if (ptrVM.isOk())
5234 {
5235 if ( mMachineState == MachineState_Running
5236 || mMachineState == MachineState_Teleporting
5237 || mMachineState == MachineState_LiveSnapshotting
5238 )
5239 {
5240 /* No need to call in the EMT thread. */
5241 LONG64 cMax;
5242 Bstr strName;
5243 BandwidthGroupType_T enmType;
5244 rc = aBandwidthGroup->COMGETTER(Name)(strName.asOutParam());
5245 if (SUCCEEDED(rc))
5246 rc = aBandwidthGroup->COMGETTER(MaxBytesPerSec)(&cMax);
5247 if (SUCCEEDED(rc))
5248 rc = aBandwidthGroup->COMGETTER(Type)(&enmType);
5249
5250 if (SUCCEEDED(rc))
5251 {
5252 int vrc = VINF_SUCCESS;
5253 if (enmType == BandwidthGroupType_Disk)
5254 vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(),
5255 cMax);
5256#ifdef VBOX_WITH_NETSHAPER
5257 else if (enmType == BandwidthGroupType_Network)
5258 vrc = PDMR3NsBwGroupSetLimit(ptrVM, Utf8Str(strName).c_str(),
5259 cMax);
5260 else
5261 rc = E_NOTIMPL;
5262#endif /* VBOX_WITH_NETSHAPER */
5263 AssertRC(vrc);
5264 }
5265 }
5266 else
5267 rc = setInvalidMachineStateError();
5268 ptrVM.release();
5269 }
5270
5271 /* notify console callbacks on success */
5272 if (SUCCEEDED(rc))
5273 {
5274 alock.release();
5275 fireBandwidthGroupChangedEvent(mEventSource, aBandwidthGroup);
5276 }
5277
5278 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5279 return rc;
5280}
5281
5282/**
5283 * Called by IInternalSessionControl::OnStorageDeviceChange().
5284 *
5285 * @note Locks this object for writing.
5286 */
5287HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove)
5288{
5289 LogFlowThisFunc(("\n"));
5290
5291 AutoCaller autoCaller(this);
5292 AssertComRCReturnRC(autoCaller.rc());
5293
5294 HRESULT rc = S_OK;
5295
5296 /* don't trigger medium change if the VM isn't running */
5297 SafeVMPtrQuiet ptrVM(this);
5298 if (ptrVM.isOk())
5299 {
5300 if (aRemove)
5301 rc = doStorageDeviceDetach(aMediumAttachment, ptrVM);
5302 else
5303 rc = doStorageDeviceAttach(aMediumAttachment, ptrVM);
5304 ptrVM.release();
5305 }
5306
5307 /* notify console callbacks on success */
5308 if (SUCCEEDED(rc))
5309 fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove);
5310
5311 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
5312 return rc;
5313}
5314
5315/**
5316 * @note Temporarily locks this object for writing.
5317 */
5318HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
5319 LONG64 *aTimestamp, BSTR *aFlags)
5320{
5321#ifndef VBOX_WITH_GUEST_PROPS
5322 ReturnComNotImplemented();
5323#else /* VBOX_WITH_GUEST_PROPS */
5324 if (!VALID_PTR(aName))
5325 return E_INVALIDARG;
5326 if (!VALID_PTR(aValue))
5327 return E_POINTER;
5328 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
5329 return E_POINTER;
5330 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5331 return E_POINTER;
5332
5333 AutoCaller autoCaller(this);
5334 AssertComRCReturnRC(autoCaller.rc());
5335
5336 /* protect mpVM (if not NULL) */
5337 AutoVMCallerWeak autoVMCaller(this);
5338 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5339
5340 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5341 * autoVMCaller, so there is no need to hold a lock of this */
5342
5343 HRESULT rc = E_UNEXPECTED;
5344 using namespace guestProp;
5345
5346 try
5347 {
5348 VBOXHGCMSVCPARM parm[4];
5349 Utf8Str Utf8Name = aName;
5350 char szBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
5351
5352 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5353 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5354 /* The + 1 is the null terminator */
5355 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5356 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5357 parm[1].u.pointer.addr = szBuffer;
5358 parm[1].u.pointer.size = sizeof(szBuffer);
5359 int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
5360 4, &parm[0]);
5361 /* The returned string should never be able to be greater than our buffer */
5362 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
5363 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
5364 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
5365 {
5366 rc = S_OK;
5367 if (vrc != VERR_NOT_FOUND)
5368 {
5369 Utf8Str strBuffer(szBuffer);
5370 strBuffer.cloneTo(aValue);
5371
5372 if (aTimestamp)
5373 *aTimestamp = parm[2].u.uint64;
5374
5375 if (aFlags)
5376 {
5377 size_t iFlags = strBuffer.length() + 1;
5378 Utf8Str(szBuffer + iFlags).cloneTo(aFlags);
5379 }
5380 }
5381 else
5382 aValue = NULL;
5383 }
5384 else
5385 rc = setError(E_UNEXPECTED,
5386 tr("The service call failed with the error %Rrc"),
5387 vrc);
5388 }
5389 catch(std::bad_alloc & /*e*/)
5390 {
5391 rc = E_OUTOFMEMORY;
5392 }
5393 return rc;
5394#endif /* VBOX_WITH_GUEST_PROPS */
5395}
5396
5397/**
5398 * @note Temporarily locks this object for writing.
5399 */
5400HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
5401{
5402#ifndef VBOX_WITH_GUEST_PROPS
5403 ReturnComNotImplemented();
5404#else /* VBOX_WITH_GUEST_PROPS */
5405 if (!VALID_PTR(aName))
5406 return E_INVALIDARG;
5407 if ((aValue != NULL) && !VALID_PTR(aValue))
5408 return E_INVALIDARG;
5409 if ((aFlags != NULL) && !VALID_PTR(aFlags))
5410 return E_INVALIDARG;
5411
5412 AutoCaller autoCaller(this);
5413 AssertComRCReturnRC(autoCaller.rc());
5414
5415 /* protect mpVM (if not NULL) */
5416 AutoVMCallerWeak autoVMCaller(this);
5417 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5418
5419 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5420 * autoVMCaller, so there is no need to hold a lock of this */
5421
5422 HRESULT rc = E_UNEXPECTED;
5423 using namespace guestProp;
5424
5425 VBOXHGCMSVCPARM parm[3];
5426 Utf8Str Utf8Name = aName;
5427 int vrc = VINF_SUCCESS;
5428
5429 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
5430 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
5431 /* The + 1 is the null terminator */
5432 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
5433 Utf8Str Utf8Value = aValue;
5434 if (aValue != NULL)
5435 {
5436 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
5437 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
5438 /* The + 1 is the null terminator */
5439 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
5440 }
5441 Utf8Str Utf8Flags = aFlags;
5442 if (aFlags != NULL)
5443 {
5444 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
5445 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
5446 /* The + 1 is the null terminator */
5447 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
5448 }
5449 if ((aValue != NULL) && (aFlags != NULL))
5450 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
5451 3, &parm[0]);
5452 else if (aValue != NULL)
5453 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
5454 2, &parm[0]);
5455 else
5456 vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
5457 1, &parm[0]);
5458 if (RT_SUCCESS(vrc))
5459 rc = S_OK;
5460 else
5461 rc = setError(E_UNEXPECTED,
5462 tr("The service call failed with the error %Rrc"),
5463 vrc);
5464 return rc;
5465#endif /* VBOX_WITH_GUEST_PROPS */
5466}
5467
5468
5469/**
5470 * @note Temporarily locks this object for writing.
5471 */
5472HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
5473 ComSafeArrayOut(BSTR, aNames),
5474 ComSafeArrayOut(BSTR, aValues),
5475 ComSafeArrayOut(LONG64, aTimestamps),
5476 ComSafeArrayOut(BSTR, aFlags))
5477{
5478#ifndef VBOX_WITH_GUEST_PROPS
5479 ReturnComNotImplemented();
5480#else /* VBOX_WITH_GUEST_PROPS */
5481 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
5482 return E_POINTER;
5483 if (ComSafeArrayOutIsNull(aNames))
5484 return E_POINTER;
5485 if (ComSafeArrayOutIsNull(aValues))
5486 return E_POINTER;
5487 if (ComSafeArrayOutIsNull(aTimestamps))
5488 return E_POINTER;
5489 if (ComSafeArrayOutIsNull(aFlags))
5490 return E_POINTER;
5491
5492 AutoCaller autoCaller(this);
5493 AssertComRCReturnRC(autoCaller.rc());
5494
5495 /* protect mpVM (if not NULL) */
5496 AutoVMCallerWeak autoVMCaller(this);
5497 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
5498
5499 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
5500 * autoVMCaller, so there is no need to hold a lock of this */
5501
5502 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
5503 ComSafeArrayOutArg(aValues),
5504 ComSafeArrayOutArg(aTimestamps),
5505 ComSafeArrayOutArg(aFlags));
5506#endif /* VBOX_WITH_GUEST_PROPS */
5507}
5508
5509
5510/*
5511 * Internal: helper function for connecting progress reporting
5512 */
5513static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
5514{
5515 HRESULT rc = S_OK;
5516 IProgress *pProgress = static_cast<IProgress *>(pvUser);
5517 if (pProgress)
5518 rc = pProgress->SetCurrentOperationProgress(uPercentage);
5519 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
5520}
5521
5522/**
5523 * @note Temporarily locks this object for writing. bird: And/or reading?
5524 */
5525HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
5526 ULONG aSourceIdx, ULONG aTargetIdx,
5527 IMedium *aSource, IMedium *aTarget,
5528 BOOL aMergeForward,
5529 IMedium *aParentForTarget,
5530 ComSafeArrayIn(IMedium *, aChildrenToReparent),
5531 IProgress *aProgress)
5532{
5533 AutoCaller autoCaller(this);
5534 AssertComRCReturnRC(autoCaller.rc());
5535
5536 HRESULT rc = S_OK;
5537 int vrc = VINF_SUCCESS;
5538
5539 /* Get the VM - must be done before the read-locking. */
5540 SafeVMPtr ptrVM(this);
5541 if (!ptrVM.isOk())
5542 return ptrVM.rc();
5543
5544 /* We will need to release the lock before doing the actual merge */
5545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5546
5547 /* paranoia - we don't want merges to happen while teleporting etc. */
5548 switch (mMachineState)
5549 {
5550 case MachineState_DeletingSnapshotOnline:
5551 case MachineState_DeletingSnapshotPaused:
5552 break;
5553
5554 default:
5555 return setInvalidMachineStateError();
5556 }
5557
5558 /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up
5559 * using uninitialized variables here. */
5560 BOOL fBuiltinIOCache;
5561 rc = mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
5562 AssertComRC(rc);
5563 SafeIfaceArray<IStorageController> ctrls;
5564 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
5565 AssertComRC(rc);
5566 LONG lDev;
5567 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
5568 AssertComRC(rc);
5569 LONG lPort;
5570 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
5571 AssertComRC(rc);
5572 IMedium *pMedium;
5573 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
5574 AssertComRC(rc);
5575 Bstr mediumLocation;
5576 if (pMedium)
5577 {
5578 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
5579 AssertComRC(rc);
5580 }
5581
5582 Bstr attCtrlName;
5583 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
5584 AssertComRC(rc);
5585 ComPtr<IStorageController> pStorageController;
5586 for (size_t i = 0; i < ctrls.size(); ++i)
5587 {
5588 Bstr ctrlName;
5589 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
5590 AssertComRC(rc);
5591 if (attCtrlName == ctrlName)
5592 {
5593 pStorageController = ctrls[i];
5594 break;
5595 }
5596 }
5597 if (pStorageController.isNull())
5598 return setError(E_FAIL,
5599 tr("Could not find storage controller '%ls'"),
5600 attCtrlName.raw());
5601
5602 StorageControllerType_T enmCtrlType;
5603 rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType);
5604 AssertComRC(rc);
5605 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
5606
5607 StorageBus_T enmBus;
5608 rc = pStorageController->COMGETTER(Bus)(&enmBus);
5609 AssertComRC(rc);
5610 ULONG uInstance;
5611 rc = pStorageController->COMGETTER(Instance)(&uInstance);
5612 AssertComRC(rc);
5613 BOOL fUseHostIOCache;
5614 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
5615 AssertComRC(rc);
5616
5617 unsigned uLUN;
5618 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
5619 AssertComRCReturnRC(rc);
5620
5621 alock.release();
5622
5623 /* Pause the VM, as it might have pending IO on this drive */
5624 VMSTATE enmVMState = VMR3GetState(ptrVM);
5625 if (mMachineState == MachineState_DeletingSnapshotOnline)
5626 {
5627 LogFlowFunc(("Suspending the VM...\n"));
5628 /* disable the callback to prevent Console-level state change */
5629 mVMStateChangeCallbackDisabled = true;
5630 int vrc2 = VMR3Suspend(ptrVM);
5631 mVMStateChangeCallbackDisabled = false;
5632 AssertRCReturn(vrc2, E_FAIL);
5633 }
5634
5635 vrc = VMR3ReqCallWait(ptrVM,
5636 VMCPUID_ANY,
5637 (PFNRT)reconfigureMediumAttachment,
5638 13,
5639 this,
5640 ptrVM.raw(),
5641 pcszDevice,
5642 uInstance,
5643 enmBus,
5644 fUseHostIOCache,
5645 fBuiltinIOCache,
5646 true /* fSetupMerge */,
5647 aSourceIdx,
5648 aTargetIdx,
5649 aMediumAttachment,
5650 mMachineState,
5651 &rc);
5652 /* error handling is after resuming the VM */
5653
5654 if (mMachineState == MachineState_DeletingSnapshotOnline)
5655 {
5656 LogFlowFunc(("Resuming the VM...\n"));
5657 /* disable the callback to prevent Console-level state change */
5658 mVMStateChangeCallbackDisabled = true;
5659 int vrc2 = VMR3Resume(ptrVM);
5660 mVMStateChangeCallbackDisabled = false;
5661 if (RT_FAILURE(vrc2))
5662 {
5663 /* too bad, we failed. try to sync the console state with the VMM state */
5664 AssertLogRelRC(vrc2);
5665 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5666 }
5667 }
5668
5669 if (RT_FAILURE(vrc))
5670 return setError(E_FAIL, tr("%Rrc"), vrc);
5671 if (FAILED(rc))
5672 return rc;
5673
5674 PPDMIBASE pIBase = NULL;
5675 PPDMIMEDIA pIMedium = NULL;
5676 vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
5677 if (RT_SUCCESS(vrc))
5678 {
5679 if (pIBase)
5680 {
5681 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
5682 if (!pIMedium)
5683 return setError(E_FAIL, tr("could not query medium interface of controller"));
5684 }
5685 else
5686 return setError(E_FAIL, tr("could not query base interface of controller"));
5687 }
5688
5689 /* Finally trigger the merge. */
5690 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
5691 if (RT_FAILURE(vrc))
5692 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
5693
5694 /* Pause the VM, as it might have pending IO on this drive */
5695 enmVMState = VMR3GetState(ptrVM);
5696 if (mMachineState == MachineState_DeletingSnapshotOnline)
5697 {
5698 LogFlowFunc(("Suspending the VM...\n"));
5699 /* disable the callback to prevent Console-level state change */
5700 mVMStateChangeCallbackDisabled = true;
5701 int vrc2 = VMR3Suspend(ptrVM);
5702 mVMStateChangeCallbackDisabled = false;
5703 AssertRCReturn(vrc2, E_FAIL);
5704 }
5705
5706 /* Update medium chain and state now, so that the VM can continue. */
5707 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
5708 aMergeForward, aParentForTarget,
5709 ComSafeArrayInArg(aChildrenToReparent));
5710
5711 vrc = VMR3ReqCallWait(ptrVM,
5712 VMCPUID_ANY,
5713 (PFNRT)reconfigureMediumAttachment,
5714 13,
5715 this,
5716 ptrVM.raw(),
5717 pcszDevice,
5718 uInstance,
5719 enmBus,
5720 fUseHostIOCache,
5721 fBuiltinIOCache,
5722 false /* fSetupMerge */,
5723 0 /* uMergeSource */,
5724 0 /* uMergeTarget */,
5725 aMediumAttachment,
5726 mMachineState,
5727 &rc);
5728 /* error handling is after resuming the VM */
5729
5730 if (mMachineState == MachineState_DeletingSnapshotOnline)
5731 {
5732 LogFlowFunc(("Resuming the VM...\n"));
5733 /* disable the callback to prevent Console-level state change */
5734 mVMStateChangeCallbackDisabled = true;
5735 int vrc2 = VMR3Resume(ptrVM);
5736 mVMStateChangeCallbackDisabled = false;
5737 AssertRC(vrc2);
5738 if (RT_FAILURE(vrc2))
5739 {
5740 /* too bad, we failed. try to sync the console state with the VMM state */
5741 vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this);
5742 }
5743 }
5744
5745 if (RT_FAILURE(vrc))
5746 return setError(E_FAIL, tr("%Rrc"), vrc);
5747 if (FAILED(rc))
5748 return rc;
5749
5750 return rc;
5751}
5752
5753
5754/**
5755 * Merely passes the call to Guest::enableVMMStatistics().
5756 */
5757void Console::enableVMMStatistics(BOOL aEnable)
5758{
5759 if (mGuest)
5760 mGuest->enableVMMStatistics(aEnable);
5761}
5762
5763/**
5764 * Gets called by Session::UpdateMachineState()
5765 * (IInternalSessionControl::updateMachineState()).
5766 *
5767 * Must be called only in certain cases (see the implementation).
5768 *
5769 * @note Locks this object for writing.
5770 */
5771HRESULT Console::updateMachineState(MachineState_T aMachineState)
5772{
5773 AutoCaller autoCaller(this);
5774 AssertComRCReturnRC(autoCaller.rc());
5775
5776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5777
5778 AssertReturn( mMachineState == MachineState_Saving
5779 || mMachineState == MachineState_LiveSnapshotting
5780 || mMachineState == MachineState_RestoringSnapshot
5781 || mMachineState == MachineState_DeletingSnapshot
5782 || mMachineState == MachineState_DeletingSnapshotOnline
5783 || mMachineState == MachineState_DeletingSnapshotPaused
5784 , E_FAIL);
5785
5786 return setMachineStateLocally(aMachineState);
5787}
5788
5789#ifdef CONSOLE_WITH_EVENT_CACHE
5790/**
5791 * @note Locks this object for writing.
5792 */
5793#endif
5794void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
5795 uint32_t xHot, uint32_t yHot,
5796 uint32_t width, uint32_t height,
5797 ComSafeArrayIn(BYTE,pShape))
5798{
5799#if 0
5800 LogFlowThisFuncEnter();
5801 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
5802 fVisible, fAlpha, xHot, yHot, width, height, pShape));
5803#endif
5804
5805 AutoCaller autoCaller(this);
5806 AssertComRCReturnVoid(autoCaller.rc());
5807
5808#ifdef CONSOLE_WITH_EVENT_CACHE
5809 {
5810 /* We need a write lock because we alter the cached callback data */
5811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5812
5813 /* Save the callback arguments */
5814 mCallbackData.mpsc.visible = fVisible;
5815 mCallbackData.mpsc.alpha = fAlpha;
5816 mCallbackData.mpsc.xHot = xHot;
5817 mCallbackData.mpsc.yHot = yHot;
5818 mCallbackData.mpsc.width = width;
5819 mCallbackData.mpsc.height = height;
5820
5821 /* start with not valid */
5822 bool wasValid = mCallbackData.mpsc.valid;
5823 mCallbackData.mpsc.valid = false;
5824
5825 com::SafeArray<BYTE> aShape(ComSafeArrayInArg(pShape));
5826 if (aShape.size() != 0)
5827 mCallbackData.mpsc.shape.initFrom(aShape);
5828 else
5829 mCallbackData.mpsc.shape.resize(0);
5830 mCallbackData.mpsc.valid = true;
5831 }
5832#endif
5833
5834 fireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayInArg(pShape));
5835
5836#if 0
5837 LogFlowThisFuncLeave();
5838#endif
5839}
5840
5841#ifdef CONSOLE_WITH_EVENT_CACHE
5842/**
5843 * @note Locks this object for writing.
5844 */
5845#endif
5846void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
5847{
5848 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
5849 supportsAbsolute, supportsRelative, needsHostCursor));
5850
5851 AutoCaller autoCaller(this);
5852 AssertComRCReturnVoid(autoCaller.rc());
5853
5854#ifdef CONSOLE_WITH_EVENT_CACHE
5855 {
5856 /* We need a write lock because we alter the cached callback data */
5857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5858
5859 /* save the callback arguments */
5860 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
5861 mCallbackData.mcc.supportsRelative = supportsRelative;
5862 mCallbackData.mcc.needsHostCursor = needsHostCursor;
5863 mCallbackData.mcc.valid = true;
5864 }
5865#endif
5866
5867 fireMouseCapabilityChangedEvent(mEventSource, supportsAbsolute, supportsRelative, needsHostCursor);
5868}
5869
5870void Console::onStateChange(MachineState_T machineState)
5871{
5872 AutoCaller autoCaller(this);
5873 AssertComRCReturnVoid(autoCaller.rc());
5874
5875 fireStateChangedEvent(mEventSource, machineState);
5876}
5877
5878void Console::onAdditionsStateChange()
5879{
5880 AutoCaller autoCaller(this);
5881 AssertComRCReturnVoid(autoCaller.rc());
5882
5883 fireAdditionsStateChangedEvent(mEventSource);
5884}
5885
5886/**
5887 * @remarks This notification only is for reporting an incompatible
5888 * Guest Additions interface, *not* the Guest Additions version!
5889 *
5890 * The user will be notified inside the guest if new Guest
5891 * Additions are available (via VBoxTray/VBoxClient).
5892 */
5893void Console::onAdditionsOutdated()
5894{
5895 AutoCaller autoCaller(this);
5896 AssertComRCReturnVoid(autoCaller.rc());
5897
5898 /** @todo implement this */
5899}
5900
5901#ifdef CONSOLE_WITH_EVENT_CACHE
5902/**
5903 * @note Locks this object for writing.
5904 */
5905#endif
5906void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
5907{
5908 AutoCaller autoCaller(this);
5909 AssertComRCReturnVoid(autoCaller.rc());
5910
5911#ifdef CONSOLE_WITH_EVENT_CACHE
5912 {
5913 /* We need a write lock because we alter the cached callback data */
5914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5915
5916 /* save the callback arguments */
5917 mCallbackData.klc.numLock = fNumLock;
5918 mCallbackData.klc.capsLock = fCapsLock;
5919 mCallbackData.klc.scrollLock = fScrollLock;
5920 mCallbackData.klc.valid = true;
5921 }
5922#endif
5923
5924 fireKeyboardLedsChangedEvent(mEventSource, fNumLock, fCapsLock, fScrollLock);
5925}
5926
5927void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
5928 IVirtualBoxErrorInfo *aError)
5929{
5930 AutoCaller autoCaller(this);
5931 AssertComRCReturnVoid(autoCaller.rc());
5932
5933 fireUSBDeviceStateChangedEvent(mEventSource, aDevice, aAttached, aError);
5934}
5935
5936void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
5937{
5938 AutoCaller autoCaller(this);
5939 AssertComRCReturnVoid(autoCaller.rc());
5940
5941 fireRuntimeErrorEvent(mEventSource, aFatal, aErrorID, aMessage);
5942}
5943
5944HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
5945{
5946 AssertReturn(aCanShow, E_POINTER);
5947 AssertReturn(aWinId, E_POINTER);
5948
5949 *aCanShow = FALSE;
5950 *aWinId = 0;
5951
5952 AutoCaller autoCaller(this);
5953 AssertComRCReturnRC(autoCaller.rc());
5954
5955 VBoxEventDesc evDesc;
5956 if (aCheck)
5957 {
5958 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
5959 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5960 //Assert(fDelivered);
5961 if (fDelivered)
5962 {
5963 ComPtr<IEvent> pEvent;
5964 evDesc.getEvent(pEvent.asOutParam());
5965 // bit clumsy
5966 ComPtr<ICanShowWindowEvent> pCanShowEvent = pEvent;
5967 if (pCanShowEvent)
5968 {
5969 BOOL fVetoed = FALSE;
5970 pCanShowEvent->IsVetoed(&fVetoed);
5971 *aCanShow = !fVetoed;
5972 }
5973 else
5974 {
5975 AssertFailed();
5976 *aCanShow = TRUE;
5977 }
5978 }
5979 else
5980 *aCanShow = TRUE;
5981 }
5982 else
5983 {
5984 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
5985 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5986 //Assert(fDelivered);
5987 if (fDelivered)
5988 {
5989 ComPtr<IEvent> pEvent;
5990 evDesc.getEvent(pEvent.asOutParam());
5991 ComPtr<IShowWindowEvent> pShowEvent = pEvent;
5992 if (pShowEvent)
5993 {
5994 LONG64 iEvWinId = 0;
5995 pShowEvent->COMGETTER(WinId)(&iEvWinId);
5996 if (iEvWinId != 0 && *aWinId == 0)
5997 *aWinId = iEvWinId;
5998 }
5999 else
6000 AssertFailed();
6001 }
6002 }
6003
6004 return S_OK;
6005}
6006
6007// private methods
6008////////////////////////////////////////////////////////////////////////////////
6009
6010/**
6011 * Increases the usage counter of the mpVM pointer. Guarantees that
6012 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
6013 * is called.
6014 *
6015 * If this method returns a failure, the caller is not allowed to use mpVM
6016 * and may return the failed result code to the upper level. This method sets
6017 * the extended error info on failure if \a aQuiet is false.
6018 *
6019 * Setting \a aQuiet to true is useful for methods that don't want to return
6020 * the failed result code to the caller when this method fails (e.g. need to
6021 * silently check for the mpVM availability).
6022 *
6023 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
6024 * returned instead of asserting. Having it false is intended as a sanity check
6025 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
6026 *
6027 * @param aQuiet true to suppress setting error info
6028 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
6029 * (otherwise this method will assert if mpVM is NULL)
6030 *
6031 * @note Locks this object for writing.
6032 */
6033HRESULT Console::addVMCaller(bool aQuiet /* = false */,
6034 bool aAllowNullVM /* = false */)
6035{
6036 AutoCaller autoCaller(this);
6037 AssertComRCReturnRC(autoCaller.rc());
6038
6039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6040
6041 if (mVMDestroying)
6042 {
6043 /* powerDown() is waiting for all callers to finish */
6044 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6045 tr("The virtual machine is being powered down"));
6046 }
6047
6048 if (mpUVM == NULL)
6049 {
6050 Assert(aAllowNullVM == true);
6051
6052 /* The machine is not powered up */
6053 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
6054 tr("The virtual machine is not powered up"));
6055 }
6056
6057 ++mVMCallers;
6058
6059 return S_OK;
6060}
6061
6062/**
6063 * Decreases the usage counter of the mpVM pointer. Must always complete
6064 * the addVMCaller() call after the mpVM pointer is no more necessary.
6065 *
6066 * @note Locks this object for writing.
6067 */
6068void Console::releaseVMCaller()
6069{
6070 AutoCaller autoCaller(this);
6071 AssertComRCReturnVoid(autoCaller.rc());
6072
6073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6074
6075 AssertReturnVoid(mpUVM != NULL);
6076
6077 Assert(mVMCallers > 0);
6078 --mVMCallers;
6079
6080 if (mVMCallers == 0 && mVMDestroying)
6081 {
6082 /* inform powerDown() there are no more callers */
6083 RTSemEventSignal(mVMZeroCallersSem);
6084 }
6085}
6086
6087
6088HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet)
6089{
6090 *a_ppVM = NULL;
6091 *a_ppUVM = NULL;
6092
6093 AutoCaller autoCaller(this);
6094 AssertComRCReturnRC(autoCaller.rc());
6095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6096
6097 /*
6098 * Repeat the checks done by addVMCaller.
6099 */
6100 if (mVMDestroying) /* powerDown() is waiting for all callers to finish */
6101 return a_Quiet
6102 ? E_ACCESSDENIED
6103 : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down"));
6104 PUVM pUVM = mpUVM;
6105 if (!pUVM)
6106 return a_Quiet
6107 ? E_ACCESSDENIED
6108 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
6109
6110 /*
6111 * Retain a reference to the user mode VM handle and get the global handle.
6112 */
6113 uint32_t cRefs = VMR3RetainUVM(pUVM);
6114 if (cRefs == UINT32_MAX)
6115 return a_Quiet
6116 ? E_ACCESSDENIED
6117 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
6118
6119 PVM pVM = VMR3GetVM(pUVM);
6120 if (!pVM)
6121 {
6122 VMR3ReleaseUVM(pUVM);
6123 return a_Quiet
6124 ? E_ACCESSDENIED
6125 : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off"));
6126 }
6127
6128 /* done */
6129 *a_ppVM = pVM;
6130 *a_ppUVM = pUVM;
6131 return S_OK;
6132}
6133
6134void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM)
6135{
6136 if (*a_ppVM && *a_ppUVM)
6137 VMR3ReleaseUVM(*a_ppUVM);
6138 *a_ppVM = NULL;
6139 *a_ppUVM = NULL;
6140}
6141
6142
6143/**
6144 * Initialize the release logging facility. In case something
6145 * goes wrong, there will be no release logging. Maybe in the future
6146 * we can add some logic to use different file names in this case.
6147 * Note that the logic must be in sync with Machine::DeleteSettings().
6148 */
6149HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
6150{
6151 HRESULT hrc = S_OK;
6152
6153 Bstr logFolder;
6154 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
6155 if (FAILED(hrc))
6156 return hrc;
6157
6158 Utf8Str logDir = logFolder;
6159
6160 /* make sure the Logs folder exists */
6161 Assert(logDir.length());
6162 if (!RTDirExists(logDir.c_str()))
6163 RTDirCreateFullPath(logDir.c_str(), 0700);
6164
6165 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
6166 logDir.c_str(), RTPATH_DELIMITER);
6167 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
6168 logDir.c_str(), RTPATH_DELIMITER);
6169
6170 /*
6171 * Age the old log files
6172 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
6173 * Overwrite target files in case they exist.
6174 */
6175 ComPtr<IVirtualBox> pVirtualBox;
6176 aMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6177 ComPtr<ISystemProperties> pSystemProperties;
6178 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
6179 ULONG cHistoryFiles = 3;
6180 pSystemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
6181 if (cHistoryFiles)
6182 {
6183 for (int i = cHistoryFiles-1; i >= 0; i--)
6184 {
6185 Utf8Str *files[] = { &logFile, &pngFile };
6186 Utf8Str oldName, newName;
6187
6188 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++j)
6189 {
6190 if (i > 0)
6191 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
6192 else
6193 oldName = *files[j];
6194 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
6195 /* If the old file doesn't exist, delete the new file (if it
6196 * exists) to provide correct rotation even if the sequence is
6197 * broken */
6198 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
6199 == VERR_FILE_NOT_FOUND)
6200 RTFileDelete(newName.c_str());
6201 }
6202 }
6203 }
6204
6205 char szError[RTPATH_MAX + 128];
6206 int vrc = com::VBoxLogRelCreate("VM", logFile.c_str(),
6207 RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
6208 "all all.restrict default.unrestricted",
6209 "VBOX_RELEASE_LOG", RTLOGDEST_FILE,
6210 32768 /* cMaxEntriesPerGroup */,
6211 0 /* cHistory */, 0 /* uHistoryFileTime */,
6212 0 /* uHistoryFileSize */, szError, sizeof(szError));
6213 if (RT_FAILURE(vrc))
6214 hrc = setError(E_FAIL, tr("Failed to open release log (%s, %Rrc)"),
6215 szError, vrc);
6216
6217 /* If we've made any directory changes, flush the directory to increase
6218 the likelihood that the log file will be usable after a system panic.
6219
6220 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
6221 is missing. Just don't have too high hopes for this to help. */
6222 if (SUCCEEDED(hrc) || cHistoryFiles)
6223 RTDirFlush(logDir.c_str());
6224
6225 return hrc;
6226}
6227
6228/**
6229 * Common worker for PowerUp and PowerUpPaused.
6230 *
6231 * @returns COM status code.
6232 *
6233 * @param aProgress Where to return the progress object.
6234 * @param aPaused true if PowerUpPaused called.
6235 */
6236HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
6237{
6238 LogFlowThisFuncEnter();
6239 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
6240
6241 CheckComArgOutPointerValid(aProgress);
6242
6243 AutoCaller autoCaller(this);
6244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6245
6246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 HRESULT rc = S_OK;
6249 ComObjPtr<Progress> pPowerupProgress;
6250 bool fBeganPoweringUp = false;
6251
6252 try
6253 {
6254 if (Global::IsOnlineOrTransient(mMachineState))
6255 throw setError(VBOX_E_INVALID_VM_STATE,
6256 tr("The virtual machine is already running or busy (machine state: %s)"),
6257 Global::stringifyMachineState(mMachineState));
6258
6259 /* test and clear the TeleporterEnabled property */
6260 BOOL fTeleporterEnabled;
6261 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
6262 if (FAILED(rc))
6263 throw rc;
6264#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
6265 if (fTeleporterEnabled)
6266 {
6267 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
6268 if (FAILED(rc))
6269 throw rc;
6270 }
6271#endif
6272
6273 /* test the FaultToleranceState property */
6274 FaultToleranceState_T enmFaultToleranceState;
6275 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
6276 if (FAILED(rc))
6277 throw rc;
6278 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
6279
6280 /* Create a progress object to track progress of this operation. Must
6281 * be done as early as possible (together with BeginPowerUp()) as this
6282 * is vital for communicating as much as possible early powerup
6283 * failure information to the API caller */
6284 pPowerupProgress.createObject();
6285 Bstr progressDesc;
6286 if (mMachineState == MachineState_Saved)
6287 progressDesc = tr("Restoring virtual machine");
6288 else if (fTeleporterEnabled)
6289 progressDesc = tr("Teleporting virtual machine");
6290 else if (fFaultToleranceSyncEnabled)
6291 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
6292 else
6293 progressDesc = tr("Starting virtual machine");
6294 if ( mMachineState == MachineState_Saved
6295 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
6296 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6297 progressDesc.raw(),
6298 FALSE /* aCancelable */);
6299 else
6300 if (fTeleporterEnabled)
6301 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6302 progressDesc.raw(),
6303 TRUE /* aCancelable */,
6304 3 /* cOperations */,
6305 10 /* ulTotalOperationsWeight */,
6306 Bstr(tr("Teleporting virtual machine")).raw(),
6307 1 /* ulFirstOperationWeight */,
6308 NULL);
6309 else
6310 if (fFaultToleranceSyncEnabled)
6311 rc = pPowerupProgress->init(static_cast<IConsole *>(this),
6312 progressDesc.raw(),
6313 TRUE /* aCancelable */,
6314 3 /* cOperations */,
6315 10 /* ulTotalOperationsWeight */,
6316 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
6317 1 /* ulFirstOperationWeight */,
6318 NULL);
6319
6320 if (FAILED(rc))
6321 throw rc;
6322
6323 /* Tell VBoxSVC and Machine about the progress object so they can
6324 combine/proxy it to any openRemoteSession caller. */
6325 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
6326 rc = mControl->BeginPowerUp(pPowerupProgress);
6327 if (FAILED(rc))
6328 {
6329 LogFlowThisFunc(("BeginPowerUp failed\n"));
6330 throw rc;
6331 }
6332 fBeganPoweringUp = true;
6333
6334 /** @todo this code prevents starting a VM with unavailable bridged
6335 * networking interface. The only benefit is a slightly better error
6336 * message, which should be moved to the driver code. This is the
6337 * only reason why I left the code in for now. The driver allows
6338 * unavailable bridged networking interfaces in certain circumstances,
6339 * and this is sabotaged by this check. The VM will initially have no
6340 * network connectivity, but the user can fix this at runtime. */
6341#if 0
6342 /* the network cards will undergo a quick consistency check */
6343 for (ULONG slot = 0;
6344 slot < maxNetworkAdapters;
6345 ++slot)
6346 {
6347 ComPtr<INetworkAdapter> pNetworkAdapter;
6348 mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
6349 BOOL enabled = FALSE;
6350 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
6351 if (!enabled)
6352 continue;
6353
6354 NetworkAttachmentType_T netattach;
6355 pNetworkAdapter->COMGETTER(AttachmentType)(&netattach);
6356 switch (netattach)
6357 {
6358 case NetworkAttachmentType_Bridged:
6359 {
6360 /* a valid host interface must have been set */
6361 Bstr hostif;
6362 pNetworkAdapter->COMGETTER(HostInterface)(hostif.asOutParam());
6363 if (hostif.isEmpty())
6364 {
6365 throw setError(VBOX_E_HOST_ERROR,
6366 tr("VM cannot start because host interface networking requires a host interface name to be set"));
6367 }
6368 ComPtr<IVirtualBox> pVirtualBox;
6369 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
6370 ComPtr<IHost> pHost;
6371 pVirtualBox->COMGETTER(Host)(pHost.asOutParam());
6372 ComPtr<IHostNetworkInterface> pHostInterface;
6373 if (!SUCCEEDED(pHost->FindHostNetworkInterfaceByName(hostif.raw(),
6374 pHostInterface.asOutParam())))
6375 {
6376 throw setError(VBOX_E_HOST_ERROR,
6377 tr("VM cannot start because the host interface '%ls' does not exist"),
6378 hostif.raw());
6379 }
6380 break;
6381 }
6382 default:
6383 break;
6384 }
6385 }
6386#endif // 0
6387
6388 /* Read console data stored in the saved state file (if not yet done) */
6389 rc = loadDataFromSavedState();
6390 if (FAILED(rc))
6391 throw rc;
6392
6393 /* Check all types of shared folders and compose a single list */
6394 SharedFolderDataMap sharedFolders;
6395 {
6396 /* first, insert global folders */
6397 for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin();
6398 it != m_mapGlobalSharedFolders.end();
6399 ++it)
6400 {
6401 const SharedFolderData &d = it->second;
6402 sharedFolders[it->first] = d;
6403 }
6404
6405 /* second, insert machine folders */
6406 for (SharedFolderDataMap::const_iterator it = m_mapMachineSharedFolders.begin();
6407 it != m_mapMachineSharedFolders.end();
6408 ++it)
6409 {
6410 const SharedFolderData &d = it->second;
6411 sharedFolders[it->first] = d;
6412 }
6413
6414 /* third, insert console folders */
6415 for (SharedFolderMap::const_iterator it = m_mapSharedFolders.begin();
6416 it != m_mapSharedFolders.end();
6417 ++it)
6418 {
6419 SharedFolder *pSF = it->second;
6420 AutoCaller sfCaller(pSF);
6421 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
6422 sharedFolders[it->first] = SharedFolderData(pSF->getHostPath(),
6423 pSF->isWritable(),
6424 pSF->isAutoMounted());
6425 }
6426 }
6427
6428 Bstr savedStateFile;
6429
6430 /*
6431 * Saved VMs will have to prove that their saved states seem kosher.
6432 */
6433 if (mMachineState == MachineState_Saved)
6434 {
6435 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
6436 if (FAILED(rc))
6437 throw rc;
6438 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
6439 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
6440 if (RT_FAILURE(vrc))
6441 throw setError(VBOX_E_FILE_ERROR,
6442 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
6443 savedStateFile.raw(), vrc);
6444 }
6445
6446 LogFlowThisFunc(("Checking if canceled...\n"));
6447 BOOL fCanceled;
6448 rc = pPowerupProgress->COMGETTER(Canceled)(&fCanceled);
6449 if (FAILED(rc))
6450 throw rc;
6451 if (fCanceled)
6452 {
6453 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
6454 throw setError(E_FAIL, tr("Powerup was canceled"));
6455 }
6456 LogFlowThisFunc(("Not canceled yet.\n"));
6457
6458 /* setup task object and thread to carry out the operation
6459 * asynchronously */
6460
6461 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
6462 ComAssertComRCRetRC(task->rc());
6463
6464 task->mConfigConstructor = configConstructor;
6465 task->mSharedFolders = sharedFolders;
6466 task->mStartPaused = aPaused;
6467 if (mMachineState == MachineState_Saved)
6468 task->mSavedStateFile = savedStateFile;
6469 task->mTeleporterEnabled = fTeleporterEnabled;
6470 task->mEnmFaultToleranceState = enmFaultToleranceState;
6471
6472 /* Reset differencing hard disks for which autoReset is true,
6473 * but only if the machine has no snapshots OR the current snapshot
6474 * is an OFFLINE snapshot; otherwise we would reset the current
6475 * differencing image of an ONLINE snapshot which contains the disk
6476 * state of the machine while it was previously running, but without
6477 * the corresponding machine state, which is equivalent to powering
6478 * off a running machine and not good idea
6479 */
6480 ComPtr<ISnapshot> pCurrentSnapshot;
6481 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
6482 if (FAILED(rc))
6483 throw rc;
6484
6485 BOOL fCurrentSnapshotIsOnline = false;
6486 if (pCurrentSnapshot)
6487 {
6488 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
6489 if (FAILED(rc))
6490 throw rc;
6491 }
6492
6493 if (!fCurrentSnapshotIsOnline)
6494 {
6495 LogFlowThisFunc(("Looking for immutable images to reset\n"));
6496
6497 com::SafeIfaceArray<IMediumAttachment> atts;
6498 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
6499 if (FAILED(rc))
6500 throw rc;
6501
6502 for (size_t i = 0;
6503 i < atts.size();
6504 ++i)
6505 {
6506 DeviceType_T devType;
6507 rc = atts[i]->COMGETTER(Type)(&devType);
6508 /** @todo later applies to floppies as well */
6509 if (devType == DeviceType_HardDisk)
6510 {
6511 ComPtr<IMedium> pMedium;
6512 rc = atts[i]->COMGETTER(Medium)(pMedium.asOutParam());
6513 if (FAILED(rc))
6514 throw rc;
6515
6516 /* needs autoreset? */
6517 BOOL autoReset = FALSE;
6518 rc = pMedium->COMGETTER(AutoReset)(&autoReset);
6519 if (FAILED(rc))
6520 throw rc;
6521
6522 if (autoReset)
6523 {
6524 ComPtr<IProgress> pResetProgress;
6525 rc = pMedium->Reset(pResetProgress.asOutParam());
6526 if (FAILED(rc))
6527 throw rc;
6528
6529 /* save for later use on the powerup thread */
6530 task->hardDiskProgresses.push_back(pResetProgress);
6531 }
6532 }
6533 }
6534 }
6535 else
6536 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
6537
6538 rc = consoleInitReleaseLog(mMachine);
6539 if (FAILED(rc))
6540 throw rc;
6541#ifdef VBOX_WITH_EXTPACK
6542 mptrExtPackManager->dumpAllToReleaseLog();
6543#endif
6544
6545#ifdef RT_OS_SOLARIS
6546 /* setup host core dumper for the VM */
6547 Bstr value;
6548 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
6549 if (SUCCEEDED(hrc) && value == "1")
6550 {
6551 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
6552 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
6553 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
6554 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
6555
6556 uint32_t fCoreFlags = 0;
6557 if ( coreDumpReplaceSys.isEmpty() == false
6558 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
6559 {
6560 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
6561 }
6562
6563 if ( coreDumpLive.isEmpty() == false
6564 && Utf8Str(coreDumpLive).toUInt32() == 1)
6565 {
6566 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
6567 }
6568
6569 Utf8Str strDumpDir(coreDumpDir);
6570 const char *pszDumpDir = strDumpDir.c_str();
6571 if ( pszDumpDir
6572 && *pszDumpDir == '\0')
6573 pszDumpDir = NULL;
6574
6575 int vrc;
6576 if ( pszDumpDir
6577 && !RTDirExists(pszDumpDir))
6578 {
6579 /*
6580 * Try create the directory.
6581 */
6582 vrc = RTDirCreateFullPath(pszDumpDir, 0700);
6583 if (RT_FAILURE(vrc))
6584 throw setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
6585 }
6586
6587 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
6588 if (RT_FAILURE(vrc))
6589 throw setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
6590 else
6591 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
6592 }
6593#endif
6594
6595 /* pass the progress object to the caller if requested */
6596 if (aProgress)
6597 {
6598 if (task->hardDiskProgresses.size() == 0)
6599 {
6600 /* there are no other operations to track, return the powerup
6601 * progress only */
6602 pPowerupProgress.queryInterfaceTo(aProgress);
6603 }
6604 else
6605 {
6606 /* create a combined progress object */
6607 ComObjPtr<CombinedProgress> pProgress;
6608 pProgress.createObject();
6609 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
6610 progresses.push_back(ComPtr<IProgress> (pPowerupProgress));
6611 rc = pProgress->init(static_cast<IConsole *>(this),
6612 progressDesc.raw(), progresses.begin(),
6613 progresses.end());
6614 AssertComRCReturnRC(rc);
6615 pProgress.queryInterfaceTo(aProgress);
6616 }
6617 }
6618
6619 int vrc = RTThreadCreate(NULL, Console::powerUpThread,
6620 (void *)task.get(), 0,
6621 RTTHREADTYPE_MAIN_WORKER, 0, "VMPwrUp");
6622 if (RT_FAILURE(vrc))
6623 throw setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
6624
6625 /* task is now owned by powerUpThread(), so release it */
6626 task.release();
6627
6628 /* finally, set the state: no right to fail in this method afterwards
6629 * since we've already started the thread and it is now responsible for
6630 * any error reporting and appropriate state change! */
6631 if (mMachineState == MachineState_Saved)
6632 setMachineState(MachineState_Restoring);
6633 else if (fTeleporterEnabled)
6634 setMachineState(MachineState_TeleportingIn);
6635 else if (enmFaultToleranceState == FaultToleranceState_Standby)
6636 setMachineState(MachineState_FaultTolerantSyncing);
6637 else
6638 setMachineState(MachineState_Starting);
6639 }
6640 catch (HRESULT aRC) { rc = aRC; }
6641
6642 if (FAILED(rc) && fBeganPoweringUp)
6643 {
6644
6645 /* The progress object will fetch the current error info */
6646 if (!pPowerupProgress.isNull())
6647 pPowerupProgress->notifyComplete(rc);
6648
6649 /* Save the error info across the IPC below. Can't be done before the
6650 * progress notification above, as saving the error info deletes it
6651 * from the current context, and thus the progress object wouldn't be
6652 * updated correctly. */
6653 ErrorInfoKeeper eik;
6654
6655 /* signal end of operation */
6656 mControl->EndPowerUp(rc);
6657 }
6658
6659 LogFlowThisFunc(("mMachineState=%d, rc=%Rhrc\n", mMachineState, rc));
6660 LogFlowThisFuncLeave();
6661 return rc;
6662}
6663
6664/**
6665 * Internal power off worker routine.
6666 *
6667 * This method may be called only at certain places with the following meaning
6668 * as shown below:
6669 *
6670 * - if the machine state is either Running or Paused, a normal
6671 * Console-initiated powerdown takes place (e.g. PowerDown());
6672 * - if the machine state is Saving, saveStateThread() has successfully done its
6673 * job;
6674 * - if the machine state is Starting or Restoring, powerUpThread() has failed
6675 * to start/load the VM;
6676 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
6677 * as a result of the powerDown() call).
6678 *
6679 * Calling it in situations other than the above will cause unexpected behavior.
6680 *
6681 * Note that this method should be the only one that destroys mpVM and sets it
6682 * to NULL.
6683 *
6684 * @param aProgress Progress object to run (may be NULL).
6685 *
6686 * @note Locks this object for writing.
6687 *
6688 * @note Never call this method from a thread that called addVMCaller() or
6689 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
6690 * release(). Otherwise it will deadlock.
6691 */
6692HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/)
6693{
6694 LogFlowThisFuncEnter();
6695
6696 AutoCaller autoCaller(this);
6697 AssertComRCReturnRC(autoCaller.rc());
6698
6699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6700
6701 /* Total # of steps for the progress object. Must correspond to the
6702 * number of "advance percent count" comments in this method! */
6703 enum { StepCount = 7 };
6704 /* current step */
6705 ULONG step = 0;
6706
6707 HRESULT rc = S_OK;
6708 int vrc = VINF_SUCCESS;
6709
6710 /* sanity */
6711 Assert(mVMDestroying == false);
6712
6713 PUVM pUVM = mpUVM; Assert(pUVM != NULL);
6714 uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX);
6715
6716 AssertMsg( mMachineState == MachineState_Running
6717 || mMachineState == MachineState_Paused
6718 || mMachineState == MachineState_Stuck
6719 || mMachineState == MachineState_Starting
6720 || mMachineState == MachineState_Stopping
6721 || mMachineState == MachineState_Saving
6722 || mMachineState == MachineState_Restoring
6723 || mMachineState == MachineState_TeleportingPausedVM
6724 || mMachineState == MachineState_FaultTolerantSyncing
6725 || mMachineState == MachineState_TeleportingIn
6726 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
6727
6728 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
6729 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
6730
6731 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
6732 * VM has already powered itself off in vmstateChangeCallback() and is just
6733 * notifying Console about that. In case of Starting or Restoring,
6734 * powerUpThread() is calling us on failure, so the VM is already off at
6735 * that point. */
6736 if ( !mVMPoweredOff
6737 && ( mMachineState == MachineState_Starting
6738 || mMachineState == MachineState_Restoring
6739 || mMachineState == MachineState_FaultTolerantSyncing
6740 || mMachineState == MachineState_TeleportingIn)
6741 )
6742 mVMPoweredOff = true;
6743
6744 /*
6745 * Go to Stopping state if not already there.
6746 *
6747 * Note that we don't go from Saving/Restoring to Stopping because
6748 * vmstateChangeCallback() needs it to set the state to Saved on
6749 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
6750 * while leaving the lock below, Saving or Restoring should be fine too.
6751 * Ditto for TeleportingPausedVM -> Teleported.
6752 */
6753 if ( mMachineState != MachineState_Saving
6754 && mMachineState != MachineState_Restoring
6755 && mMachineState != MachineState_Stopping
6756 && mMachineState != MachineState_TeleportingIn
6757 && mMachineState != MachineState_TeleportingPausedVM
6758 && mMachineState != MachineState_FaultTolerantSyncing
6759 )
6760 setMachineState(MachineState_Stopping);
6761
6762 /* ----------------------------------------------------------------------
6763 * DONE with necessary state changes, perform the power down actions (it's
6764 * safe to release the object lock now if needed)
6765 * ---------------------------------------------------------------------- */
6766
6767 /* Stop the VRDP server to prevent new clients connection while VM is being
6768 * powered off. */
6769 if (mConsoleVRDPServer)
6770 {
6771 LogFlowThisFunc(("Stopping VRDP server...\n"));
6772
6773 /* Leave the lock since EMT will call us back as addVMCaller()
6774 * in updateDisplayData(). */
6775 alock.release();
6776
6777 mConsoleVRDPServer->Stop();
6778
6779 alock.acquire();
6780 }
6781
6782 /* advance percent count */
6783 if (aProgress)
6784 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6785
6786
6787 /* ----------------------------------------------------------------------
6788 * Now, wait for all mpVM callers to finish their work if there are still
6789 * some on other threads. NO methods that need mpVM (or initiate other calls
6790 * that need it) may be called after this point
6791 * ---------------------------------------------------------------------- */
6792
6793 /* go to the destroying state to prevent from adding new callers */
6794 mVMDestroying = true;
6795
6796 if (mVMCallers > 0)
6797 {
6798 /* lazy creation */
6799 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
6800 RTSemEventCreate(&mVMZeroCallersSem);
6801
6802 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
6803 mVMCallers));
6804
6805 alock.release();
6806
6807 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
6808
6809 alock.acquire();
6810 }
6811
6812 /* advance percent count */
6813 if (aProgress)
6814 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6815
6816 vrc = VINF_SUCCESS;
6817
6818 /*
6819 * Power off the VM if not already done that.
6820 * Leave the lock since EMT will call vmstateChangeCallback.
6821 *
6822 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
6823 * VM-(guest-)initiated power off happened in parallel a ms before this
6824 * call. So far, we let this error pop up on the user's side.
6825 */
6826 if (!mVMPoweredOff)
6827 {
6828 LogFlowThisFunc(("Powering off the VM...\n"));
6829 alock.release();
6830 vrc = VMR3PowerOff(VMR3GetVM(pUVM));
6831#ifdef VBOX_WITH_EXTPACK
6832 mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM));
6833#endif
6834 alock.acquire();
6835 }
6836
6837 /* advance percent count */
6838 if (aProgress)
6839 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount );
6840
6841#ifdef VBOX_WITH_HGCM
6842 /* Shutdown HGCM services before destroying the VM. */
6843 if (m_pVMMDev)
6844 {
6845 LogFlowThisFunc(("Shutdown HGCM...\n"));
6846
6847 /* Leave the lock since EMT will call us back as addVMCaller() */
6848 alock.release();
6849
6850 m_pVMMDev->hgcmShutdown();
6851
6852 alock.acquire();
6853 }
6854
6855 /* advance percent count */
6856 if (aProgress)
6857 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6858
6859#endif /* VBOX_WITH_HGCM */
6860
6861 LogFlowThisFunc(("Ready for VM destruction.\n"));
6862
6863 /* If we are called from Console::uninit(), then try to destroy the VM even
6864 * on failure (this will most likely fail too, but what to do?..) */
6865 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
6866 {
6867 /* If the machine has an USB controller, release all USB devices
6868 * (symmetric to the code in captureUSBDevices()) */
6869 bool fHasUSBController = false;
6870 {
6871 PPDMIBASE pBase;
6872 vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase);
6873 if (RT_SUCCESS(vrc))
6874 {
6875 fHasUSBController = true;
6876 alock.release();
6877 detachAllUSBDevices(false /* aDone */);
6878 alock.acquire();
6879 }
6880 }
6881
6882 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
6883 * this point). We release the lock before calling VMR3Destroy() because
6884 * it will result into calling destructors of drivers associated with
6885 * Console children which may in turn try to lock Console (e.g. by
6886 * instantiating SafeVMPtr to access mpVM). It's safe here because
6887 * mVMDestroying is set which should prevent any activity. */
6888
6889 /* Set mpUVM to NULL early just in case if some old code is not using
6890 * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */
6891 VMR3ReleaseUVM(mpUVM);
6892 mpUVM = NULL;
6893
6894 LogFlowThisFunc(("Destroying the VM...\n"));
6895
6896 alock.release();
6897
6898 vrc = VMR3Destroy(VMR3GetVM(pUVM));
6899
6900 /* take the lock again */
6901 alock.acquire();
6902
6903 /* advance percent count */
6904 if (aProgress)
6905 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6906
6907 if (RT_SUCCESS(vrc))
6908 {
6909 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
6910 mMachineState));
6911 /* Note: the Console-level machine state change happens on the
6912 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
6913 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
6914 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
6915 * occurred yet. This is okay, because mMachineState is already
6916 * Stopping in this case, so any other attempt to call PowerDown()
6917 * will be rejected. */
6918 }
6919 else
6920 {
6921 /* bad bad bad, but what to do? (Give Console our UVM ref.) */
6922 mpUVM = pUVM;
6923 pUVM = NULL;
6924 rc = setError(VBOX_E_VM_ERROR,
6925 tr("Could not destroy the machine. (Error: %Rrc)"),
6926 vrc);
6927 }
6928
6929 /* Complete the detaching of the USB devices. */
6930 if (fHasUSBController)
6931 {
6932 alock.release();
6933 detachAllUSBDevices(true /* aDone */);
6934 alock.acquire();
6935 }
6936
6937 /* advance percent count */
6938 if (aProgress)
6939 aProgress->SetCurrentOperationProgress(99 * (++step) / StepCount);
6940 }
6941 else
6942 {
6943 rc = setError(VBOX_E_VM_ERROR,
6944 tr("Could not power off the machine. (Error: %Rrc)"),
6945 vrc);
6946 }
6947
6948 /*
6949 * Finished with the destruction.
6950 *
6951 * Note that if something impossible happened and we've failed to destroy
6952 * the VM, mVMDestroying will remain true and mMachineState will be
6953 * something like Stopping, so most Console methods will return an error
6954 * to the caller.
6955 */
6956 if (mpUVM != NULL)
6957 VMR3ReleaseUVM(pUVM);
6958 else
6959 mVMDestroying = false;
6960
6961#ifdef CONSOLE_WITH_EVENT_CACHE
6962 if (SUCCEEDED(rc))
6963 mCallbackData.clear();
6964#endif
6965
6966 LogFlowThisFuncLeave();
6967 return rc;
6968}
6969
6970/**
6971 * @note Locks this object for writing.
6972 */
6973HRESULT Console::setMachineState(MachineState_T aMachineState,
6974 bool aUpdateServer /* = true */)
6975{
6976 AutoCaller autoCaller(this);
6977 AssertComRCReturnRC(autoCaller.rc());
6978
6979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 HRESULT rc = S_OK;
6982
6983 if (mMachineState != aMachineState)
6984 {
6985 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
6986 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
6987 mMachineState = aMachineState;
6988
6989 /// @todo (dmik)
6990 // possibly, we need to redo onStateChange() using the dedicated
6991 // Event thread, like it is done in VirtualBox. This will make it
6992 // much safer (no deadlocks possible if someone tries to use the
6993 // console from the callback), however, listeners will lose the
6994 // ability to synchronously react to state changes (is it really
6995 // necessary??)
6996 LogFlowThisFunc(("Doing onStateChange()...\n"));
6997 onStateChange(aMachineState);
6998 LogFlowThisFunc(("Done onStateChange()\n"));
6999
7000 if (aUpdateServer)
7001 {
7002 /* Server notification MUST be done from under the lock; otherwise
7003 * the machine state here and on the server might go out of sync
7004 * which can lead to various unexpected results (like the machine
7005 * state being >= MachineState_Running on the server, while the
7006 * session state is already SessionState_Unlocked at the same time
7007 * there).
7008 *
7009 * Cross-lock conditions should be carefully watched out: calling
7010 * UpdateState we will require Machine and SessionMachine locks
7011 * (remember that here we're holding the Console lock here, and also
7012 * all locks that have been acquire by the thread before calling
7013 * this method).
7014 */
7015 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
7016 rc = mControl->UpdateState(aMachineState);
7017 LogFlowThisFunc(("mControl->UpdateState()=%Rhrc\n", rc));
7018 }
7019 }
7020
7021 return rc;
7022}
7023
7024/**
7025 * Searches for a shared folder with the given logical name
7026 * in the collection of shared folders.
7027 *
7028 * @param aName logical name of the shared folder
7029 * @param aSharedFolder where to return the found object
7030 * @param aSetError whether to set the error info if the folder is
7031 * not found
7032 * @return
7033 * S_OK when found or E_INVALIDARG when not found
7034 *
7035 * @note The caller must lock this object for writing.
7036 */
7037HRESULT Console::findSharedFolder(const Utf8Str &strName,
7038 ComObjPtr<SharedFolder> &aSharedFolder,
7039 bool aSetError /* = false */)
7040{
7041 /* sanity check */
7042 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7043
7044 SharedFolderMap::const_iterator it = m_mapSharedFolders.find(strName);
7045 if (it != m_mapSharedFolders.end())
7046 {
7047 aSharedFolder = it->second;
7048 return S_OK;
7049 }
7050
7051 if (aSetError)
7052 setError(VBOX_E_FILE_ERROR,
7053 tr("Could not find a shared folder named '%s'."),
7054 strName.c_str());
7055
7056 return VBOX_E_FILE_ERROR;
7057}
7058
7059/**
7060 * Fetches the list of global or machine shared folders from the server.
7061 *
7062 * @param aGlobal true to fetch global folders.
7063 *
7064 * @note The caller must lock this object for writing.
7065 */
7066HRESULT Console::fetchSharedFolders(BOOL aGlobal)
7067{
7068 /* sanity check */
7069 AssertReturn(AutoCaller(this).state() == InInit ||
7070 isWriteLockOnCurrentThread(), E_FAIL);
7071
7072 LogFlowThisFunc(("Entering\n"));
7073
7074 /* Check if we're online and keep it that way. */
7075 SafeVMPtrQuiet ptrVM(this);
7076 AutoVMCallerQuietWeak autoVMCaller(this);
7077 bool const online = ptrVM.isOk()
7078 && m_pVMMDev
7079 && m_pVMMDev->isShFlActive();
7080
7081 HRESULT rc = S_OK;
7082
7083 try
7084 {
7085 if (aGlobal)
7086 {
7087 /// @todo grab & process global folders when they are done
7088 }
7089 else
7090 {
7091 SharedFolderDataMap oldFolders;
7092 if (online)
7093 oldFolders = m_mapMachineSharedFolders;
7094
7095 m_mapMachineSharedFolders.clear();
7096
7097 SafeIfaceArray<ISharedFolder> folders;
7098 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
7099 if (FAILED(rc)) throw rc;
7100
7101 for (size_t i = 0; i < folders.size(); ++i)
7102 {
7103 ComPtr<ISharedFolder> pSharedFolder = folders[i];
7104
7105 Bstr bstrName;
7106 Bstr bstrHostPath;
7107 BOOL writable;
7108 BOOL autoMount;
7109
7110 rc = pSharedFolder->COMGETTER(Name)(bstrName.asOutParam());
7111 if (FAILED(rc)) throw rc;
7112 Utf8Str strName(bstrName);
7113
7114 rc = pSharedFolder->COMGETTER(HostPath)(bstrHostPath.asOutParam());
7115 if (FAILED(rc)) throw rc;
7116 Utf8Str strHostPath(bstrHostPath);
7117
7118 rc = pSharedFolder->COMGETTER(Writable)(&writable);
7119 if (FAILED(rc)) throw rc;
7120
7121 rc = pSharedFolder->COMGETTER(AutoMount)(&autoMount);
7122 if (FAILED(rc)) throw rc;
7123
7124 m_mapMachineSharedFolders.insert(std::make_pair(strName,
7125 SharedFolderData(strHostPath, !!writable, !!autoMount)));
7126
7127 /* send changes to HGCM if the VM is running */
7128 if (online)
7129 {
7130 SharedFolderDataMap::iterator it = oldFolders.find(strName);
7131 if ( it == oldFolders.end()
7132 || it->second.m_strHostPath != strHostPath)
7133 {
7134 /* a new machine folder is added or
7135 * the existing machine folder is changed */
7136 if (m_mapSharedFolders.find(strName) != m_mapSharedFolders.end())
7137 ; /* the console folder exists, nothing to do */
7138 else
7139 {
7140 /* remove the old machine folder (when changed)
7141 * or the global folder if any (when new) */
7142 if ( it != oldFolders.end()
7143 || m_mapGlobalSharedFolders.find(strName) != m_mapGlobalSharedFolders.end()
7144 )
7145 {
7146 rc = removeSharedFolder(strName);
7147 if (FAILED(rc)) throw rc;
7148 }
7149
7150 /* create the new machine folder */
7151 rc = createSharedFolder(strName,
7152 SharedFolderData(strHostPath, !!writable, !!autoMount));
7153 if (FAILED(rc)) throw rc;
7154 }
7155 }
7156 /* forget the processed (or identical) folder */
7157 if (it != oldFolders.end())
7158 oldFolders.erase(it);
7159 }
7160 }
7161
7162 /* process outdated (removed) folders */
7163 if (online)
7164 {
7165 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
7166 it != oldFolders.end(); ++it)
7167 {
7168 if (m_mapSharedFolders.find(it->first) != m_mapSharedFolders.end())
7169 ; /* the console folder exists, nothing to do */
7170 else
7171 {
7172 /* remove the outdated machine folder */
7173 rc = removeSharedFolder(it->first);
7174 if (FAILED(rc)) throw rc;
7175
7176 /* create the global folder if there is any */
7177 SharedFolderDataMap::const_iterator git =
7178 m_mapGlobalSharedFolders.find(it->first);
7179 if (git != m_mapGlobalSharedFolders.end())
7180 {
7181 rc = createSharedFolder(git->first, git->second);
7182 if (FAILED(rc)) throw rc;
7183 }
7184 }
7185 }
7186 }
7187 }
7188 }
7189 catch (HRESULT rc2)
7190 {
7191 if (online)
7192 setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder",
7193 N_("Broken shared folder!"));
7194 }
7195
7196 LogFlowThisFunc(("Leaving\n"));
7197
7198 return rc;
7199}
7200
7201/**
7202 * Searches for a shared folder with the given name in the list of machine
7203 * shared folders and then in the list of the global shared folders.
7204 *
7205 * @param aName Name of the folder to search for.
7206 * @param aIt Where to store the pointer to the found folder.
7207 * @return @c true if the folder was found and @c false otherwise.
7208 *
7209 * @note The caller must lock this object for reading.
7210 */
7211bool Console::findOtherSharedFolder(const Utf8Str &strName,
7212 SharedFolderDataMap::const_iterator &aIt)
7213{
7214 /* sanity check */
7215 AssertReturn(isWriteLockOnCurrentThread(), false);
7216
7217 /* first, search machine folders */
7218 aIt = m_mapMachineSharedFolders.find(strName);
7219 if (aIt != m_mapMachineSharedFolders.end())
7220 return true;
7221
7222 /* second, search machine folders */
7223 aIt = m_mapGlobalSharedFolders.find(strName);
7224 if (aIt != m_mapGlobalSharedFolders.end())
7225 return true;
7226
7227 return false;
7228}
7229
7230/**
7231 * Calls the HGCM service to add a shared folder definition.
7232 *
7233 * @param aName Shared folder name.
7234 * @param aHostPath Shared folder path.
7235 *
7236 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7237 * @note Doesn't lock anything.
7238 */
7239HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderData &aData)
7240{
7241 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7242 ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL);
7243
7244 /* sanity checks */
7245 AssertReturn(mpUVM, E_FAIL);
7246 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7247
7248 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
7249 SHFLSTRING *pFolderName, *pMapName;
7250 size_t cbString;
7251
7252 Bstr value;
7253 HRESULT hrc = mMachine->GetExtraData(BstrFmt("VBoxInternal2/SharedFoldersEnableSymlinksCreate/%s",
7254 strName.c_str()).raw(),
7255 value.asOutParam());
7256 bool fSymlinksCreate = hrc == S_OK && value == "1";
7257
7258 Log(("Adding shared folder '%s' -> '%s'\n", strName.c_str(), aData.m_strHostPath.c_str()));
7259
7260 // check whether the path is valid and exists
7261 char hostPathFull[RTPATH_MAX];
7262 int vrc = RTPathAbsEx(NULL,
7263 aData.m_strHostPath.c_str(),
7264 hostPathFull,
7265 sizeof(hostPathFull));
7266 if (RT_FAILURE(vrc))
7267 return setError(E_INVALIDARG,
7268 tr("Invalid shared folder path: '%s' (%Rrc)"),
7269 aData.m_strHostPath.c_str(), vrc);
7270 if (!RTPathExists(hostPathFull))
7271 return setError(E_INVALIDARG,
7272 tr("Shared folder path '%s' does not exist on the host"),
7273 aData.m_strHostPath.c_str());
7274 /* Check whether the path is full (absolute) */
7275 if (RTPathCompare(aData.m_strHostPath.c_str(), hostPathFull) != 0)
7276 return setError(E_INVALIDARG,
7277 tr("Shared folder path '%s' is not absolute"),
7278 aData.m_strHostPath.c_str());
7279
7280 // now that we know the path is good, give it to HGCM
7281
7282 Bstr bstrName(strName);
7283 Bstr bstrHostPath(aData.m_strHostPath);
7284
7285 cbString = (bstrHostPath.length() + 1) * sizeof(RTUTF16);
7286 if (cbString >= UINT16_MAX)
7287 return setError(E_INVALIDARG, tr("The name is too long"));
7288 pFolderName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7289 Assert(pFolderName);
7290 memcpy(pFolderName->String.ucs2, bstrHostPath.raw(), cbString);
7291
7292 pFolderName->u16Size = (uint16_t)cbString;
7293 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7294
7295 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
7296 parms[0].u.pointer.addr = pFolderName;
7297 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7298
7299 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7300 if (cbString >= UINT16_MAX)
7301 {
7302 RTMemFree(pFolderName);
7303 return setError(E_INVALIDARG, tr("The host path is too long"));
7304 }
7305 pMapName = (SHFLSTRING*)RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7306 Assert(pMapName);
7307 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7308
7309 pMapName->u16Size = (uint16_t)cbString;
7310 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7311
7312 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
7313 parms[1].u.pointer.addr = pMapName;
7314 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7315
7316 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
7317 parms[2].u.uint32 = (aData.m_fWritable ? SHFL_ADD_MAPPING_F_WRITABLE : 0)
7318 | (aData.m_fAutoMount ? SHFL_ADD_MAPPING_F_AUTOMOUNT : 0)
7319 | (fSymlinksCreate ? SHFL_ADD_MAPPING_F_CREATE_SYMLINKS : 0);
7320
7321 vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7322 SHFL_FN_ADD_MAPPING,
7323 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
7324 RTMemFree(pFolderName);
7325 RTMemFree(pMapName);
7326
7327 if (RT_FAILURE(vrc))
7328 return setError(E_FAIL,
7329 tr("Could not create a shared folder '%s' mapped to '%s' (%Rrc)"),
7330 strName.c_str(), aData.m_strHostPath.c_str(), vrc);
7331
7332 return S_OK;
7333}
7334
7335/**
7336 * Calls the HGCM service to remove the shared folder definition.
7337 *
7338 * @param aName Shared folder name.
7339 *
7340 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
7341 * @note Doesn't lock anything.
7342 */
7343HRESULT Console::removeSharedFolder(const Utf8Str &strName)
7344{
7345 ComAssertRet(strName.isNotEmpty(), E_FAIL);
7346
7347 /* sanity checks */
7348 AssertReturn(mpUVM, E_FAIL);
7349 AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
7350
7351 VBOXHGCMSVCPARM parms;
7352 SHFLSTRING *pMapName;
7353 size_t cbString;
7354
7355 Log(("Removing shared folder '%s'\n", strName.c_str()));
7356
7357 Bstr bstrName(strName);
7358 cbString = (bstrName.length() + 1) * sizeof(RTUTF16);
7359 if (cbString >= UINT16_MAX)
7360 return setError(E_INVALIDARG, tr("The name is too long"));
7361 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
7362 Assert(pMapName);
7363 memcpy(pMapName->String.ucs2, bstrName.raw(), cbString);
7364
7365 pMapName->u16Size = (uint16_t)cbString;
7366 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
7367
7368 parms.type = VBOX_HGCM_SVC_PARM_PTR;
7369 parms.u.pointer.addr = pMapName;
7370 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
7371
7372 int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
7373 SHFL_FN_REMOVE_MAPPING,
7374 1, &parms);
7375 RTMemFree(pMapName);
7376 if (RT_FAILURE(vrc))
7377 return setError(E_FAIL,
7378 tr("Could not remove the shared folder '%s' (%Rrc)"),
7379 strName.c_str(), vrc);
7380
7381 return S_OK;
7382}
7383
7384/**
7385 * VM state callback function. Called by the VMM
7386 * using its state machine states.
7387 *
7388 * Primarily used to handle VM initiated power off, suspend and state saving,
7389 * but also for doing termination completed work (VMSTATE_TERMINATE).
7390 *
7391 * In general this function is called in the context of the EMT.
7392 *
7393 * @param aVM The VM handle.
7394 * @param aState The new state.
7395 * @param aOldState The old state.
7396 * @param aUser The user argument (pointer to the Console object).
7397 *
7398 * @note Locks the Console object for writing.
7399 */
7400DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
7401 VMSTATE aState,
7402 VMSTATE aOldState,
7403 void *aUser)
7404{
7405 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
7406 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
7407
7408 Console *that = static_cast<Console *>(aUser);
7409 AssertReturnVoid(that);
7410
7411 AutoCaller autoCaller(that);
7412
7413 /* Note that we must let this method proceed even if Console::uninit() has
7414 * been already called. In such case this VMSTATE change is a result of:
7415 * 1) powerDown() called from uninit() itself, or
7416 * 2) VM-(guest-)initiated power off. */
7417 AssertReturnVoid( autoCaller.isOk()
7418 || autoCaller.state() == InUninit);
7419
7420 switch (aState)
7421 {
7422 /*
7423 * The VM has terminated
7424 */
7425 case VMSTATE_OFF:
7426 {
7427 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7428
7429 if (that->mVMStateChangeCallbackDisabled)
7430 break;
7431
7432 /* Do we still think that it is running? It may happen if this is a
7433 * VM-(guest-)initiated shutdown/poweroff.
7434 */
7435 if ( that->mMachineState != MachineState_Stopping
7436 && that->mMachineState != MachineState_Saving
7437 && that->mMachineState != MachineState_Restoring
7438 && that->mMachineState != MachineState_TeleportingIn
7439 && that->mMachineState != MachineState_FaultTolerantSyncing
7440 && that->mMachineState != MachineState_TeleportingPausedVM
7441 && !that->mVMIsAlreadyPoweringOff
7442 )
7443 {
7444 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
7445
7446 /* prevent powerDown() from calling VMR3PowerOff() again */
7447 Assert(that->mVMPoweredOff == false);
7448 that->mVMPoweredOff = true;
7449
7450 /*
7451 * request a progress object from the server
7452 * (this will set the machine state to Stopping on the server
7453 * to block others from accessing this machine)
7454 */
7455 ComPtr<IProgress> pProgress;
7456 HRESULT rc = that->mControl->BeginPoweringDown(pProgress.asOutParam());
7457 AssertComRC(rc);
7458
7459 /* sync the state with the server */
7460 that->setMachineStateLocally(MachineState_Stopping);
7461
7462 /* Setup task object and thread to carry out the operation
7463 * asynchronously (if we call powerDown() right here but there
7464 * is one or more mpVM callers (added with addVMCaller()) we'll
7465 * deadlock).
7466 */
7467 std::auto_ptr<VMPowerDownTask> task(new VMPowerDownTask(that,
7468 pProgress));
7469
7470 /* If creating a task failed, this can currently mean one of
7471 * two: either Console::uninit() has been called just a ms
7472 * before (so a powerDown() call is already on the way), or
7473 * powerDown() itself is being already executed. Just do
7474 * nothing.
7475 */
7476 if (!task->isOk())
7477 {
7478 LogFlowFunc(("Console is already being uninitialized.\n"));
7479 break;
7480 }
7481
7482 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
7483 (void *) task.get(), 0,
7484 RTTHREADTYPE_MAIN_WORKER, 0,
7485 "VMPwrDwn");
7486 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
7487
7488 /* task is now owned by powerDownThread(), so release it */
7489 task.release();
7490 }
7491 break;
7492 }
7493
7494 /* The VM has been completely destroyed.
7495 *
7496 * Note: This state change can happen at two points:
7497 * 1) At the end of VMR3Destroy() if it was not called from EMT.
7498 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
7499 * called by EMT.
7500 */
7501 case VMSTATE_TERMINATED:
7502 {
7503 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7504
7505 if (that->mVMStateChangeCallbackDisabled)
7506 break;
7507
7508 /* Terminate host interface networking. If aVM is NULL, we've been
7509 * manually called from powerUpThread() either before calling
7510 * VMR3Create() or after VMR3Create() failed, so no need to touch
7511 * networking.
7512 */
7513 if (aVM)
7514 that->powerDownHostInterfaces();
7515
7516 /* From now on the machine is officially powered down or remains in
7517 * the Saved state.
7518 */
7519 switch (that->mMachineState)
7520 {
7521 default:
7522 AssertFailed();
7523 /* fall through */
7524 case MachineState_Stopping:
7525 /* successfully powered down */
7526 that->setMachineState(MachineState_PoweredOff);
7527 break;
7528 case MachineState_Saving:
7529 /* successfully saved */
7530 that->setMachineState(MachineState_Saved);
7531 break;
7532 case MachineState_Starting:
7533 /* failed to start, but be patient: set back to PoweredOff
7534 * (for similarity with the below) */
7535 that->setMachineState(MachineState_PoweredOff);
7536 break;
7537 case MachineState_Restoring:
7538 /* failed to load the saved state file, but be patient: set
7539 * back to Saved (to preserve the saved state file) */
7540 that->setMachineState(MachineState_Saved);
7541 break;
7542 case MachineState_TeleportingIn:
7543 /* Teleportation failed or was canceled. Back to powered off. */
7544 that->setMachineState(MachineState_PoweredOff);
7545 break;
7546 case MachineState_TeleportingPausedVM:
7547 /* Successfully teleported the VM. */
7548 that->setMachineState(MachineState_Teleported);
7549 break;
7550 case MachineState_FaultTolerantSyncing:
7551 /* Fault tolerant sync failed or was canceled. Back to powered off. */
7552 that->setMachineState(MachineState_PoweredOff);
7553 break;
7554 }
7555 break;
7556 }
7557
7558 case VMSTATE_RESETTING:
7559 {
7560#ifdef VBOX_WITH_GUEST_PROPS
7561 /* Do not take any read/write locks here! */
7562 that->guestPropertiesHandleVMReset();
7563#endif
7564 break;
7565 }
7566
7567 case VMSTATE_SUSPENDED:
7568 {
7569 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7570
7571 if (that->mVMStateChangeCallbackDisabled)
7572 break;
7573
7574 switch (that->mMachineState)
7575 {
7576 case MachineState_Teleporting:
7577 that->setMachineState(MachineState_TeleportingPausedVM);
7578 break;
7579
7580 case MachineState_LiveSnapshotting:
7581 that->setMachineState(MachineState_Saving);
7582 break;
7583
7584 case MachineState_TeleportingPausedVM:
7585 case MachineState_Saving:
7586 case MachineState_Restoring:
7587 case MachineState_Stopping:
7588 case MachineState_TeleportingIn:
7589 case MachineState_FaultTolerantSyncing:
7590 /* The worker thread handles the transition. */
7591 break;
7592
7593 default:
7594 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
7595 case MachineState_Running:
7596 that->setMachineState(MachineState_Paused);
7597 break;
7598
7599 case MachineState_Paused:
7600 /* Nothing to do. */
7601 break;
7602 }
7603 break;
7604 }
7605
7606 case VMSTATE_SUSPENDED_LS:
7607 case VMSTATE_SUSPENDED_EXT_LS:
7608 {
7609 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7610 if (that->mVMStateChangeCallbackDisabled)
7611 break;
7612 switch (that->mMachineState)
7613 {
7614 case MachineState_Teleporting:
7615 that->setMachineState(MachineState_TeleportingPausedVM);
7616 break;
7617
7618 case MachineState_LiveSnapshotting:
7619 that->setMachineState(MachineState_Saving);
7620 break;
7621
7622 case MachineState_TeleportingPausedVM:
7623 case MachineState_Saving:
7624 /* ignore */
7625 break;
7626
7627 default:
7628 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7629 that->setMachineState(MachineState_Paused);
7630 break;
7631 }
7632 break;
7633 }
7634
7635 case VMSTATE_RUNNING:
7636 {
7637 if ( aOldState == VMSTATE_POWERING_ON
7638 || aOldState == VMSTATE_RESUMING
7639 || aOldState == VMSTATE_RUNNING_FT)
7640 {
7641 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7642
7643 if (that->mVMStateChangeCallbackDisabled)
7644 break;
7645
7646 Assert( ( ( that->mMachineState == MachineState_Starting
7647 || that->mMachineState == MachineState_Paused)
7648 && aOldState == VMSTATE_POWERING_ON)
7649 || ( ( that->mMachineState == MachineState_Restoring
7650 || that->mMachineState == MachineState_TeleportingIn
7651 || that->mMachineState == MachineState_Paused
7652 || that->mMachineState == MachineState_Saving
7653 )
7654 && aOldState == VMSTATE_RESUMING)
7655 || ( that->mMachineState == MachineState_FaultTolerantSyncing
7656 && aOldState == VMSTATE_RUNNING_FT));
7657
7658 that->setMachineState(MachineState_Running);
7659 }
7660
7661 break;
7662 }
7663
7664 case VMSTATE_RUNNING_LS:
7665 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
7666 || that->mMachineState == MachineState_Teleporting,
7667 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7668 break;
7669
7670 case VMSTATE_RUNNING_FT:
7671 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
7672 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
7673 break;
7674
7675 case VMSTATE_FATAL_ERROR:
7676 {
7677 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7678
7679 if (that->mVMStateChangeCallbackDisabled)
7680 break;
7681
7682 /* Fatal errors are only for running VMs. */
7683 Assert(Global::IsOnline(that->mMachineState));
7684
7685 /* Note! 'Pause' is used here in want of something better. There
7686 * are currently only two places where fatal errors might be
7687 * raised, so it is not worth adding a new externally
7688 * visible state for this yet. */
7689 that->setMachineState(MachineState_Paused);
7690 break;
7691 }
7692
7693 case VMSTATE_GURU_MEDITATION:
7694 {
7695 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7696
7697 if (that->mVMStateChangeCallbackDisabled)
7698 break;
7699
7700 /* Guru are only for running VMs */
7701 Assert(Global::IsOnline(that->mMachineState));
7702
7703 that->setMachineState(MachineState_Stuck);
7704 break;
7705 }
7706
7707 default: /* shut up gcc */
7708 break;
7709 }
7710}
7711
7712/**
7713 * Changes the clipboard mode.
7714 *
7715 * @param aClipboardMode new clipboard mode.
7716 */
7717void Console::changeClipboardMode(ClipboardMode_T aClipboardMode)
7718{
7719 VMMDev *pVMMDev = m_pVMMDev;
7720 Assert(pVMMDev);
7721
7722 VBOXHGCMSVCPARM parm;
7723 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7724
7725 switch (aClipboardMode)
7726 {
7727 default:
7728 case ClipboardMode_Disabled:
7729 LogRel(("Shared clipboard mode: Off\n"));
7730 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_OFF;
7731 break;
7732 case ClipboardMode_GuestToHost:
7733 LogRel(("Shared clipboard mode: Guest to Host\n"));
7734 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST;
7735 break;
7736 case ClipboardMode_HostToGuest:
7737 LogRel(("Shared clipboard mode: Host to Guest\n"));
7738 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST;
7739 break;
7740 case ClipboardMode_Bidirectional:
7741 LogRel(("Shared clipboard mode: Bidirectional\n"));
7742 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL;
7743 break;
7744 }
7745
7746 pVMMDev->hgcmHostCall("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, 1, &parm);
7747}
7748
7749/**
7750 * Changes the drag'n_drop mode.
7751 *
7752 * @param aDragAndDropMode new drag'n'drop mode.
7753 */
7754void Console::changeDragAndDropMode(DragAndDropMode_T aDragAndDropMode)
7755{
7756 VMMDev *pVMMDev = m_pVMMDev;
7757 Assert(pVMMDev);
7758
7759 VBOXHGCMSVCPARM parm;
7760 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
7761
7762 switch (aDragAndDropMode)
7763 {
7764 default:
7765 case DragAndDropMode_Disabled:
7766 LogRel(("Drag'n'drop mode: Off\n"));
7767 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_OFF;
7768 break;
7769 case ClipboardMode_GuestToHost:
7770 LogRel(("Drag'n'drop mode: Guest to Host\n"));
7771 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST;
7772 break;
7773 case ClipboardMode_HostToGuest:
7774 LogRel(("Drag'n'drop mode: Host to Guest\n"));
7775 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST;
7776 break;
7777 case ClipboardMode_Bidirectional:
7778 LogRel(("Drag'n'drop mode: Bidirectional\n"));
7779 parm.u.uint32 = VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL;
7780 break;
7781 }
7782
7783 pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", DragAndDropSvc::HOST_DND_SET_MODE, 1, &parm);
7784}
7785
7786#ifdef VBOX_WITH_USB
7787/**
7788 * Sends a request to VMM to attach the given host device.
7789 * After this method succeeds, the attached device will appear in the
7790 * mUSBDevices collection.
7791 *
7792 * @param aHostDevice device to attach
7793 *
7794 * @note Synchronously calls EMT.
7795 */
7796HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
7797{
7798 AssertReturn(aHostDevice, E_FAIL);
7799 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
7800
7801 HRESULT hrc;
7802
7803 /*
7804 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
7805 * method in EMT (using usbAttachCallback()).
7806 */
7807 Bstr BstrAddress;
7808 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
7809 ComAssertComRCRetRC(hrc);
7810
7811 Utf8Str Address(BstrAddress);
7812
7813 Bstr id;
7814 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
7815 ComAssertComRCRetRC(hrc);
7816 Guid uuid(id);
7817
7818 BOOL fRemote = FALSE;
7819 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
7820 ComAssertComRCRetRC(hrc);
7821
7822 /* Get the VM handle. */
7823 SafeVMPtr ptrVM(this);
7824 if (!ptrVM.isOk())
7825 return ptrVM.rc();
7826
7827 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
7828 Address.c_str(), uuid.raw()));
7829
7830 void *pvRemoteBackend = NULL;
7831 if (fRemote)
7832 {
7833 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
7834 pvRemoteBackend = consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &uuid);
7835 if (!pvRemoteBackend)
7836 return E_INVALIDARG; /* The clientId is invalid then. */
7837 }
7838
7839 USHORT portVersion = 1;
7840 hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
7841 AssertComRCReturnRC(hrc);
7842 Assert(portVersion == 1 || portVersion == 2);
7843
7844 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7845 (PFNRT)usbAttachCallback, 9,
7846 this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), pvRemoteBackend, portVersion, aMaskedIfs);
7847
7848 if (RT_SUCCESS(vrc))
7849 {
7850 /* Create a OUSBDevice and add it to the device list */
7851 ComObjPtr<OUSBDevice> pUSBDevice;
7852 pUSBDevice.createObject();
7853 hrc = pUSBDevice->init(aHostDevice);
7854 AssertComRC(hrc);
7855
7856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7857 mUSBDevices.push_back(pUSBDevice);
7858 LogFlowFunc(("Attached device {%RTuuid}\n", pUSBDevice->id().raw()));
7859
7860 /* notify callbacks */
7861 alock.release();
7862 onUSBDeviceStateChange(pUSBDevice, true /* aAttached */, NULL);
7863 }
7864 else
7865 {
7866 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
7867 Address.c_str(), uuid.raw(), vrc));
7868
7869 switch (vrc)
7870 {
7871 case VERR_VUSB_NO_PORTS:
7872 hrc = setError(E_FAIL,
7873 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
7874 break;
7875 case VERR_VUSB_USBFS_PERMISSION:
7876 hrc = setError(E_FAIL,
7877 tr("Not permitted to open the USB device, check usbfs options"));
7878 break;
7879 default:
7880 hrc = setError(E_FAIL,
7881 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
7882 vrc);
7883 break;
7884 }
7885 }
7886
7887 return hrc;
7888}
7889
7890/**
7891 * USB device attach callback used by AttachUSBDevice().
7892 * Note that AttachUSBDevice() doesn't return until this callback is executed,
7893 * so we don't use AutoCaller and don't care about reference counters of
7894 * interface pointers passed in.
7895 *
7896 * @thread EMT
7897 * @note Locks the console object for writing.
7898 */
7899//static
7900DECLCALLBACK(int)
7901Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, void *pvRemoteBackend, USHORT aPortVersion, ULONG aMaskedIfs)
7902{
7903 LogFlowFuncEnter();
7904 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
7905
7906 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7907 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
7908
7909 int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend,
7910 aPortVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
7911 LogFlowFunc(("vrc=%Rrc\n", vrc));
7912 LogFlowFuncLeave();
7913 return vrc;
7914}
7915
7916/**
7917 * Sends a request to VMM to detach the given host device. After this method
7918 * succeeds, the detached device will disappear from the mUSBDevices
7919 * collection.
7920 *
7921 * @param aHostDevice device to attach
7922 *
7923 * @note Synchronously calls EMT.
7924 */
7925HRESULT Console::detachUSBDevice(const ComObjPtr<OUSBDevice> &aHostDevice)
7926{
7927 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
7928
7929 /* Get the VM handle. */
7930 SafeVMPtr ptrVM(this);
7931 if (!ptrVM.isOk())
7932 return ptrVM.rc();
7933
7934 /* if the device is attached, then there must at least one USB hub. */
7935 AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL);
7936
7937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7938 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
7939 aHostDevice->id().raw()));
7940
7941 /*
7942 * If this was a remote device, release the backend pointer.
7943 * The pointer was requested in usbAttachCallback.
7944 */
7945 BOOL fRemote = FALSE;
7946
7947 HRESULT hrc2 = aHostDevice->COMGETTER(Remote)(&fRemote);
7948 if (FAILED(hrc2))
7949 setErrorStatic(hrc2, "GetRemote() failed");
7950
7951 PCRTUUID pUuid = aHostDevice->id().raw();
7952 if (fRemote)
7953 {
7954 Guid guid(*pUuid);
7955 consoleVRDPServer()->USBBackendReleasePointer(&guid);
7956 }
7957
7958 alock.release();
7959 int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY,
7960 (PFNRT)usbDetachCallback, 5,
7961 this, ptrVM.raw(), pUuid);
7962 if (RT_SUCCESS(vrc))
7963 {
7964 LogFlowFunc(("Detached device {%RTuuid}\n", pUuid));
7965
7966 /* notify callbacks */
7967 onUSBDeviceStateChange(aHostDevice, false /* aAttached */, NULL);
7968 }
7969
7970 ComAssertRCRet(vrc, E_FAIL);
7971
7972 return S_OK;
7973}
7974
7975/**
7976 * USB device detach callback used by DetachUSBDevice().
7977 * Note that DetachUSBDevice() doesn't return until this callback is executed,
7978 * so we don't use AutoCaller and don't care about reference counters of
7979 * interface pointers passed in.
7980 *
7981 * @thread EMT
7982 */
7983//static
7984DECLCALLBACK(int)
7985Console::usbDetachCallback(Console *that, PVM pVM, PCRTUUID aUuid)
7986{
7987 LogFlowFuncEnter();
7988 LogFlowFunc(("that={%p} aUuid={%RTuuid}\n", that, aUuid));
7989
7990 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
7991 AssertReturn(!that->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
7992
7993 int vrc = PDMR3USBDetachDevice(pVM, aUuid);
7994
7995 LogFlowFunc(("vrc=%Rrc\n", vrc));
7996 LogFlowFuncLeave();
7997 return vrc;
7998}
7999#endif /* VBOX_WITH_USB */
8000
8001/* Note: FreeBSD needs this whether netflt is used or not. */
8002#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
8003/**
8004 * Helper function to handle host interface device creation and attachment.
8005 *
8006 * @param networkAdapter the network adapter which attachment should be reset
8007 * @return COM status code
8008 *
8009 * @note The caller must lock this object for writing.
8010 *
8011 * @todo Move this back into the driver!
8012 */
8013HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
8014{
8015 LogFlowThisFunc(("\n"));
8016 /* sanity check */
8017 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8018
8019# ifdef VBOX_STRICT
8020 /* paranoia */
8021 NetworkAttachmentType_T attachment;
8022 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8023 Assert(attachment == NetworkAttachmentType_Bridged);
8024# endif /* VBOX_STRICT */
8025
8026 HRESULT rc = S_OK;
8027
8028 ULONG slot = 0;
8029 rc = networkAdapter->COMGETTER(Slot)(&slot);
8030 AssertComRC(rc);
8031
8032# ifdef RT_OS_LINUX
8033 /*
8034 * Allocate a host interface device
8035 */
8036 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
8037 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
8038 if (RT_SUCCESS(rcVBox))
8039 {
8040 /*
8041 * Set/obtain the tap interface.
8042 */
8043 struct ifreq IfReq;
8044 memset(&IfReq, 0, sizeof(IfReq));
8045 /* The name of the TAP interface we are using */
8046 Bstr tapDeviceName;
8047 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8048 if (FAILED(rc))
8049 tapDeviceName.setNull(); /* Is this necessary? */
8050 if (tapDeviceName.isEmpty())
8051 {
8052 LogRel(("No TAP device name was supplied.\n"));
8053 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8054 }
8055
8056 if (SUCCEEDED(rc))
8057 {
8058 /* If we are using a static TAP device then try to open it. */
8059 Utf8Str str(tapDeviceName);
8060 if (str.length() <= sizeof(IfReq.ifr_name))
8061 strcpy(IfReq.ifr_name, str.c_str());
8062 else
8063 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
8064 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
8065 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
8066 if (rcVBox != 0)
8067 {
8068 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
8069 rc = setError(E_FAIL,
8070 tr("Failed to open the host network interface %ls"),
8071 tapDeviceName.raw());
8072 }
8073 }
8074 if (SUCCEEDED(rc))
8075 {
8076 /*
8077 * Make it pollable.
8078 */
8079 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
8080 {
8081 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
8082 /*
8083 * Here is the right place to communicate the TAP file descriptor and
8084 * the host interface name to the server if/when it becomes really
8085 * necessary.
8086 */
8087 maTAPDeviceName[slot] = tapDeviceName;
8088 rcVBox = VINF_SUCCESS;
8089 }
8090 else
8091 {
8092 int iErr = errno;
8093
8094 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
8095 rcVBox = VERR_HOSTIF_BLOCKING;
8096 rc = setError(E_FAIL,
8097 tr("could not set up the host networking device for non blocking access: %s"),
8098 strerror(errno));
8099 }
8100 }
8101 }
8102 else
8103 {
8104 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
8105 switch (rcVBox)
8106 {
8107 case VERR_ACCESS_DENIED:
8108 /* will be handled by our caller */
8109 rc = rcVBox;
8110 break;
8111 default:
8112 rc = setError(E_FAIL,
8113 tr("Could not set up the host networking device: %Rrc"),
8114 rcVBox);
8115 break;
8116 }
8117 }
8118
8119# elif defined(RT_OS_FREEBSD)
8120 /*
8121 * Set/obtain the tap interface.
8122 */
8123 /* The name of the TAP interface we are using */
8124 Bstr tapDeviceName;
8125 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8126 if (FAILED(rc))
8127 tapDeviceName.setNull(); /* Is this necessary? */
8128 if (tapDeviceName.isEmpty())
8129 {
8130 LogRel(("No TAP device name was supplied.\n"));
8131 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
8132 }
8133 char szTapdev[1024] = "/dev/";
8134 /* If we are using a static TAP device then try to open it. */
8135 Utf8Str str(tapDeviceName);
8136 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
8137 strcat(szTapdev, str.c_str());
8138 else
8139 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
8140 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
8141 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
8142 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
8143
8144 if (RT_SUCCESS(rcVBox))
8145 maTAPDeviceName[slot] = tapDeviceName;
8146 else
8147 {
8148 switch (rcVBox)
8149 {
8150 case VERR_ACCESS_DENIED:
8151 /* will be handled by our caller */
8152 rc = rcVBox;
8153 break;
8154 default:
8155 rc = setError(E_FAIL,
8156 tr("Failed to open the host network interface %ls"),
8157 tapDeviceName.raw());
8158 break;
8159 }
8160 }
8161# else
8162# error "huh?"
8163# endif
8164 /* in case of failure, cleanup. */
8165 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
8166 {
8167 LogRel(("General failure attaching to host interface\n"));
8168 rc = setError(E_FAIL,
8169 tr("General failure attaching to host interface"));
8170 }
8171 LogFlowThisFunc(("rc=%d\n", rc));
8172 return rc;
8173}
8174
8175
8176/**
8177 * Helper function to handle detachment from a host interface
8178 *
8179 * @param networkAdapter the network adapter which attachment should be reset
8180 * @return COM status code
8181 *
8182 * @note The caller must lock this object for writing.
8183 *
8184 * @todo Move this back into the driver!
8185 */
8186HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
8187{
8188 /* sanity check */
8189 LogFlowThisFunc(("\n"));
8190 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8191
8192 HRESULT rc = S_OK;
8193# ifdef VBOX_STRICT
8194 /* paranoia */
8195 NetworkAttachmentType_T attachment;
8196 networkAdapter->COMGETTER(AttachmentType)(&attachment);
8197 Assert(attachment == NetworkAttachmentType_Bridged);
8198# endif /* VBOX_STRICT */
8199
8200 ULONG slot = 0;
8201 rc = networkAdapter->COMGETTER(Slot)(&slot);
8202 AssertComRC(rc);
8203
8204 /* is there an open TAP device? */
8205 if (maTapFD[slot] != NIL_RTFILE)
8206 {
8207 /*
8208 * Close the file handle.
8209 */
8210 Bstr tapDeviceName, tapTerminateApplication;
8211 bool isStatic = true;
8212 rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam());
8213 if (FAILED(rc) || tapDeviceName.isEmpty())
8214 {
8215 /* If the name is empty, this is a dynamic TAP device, so close it now,
8216 so that the termination script can remove the interface. Otherwise we still
8217 need the FD to pass to the termination script. */
8218 isStatic = false;
8219 int rcVBox = RTFileClose(maTapFD[slot]);
8220 AssertRC(rcVBox);
8221 maTapFD[slot] = NIL_RTFILE;
8222 }
8223 if (isStatic)
8224 {
8225 /* If we are using a static TAP device, we close it now, after having called the
8226 termination script. */
8227 int rcVBox = RTFileClose(maTapFD[slot]);
8228 AssertRC(rcVBox);
8229 }
8230 /* the TAP device name and handle are no longer valid */
8231 maTapFD[slot] = NIL_RTFILE;
8232 maTAPDeviceName[slot] = "";
8233 }
8234 LogFlowThisFunc(("returning %d\n", rc));
8235 return rc;
8236}
8237#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8238
8239/**
8240 * Called at power down to terminate host interface networking.
8241 *
8242 * @note The caller must lock this object for writing.
8243 */
8244HRESULT Console::powerDownHostInterfaces()
8245{
8246 LogFlowThisFunc(("\n"));
8247
8248 /* sanity check */
8249 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8250
8251 /*
8252 * host interface termination handling
8253 */
8254 HRESULT rc = S_OK;
8255 ComPtr<IVirtualBox> pVirtualBox;
8256 mMachine->COMGETTER(Parent)(pVirtualBox.asOutParam());
8257 ComPtr<ISystemProperties> pSystemProperties;
8258 if (pVirtualBox)
8259 pVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
8260 ChipsetType_T chipsetType = ChipsetType_PIIX3;
8261 mMachine->COMGETTER(ChipsetType)(&chipsetType);
8262 ULONG maxNetworkAdapters = 0;
8263 if (pSystemProperties)
8264 pSystemProperties->GetMaxNetworkAdapters(chipsetType, &maxNetworkAdapters);
8265
8266 for (ULONG slot = 0; slot < maxNetworkAdapters; slot++)
8267 {
8268 ComPtr<INetworkAdapter> pNetworkAdapter;
8269 rc = mMachine->GetNetworkAdapter(slot, pNetworkAdapter.asOutParam());
8270 if (FAILED(rc)) break;
8271
8272 BOOL enabled = FALSE;
8273 pNetworkAdapter->COMGETTER(Enabled)(&enabled);
8274 if (!enabled)
8275 continue;
8276
8277 NetworkAttachmentType_T attachment;
8278 pNetworkAdapter->COMGETTER(AttachmentType)(&attachment);
8279 if (attachment == NetworkAttachmentType_Bridged)
8280 {
8281#if ((defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT))
8282 HRESULT rc2 = detachFromTapInterface(pNetworkAdapter);
8283 if (FAILED(rc2) && SUCCEEDED(rc))
8284 rc = rc2;
8285#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
8286 }
8287 }
8288
8289 return rc;
8290}
8291
8292
8293/**
8294 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
8295 * and VMR3Teleport.
8296 *
8297 * @param pVM The VM handle.
8298 * @param uPercent Completion percentage (0-100).
8299 * @param pvUser Pointer to an IProgress instance.
8300 * @return VINF_SUCCESS.
8301 */
8302/*static*/
8303DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
8304{
8305 IProgress *pProgress = static_cast<IProgress *>(pvUser);
8306
8307 /* update the progress object */
8308 if (pProgress)
8309 pProgress->SetCurrentOperationProgress(uPercent);
8310
8311 return VINF_SUCCESS;
8312}
8313
8314/**
8315 * @copydoc FNVMATERROR
8316 *
8317 * @remarks Might be some tiny serialization concerns with access to the string
8318 * object here...
8319 */
8320/*static*/ DECLCALLBACK(void)
8321Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
8322 const char *pszErrorFmt, va_list va)
8323{
8324 Utf8Str *pErrorText = (Utf8Str *)pvUser;
8325 AssertPtr(pErrorText);
8326
8327 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
8328 va_list va2;
8329 va_copy(va2, va);
8330
8331 /* Append to any the existing error message. */
8332 if (pErrorText->length())
8333 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
8334 pszErrorFmt, &va2, rc, rc);
8335 else
8336 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
8337
8338 va_end(va2);
8339}
8340
8341/**
8342 * VM runtime error callback function.
8343 * See VMSetRuntimeError for the detailed description of parameters.
8344 *
8345 * @param pVM The VM handle.
8346 * @param pvUser The user argument.
8347 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
8348 * @param pszErrorId Error ID string.
8349 * @param pszFormat Error message format string.
8350 * @param va Error message arguments.
8351 * @thread EMT.
8352 */
8353/* static */ DECLCALLBACK(void)
8354Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
8355 const char *pszErrorId,
8356 const char *pszFormat, va_list va)
8357{
8358 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
8359 LogFlowFuncEnter();
8360
8361 Console *that = static_cast<Console *>(pvUser);
8362 AssertReturnVoid(that);
8363
8364 Utf8Str message(pszFormat, va);
8365
8366 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
8367 fFatal, pszErrorId, message.c_str()));
8368
8369 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
8370 Bstr(message).raw());
8371
8372 LogFlowFuncLeave();
8373}
8374
8375/**
8376 * Captures USB devices that match filters of the VM.
8377 * Called at VM startup.
8378 *
8379 * @param pVM The VM handle.
8380 */
8381HRESULT Console::captureUSBDevices(PVM pVM)
8382{
8383 LogFlowThisFunc(("\n"));
8384
8385 /* sanity check */
8386 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
8387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8388
8389 /* If the machine has an USB controller, ask the USB proxy service to
8390 * capture devices */
8391 PPDMIBASE pBase;
8392 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
8393 if (RT_SUCCESS(vrc))
8394 {
8395 /* release the lock before calling Host in VBoxSVC since Host may call
8396 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8397 * produce an inter-process dead-lock otherwise. */
8398 alock.release();
8399
8400 HRESULT hrc = mControl->AutoCaptureUSBDevices();
8401 ComAssertComRCRetRC(hrc);
8402 }
8403 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
8404 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
8405 vrc = VINF_SUCCESS;
8406 else
8407 AssertRC(vrc);
8408
8409 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
8410}
8411
8412
8413/**
8414 * Detach all USB device which are attached to the VM for the
8415 * purpose of clean up and such like.
8416 */
8417void Console::detachAllUSBDevices(bool aDone)
8418{
8419 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
8420
8421 /* sanity check */
8422 AssertReturnVoid(!isWriteLockOnCurrentThread());
8423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8424
8425 mUSBDevices.clear();
8426
8427 /* release the lock before calling Host in VBoxSVC since Host may call
8428 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
8429 * produce an inter-process dead-lock otherwise. */
8430 alock.release();
8431
8432 mControl->DetachAllUSBDevices(aDone);
8433}
8434
8435/**
8436 * @note Locks this object for writing.
8437 */
8438void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDEUSBDEVICEDESC *pDevList, uint32_t cbDevList, bool fDescExt)
8439{
8440 LogFlowThisFuncEnter();
8441 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d, fDescExt = %d\n", u32ClientId, pDevList, cbDevList, fDescExt));
8442
8443 AutoCaller autoCaller(this);
8444 if (!autoCaller.isOk())
8445 {
8446 /* Console has been already uninitialized, deny request */
8447 AssertMsgFailed(("Console is already uninitialized\n"));
8448 LogFlowThisFunc(("Console is already uninitialized\n"));
8449 LogFlowThisFuncLeave();
8450 return;
8451 }
8452
8453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8454
8455 /*
8456 * Mark all existing remote USB devices as dirty.
8457 */
8458 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8459 it != mRemoteUSBDevices.end();
8460 ++it)
8461 {
8462 (*it)->dirty(true);
8463 }
8464
8465 /*
8466 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
8467 */
8468 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
8469 VRDEUSBDEVICEDESC *e = pDevList;
8470
8471 /* The cbDevList condition must be checked first, because the function can
8472 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
8473 */
8474 while (cbDevList >= 2 && e->oNext)
8475 {
8476 /* Sanitize incoming strings in case they aren't valid UTF-8. */
8477 if (e->oManufacturer)
8478 RTStrPurgeEncoding((char *)e + e->oManufacturer);
8479 if (e->oProduct)
8480 RTStrPurgeEncoding((char *)e + e->oProduct);
8481 if (e->oSerialNumber)
8482 RTStrPurgeEncoding((char *)e + e->oSerialNumber);
8483
8484 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
8485 e->idVendor, e->idProduct,
8486 e->oProduct? (char *)e + e->oProduct: ""));
8487
8488 bool fNewDevice = true;
8489
8490 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8491 it != mRemoteUSBDevices.end();
8492 ++it)
8493 {
8494 if ((*it)->devId() == e->id
8495 && (*it)->clientId() == u32ClientId)
8496 {
8497 /* The device is already in the list. */
8498 (*it)->dirty(false);
8499 fNewDevice = false;
8500 break;
8501 }
8502 }
8503
8504 if (fNewDevice)
8505 {
8506 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
8507 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
8508
8509 /* Create the device object and add the new device to list. */
8510 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8511 pUSBDevice.createObject();
8512 pUSBDevice->init(u32ClientId, e, fDescExt);
8513
8514 mRemoteUSBDevices.push_back(pUSBDevice);
8515
8516 /* Check if the device is ok for current USB filters. */
8517 BOOL fMatched = FALSE;
8518 ULONG fMaskedIfs = 0;
8519
8520 HRESULT hrc = mControl->RunUSBDeviceFilters(pUSBDevice, &fMatched, &fMaskedIfs);
8521
8522 AssertComRC(hrc);
8523
8524 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
8525
8526 if (fMatched)
8527 {
8528 alock.release();
8529 hrc = onUSBDeviceAttach(pUSBDevice, NULL, fMaskedIfs);
8530 alock.acquire();
8531
8532 /// @todo (r=dmik) warning reporting subsystem
8533
8534 if (hrc == S_OK)
8535 {
8536 LogFlowThisFunc(("Device attached\n"));
8537 pUSBDevice->captured(true);
8538 }
8539 }
8540 }
8541
8542 if (cbDevList < e->oNext)
8543 {
8544 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
8545 cbDevList, e->oNext));
8546 break;
8547 }
8548
8549 cbDevList -= e->oNext;
8550
8551 e = (VRDEUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
8552 }
8553
8554 /*
8555 * Remove dirty devices, that is those which are not reported by the server anymore.
8556 */
8557 for (;;)
8558 {
8559 ComObjPtr<RemoteUSBDevice> pUSBDevice;
8560
8561 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
8562 while (it != mRemoteUSBDevices.end())
8563 {
8564 if ((*it)->dirty())
8565 {
8566 pUSBDevice = *it;
8567 break;
8568 }
8569
8570 ++it;
8571 }
8572
8573 if (!pUSBDevice)
8574 {
8575 break;
8576 }
8577
8578 USHORT vendorId = 0;
8579 pUSBDevice->COMGETTER(VendorId)(&vendorId);
8580
8581 USHORT productId = 0;
8582 pUSBDevice->COMGETTER(ProductId)(&productId);
8583
8584 Bstr product;
8585 pUSBDevice->COMGETTER(Product)(product.asOutParam());
8586
8587 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
8588 vendorId, productId, product.raw()));
8589
8590 /* Detach the device from VM. */
8591 if (pUSBDevice->captured())
8592 {
8593 Bstr uuid;
8594 pUSBDevice->COMGETTER(Id)(uuid.asOutParam());
8595 alock.release();
8596 onUSBDeviceDetach(uuid.raw(), NULL);
8597 alock.acquire();
8598 }
8599
8600 /* And remove it from the list. */
8601 mRemoteUSBDevices.erase(it);
8602 }
8603
8604 LogFlowThisFuncLeave();
8605}
8606
8607/**
8608 * Progress cancelation callback for fault tolerance VM poweron
8609 */
8610static void faultToleranceProgressCancelCallback(void *pvUser)
8611{
8612 PVM pVM = (PVM)pvUser;
8613
8614 if (pVM)
8615 FTMR3CancelStandby(pVM);
8616}
8617
8618/**
8619 * Thread function which starts the VM (also from saved state) and
8620 * track progress.
8621 *
8622 * @param Thread The thread id.
8623 * @param pvUser Pointer to a VMPowerUpTask structure.
8624 * @return VINF_SUCCESS (ignored).
8625 *
8626 * @note Locks the Console object for writing.
8627 */
8628/*static*/
8629DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
8630{
8631 LogFlowFuncEnter();
8632
8633 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
8634 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8635
8636 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
8637 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
8638
8639 VirtualBoxBase::initializeComForThread();
8640
8641 HRESULT rc = S_OK;
8642 int vrc = VINF_SUCCESS;
8643
8644 /* Set up a build identifier so that it can be seen from core dumps what
8645 * exact build was used to produce the core. */
8646 static char saBuildID[40];
8647 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
8648 "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
8649
8650 ComObjPtr<Console> pConsole = task->mConsole;
8651
8652 /* Note: no need to use addCaller() because VMPowerUpTask does that */
8653
8654 /* The lock is also used as a signal from the task initiator (which
8655 * releases it only after RTThreadCreate()) that we can start the job */
8656 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
8657
8658 /* sanity */
8659 Assert(pConsole->mpUVM == NULL);
8660
8661 try
8662 {
8663 // Create the VMM device object, which starts the HGCM thread; do this only
8664 // once for the console, for the pathological case that the same console
8665 // object is used to power up a VM twice. VirtualBox 4.0: we now do that
8666 // here instead of the Console constructor (see Console::init())
8667 if (!pConsole->m_pVMMDev)
8668 {
8669 pConsole->m_pVMMDev = new VMMDev(pConsole);
8670 AssertReturn(pConsole->m_pVMMDev, E_FAIL);
8671 }
8672
8673 /* wait for auto reset ops to complete so that we can successfully lock
8674 * the attached hard disks by calling LockMedia() below */
8675 for (VMPowerUpTask::ProgressList::const_iterator
8676 it = task->hardDiskProgresses.begin();
8677 it != task->hardDiskProgresses.end(); ++it)
8678 {
8679 HRESULT rc2 = (*it)->WaitForCompletion(-1);
8680 AssertComRC(rc2);
8681 }
8682
8683 /*
8684 * Lock attached media. This method will also check their accessibility.
8685 * If we're a teleporter, we'll have to postpone this action so we can
8686 * migrate between local processes.
8687 *
8688 * Note! The media will be unlocked automatically by
8689 * SessionMachine::setMachineState() when the VM is powered down.
8690 */
8691 if ( !task->mTeleporterEnabled
8692 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
8693 {
8694 rc = pConsole->mControl->LockMedia();
8695 if (FAILED(rc)) throw rc;
8696 }
8697
8698 /* Create the VRDP server. In case of headless operation, this will
8699 * also create the framebuffer, required at VM creation.
8700 */
8701 ConsoleVRDPServer *server = pConsole->consoleVRDPServer();
8702 Assert(server);
8703
8704 /* Does VRDP server call Console from the other thread?
8705 * Not sure (and can change), so release the lock just in case.
8706 */
8707 alock.release();
8708 vrc = server->Launch();
8709 alock.acquire();
8710
8711 if (vrc == VERR_NET_ADDRESS_IN_USE)
8712 {
8713 Utf8Str errMsg;
8714 Bstr bstr;
8715 pConsole->mVRDEServer->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
8716 Utf8Str ports = bstr;
8717 errMsg = Utf8StrFmt(tr("VirtualBox Remote Desktop Extension server can't bind to the port: %s"),
8718 ports.c_str());
8719 LogRel(("VRDE: Warning: failed to launch VRDE server (%Rrc): '%s'\n",
8720 vrc, errMsg.c_str()));
8721 }
8722 else if (vrc == VINF_NOT_SUPPORTED)
8723 {
8724 /* This means that the VRDE is not installed. */
8725 LogRel(("VRDE: VirtualBox Remote Desktop Extension is not available.\n"));
8726 }
8727 else if (RT_FAILURE(vrc))
8728 {
8729 /* Fail, if the server is installed but can't start. */
8730 Utf8Str errMsg;
8731 switch (vrc)
8732 {
8733 case VERR_FILE_NOT_FOUND:
8734 {
8735 /* VRDE library file is missing. */
8736 errMsg = Utf8StrFmt(tr("Could not find the VirtualBox Remote Desktop Extension library."));
8737 break;
8738 }
8739 default:
8740 errMsg = Utf8StrFmt(tr("Failed to launch Remote Desktop Extension server (%Rrc)"),
8741 vrc);
8742 }
8743 LogRel(("VRDE: Failed: (%Rrc), error message: '%s'\n",
8744 vrc, errMsg.c_str()));
8745 throw setErrorStatic(E_FAIL, errMsg.c_str());
8746 }
8747
8748 ComPtr<IMachine> pMachine = pConsole->machine();
8749 ULONG cCpus = 1;
8750 pMachine->COMGETTER(CPUCount)(&cCpus);
8751
8752 /*
8753 * Create the VM
8754 */
8755 PVM pVM;
8756 /*
8757 * release the lock since EMT will call Console. It's safe because
8758 * mMachineState is either Starting or Restoring state here.
8759 */
8760 alock.release();
8761
8762 vrc = VMR3Create(cCpus,
8763 pConsole->mpVmm2UserMethods,
8764 Console::genericVMSetErrorCallback,
8765 &task->mErrorMsg,
8766 task->mConfigConstructor,
8767 static_cast<Console *>(pConsole),
8768 &pVM);
8769
8770 alock.acquire();
8771
8772 /* Enable client connections to the server. */
8773 pConsole->consoleVRDPServer()->EnableConnections();
8774
8775 if (RT_SUCCESS(vrc))
8776 {
8777 do
8778 {
8779 /*
8780 * Register our load/save state file handlers
8781 */
8782 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
8783 NULL, NULL, NULL,
8784 NULL, saveStateFileExec, NULL,
8785 NULL, loadStateFileExec, NULL,
8786 static_cast<Console *>(pConsole));
8787 AssertRCBreak(vrc);
8788
8789 vrc = static_cast<Console *>(pConsole)->getDisplay()->registerSSM(pVM);
8790 AssertRC(vrc);
8791 if (RT_FAILURE(vrc))
8792 break;
8793
8794 /*
8795 * Synchronize debugger settings
8796 */
8797 MachineDebugger *machineDebugger = pConsole->getMachineDebugger();
8798 if (machineDebugger)
8799 machineDebugger->flushQueuedSettings();
8800
8801 /*
8802 * Shared Folders
8803 */
8804 if (pConsole->m_pVMMDev->isShFlActive())
8805 {
8806 /* Does the code below call Console from the other thread?
8807 * Not sure, so release the lock just in case. */
8808 alock.release();
8809
8810 for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
8811 it != task->mSharedFolders.end();
8812 ++it)
8813 {
8814 const SharedFolderData &d = it->second;
8815 rc = pConsole->createSharedFolder(it->first, d);
8816 if (FAILED(rc))
8817 {
8818 ErrorInfoKeeper eik;
8819 setVMRuntimeErrorCallbackF(pVM, pConsole, 0, "BrokenSharedFolder",
8820 N_("The shared folder '%s' could not be set up: %ls.\n"
8821 "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"),
8822 it->first.c_str(), eik.getText().raw());
8823 }
8824 }
8825 if (FAILED(rc))
8826 rc = S_OK; // do not fail with broken shared folders
8827
8828 /* acquire the lock again */
8829 alock.acquire();
8830 }
8831
8832 /* release the lock before a lengthy operation */
8833 alock.release();
8834
8835 /*
8836 * Capture USB devices.
8837 */
8838 rc = pConsole->captureUSBDevices(pVM);
8839 if (FAILED(rc)) break;
8840
8841 /* Load saved state? */
8842 if (task->mSavedStateFile.length())
8843 {
8844 LogFlowFunc(("Restoring saved state from '%s'...\n",
8845 task->mSavedStateFile.c_str()));
8846
8847 vrc = VMR3LoadFromFile(pVM,
8848 task->mSavedStateFile.c_str(),
8849 Console::stateProgressCallback,
8850 static_cast<IProgress *>(task->mProgress));
8851
8852 if (RT_SUCCESS(vrc))
8853 {
8854 if (task->mStartPaused)
8855 /* done */
8856 pConsole->setMachineState(MachineState_Paused);
8857 else
8858 {
8859 /* Start/Resume the VM execution */
8860#ifdef VBOX_WITH_EXTPACK
8861 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8862#endif
8863 if (RT_SUCCESS(vrc))
8864 vrc = VMR3Resume(pVM);
8865 AssertLogRelRC(vrc);
8866 }
8867 }
8868
8869 /* Power off in case we failed loading or resuming the VM */
8870 if (RT_FAILURE(vrc))
8871 {
8872 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8873#ifdef VBOX_WITH_EXTPACK
8874 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8875#endif
8876 }
8877 }
8878 else if (task->mTeleporterEnabled)
8879 {
8880 /* -> ConsoleImplTeleporter.cpp */
8881 bool fPowerOffOnFailure;
8882 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8883 task->mProgress, &fPowerOffOnFailure);
8884 if (FAILED(rc) && fPowerOffOnFailure)
8885 {
8886 ErrorInfoKeeper eik;
8887 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8888#ifdef VBOX_WITH_EXTPACK
8889 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8890#endif
8891 }
8892 }
8893 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8894 {
8895 /*
8896 * Get the config.
8897 */
8898 ULONG uPort;
8899 ULONG uInterval;
8900 Bstr bstrAddress, bstrPassword;
8901
8902 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8903 if (SUCCEEDED(rc))
8904 {
8905 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8906 if (SUCCEEDED(rc))
8907 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8908 if (SUCCEEDED(rc))
8909 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8910 }
8911 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8912 {
8913 if (SUCCEEDED(rc))
8914 {
8915 Utf8Str strAddress(bstrAddress);
8916 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8917 Utf8Str strPassword(bstrPassword);
8918 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8919
8920 /* Power on the FT enabled VM. */
8921#ifdef VBOX_WITH_EXTPACK
8922 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8923#endif
8924 if (RT_SUCCESS(vrc))
8925 vrc = FTMR3PowerOn(pVM,
8926 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8927 uInterval,
8928 pszAddress,
8929 uPort,
8930 pszPassword);
8931 AssertLogRelRC(vrc);
8932 }
8933 task->mProgress->setCancelCallback(NULL, NULL);
8934 }
8935 else
8936 rc = E_FAIL;
8937 }
8938 else if (task->mStartPaused)
8939 /* done */
8940 pConsole->setMachineState(MachineState_Paused);
8941 else
8942 {
8943 /* Power on the VM (i.e. start executing) */
8944#ifdef VBOX_WITH_EXTPACK
8945 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8946#endif
8947 if (RT_SUCCESS(vrc))
8948 vrc = VMR3PowerOn(pVM);
8949 AssertLogRelRC(vrc);
8950 }
8951
8952 /* acquire the lock again */
8953 alock.acquire();
8954 }
8955 while (0);
8956
8957 /* On failure, destroy the VM */
8958 if (FAILED(rc) || RT_FAILURE(vrc))
8959 {
8960 /* preserve existing error info */
8961 ErrorInfoKeeper eik;
8962
8963 /* powerDown() will call VMR3Destroy() and do all necessary
8964 * cleanup (VRDP, USB devices) */
8965 alock.release();
8966 HRESULT rc2 = pConsole->powerDown();
8967 alock.acquire();
8968 AssertComRC(rc2);
8969 }
8970 else
8971 {
8972 /*
8973 * Deregister the VMSetError callback. This is necessary as the
8974 * pfnVMAtError() function passed to VMR3Create() is supposed to
8975 * be sticky but our error callback isn't.
8976 */
8977 alock.release();
8978 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8979 /** @todo register another VMSetError callback? */
8980 alock.acquire();
8981 }
8982 }
8983 else
8984 {
8985 /*
8986 * If VMR3Create() failed it has released the VM memory.
8987 */
8988 VMR3ReleaseUVM(pConsole->mpUVM);
8989 pConsole->mpUVM = NULL;
8990 }
8991
8992 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8993 {
8994 /* If VMR3Create() or one of the other calls in this function fail,
8995 * an appropriate error message has been set in task->mErrorMsg.
8996 * However since that happens via a callback, the rc status code in
8997 * this function is not updated.
8998 */
8999 if (!task->mErrorMsg.length())
9000 {
9001 /* If the error message is not set but we've got a failure,
9002 * convert the VBox status code into a meaningful error message.
9003 * This becomes unused once all the sources of errors set the
9004 * appropriate error message themselves.
9005 */
9006 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
9007 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
9008 vrc);
9009 }
9010
9011 /* Set the error message as the COM error.
9012 * Progress::notifyComplete() will pick it up later. */
9013 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
9014 }
9015 }
9016 catch (HRESULT aRC) { rc = aRC; }
9017
9018 if ( pConsole->mMachineState == MachineState_Starting
9019 || pConsole->mMachineState == MachineState_Restoring
9020 || pConsole->mMachineState == MachineState_TeleportingIn
9021 )
9022 {
9023 /* We are still in the Starting/Restoring state. This means one of:
9024 *
9025 * 1) we failed before VMR3Create() was called;
9026 * 2) VMR3Create() failed.
9027 *
9028 * In both cases, there is no need to call powerDown(), but we still
9029 * need to go back to the PoweredOff/Saved state. Reuse
9030 * vmstateChangeCallback() for that purpose.
9031 */
9032
9033 /* preserve existing error info */
9034 ErrorInfoKeeper eik;
9035
9036 Assert(pConsole->mpUVM == NULL);
9037 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
9038 pConsole);
9039 }
9040
9041 /*
9042 * Evaluate the final result. Note that the appropriate mMachineState value
9043 * is already set by vmstateChangeCallback() in all cases.
9044 */
9045
9046 /* release the lock, don't need it any more */
9047 alock.release();
9048
9049 if (SUCCEEDED(rc))
9050 {
9051 /* Notify the progress object of the success */
9052 task->mProgress->notifyComplete(S_OK);
9053 }
9054 else
9055 {
9056 /* The progress object will fetch the current error info */
9057 task->mProgress->notifyComplete(rc);
9058 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
9059 }
9060
9061 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
9062 pConsole->mControl->EndPowerUp(rc);
9063
9064#if defined(RT_OS_WINDOWS)
9065 /* uninitialize COM */
9066 CoUninitialize();
9067#endif
9068
9069 LogFlowFuncLeave();
9070
9071 return VINF_SUCCESS;
9072}
9073
9074
9075/**
9076 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
9077 *
9078 * @param pConsole Reference to the console object.
9079 * @param pVM The VM handle.
9080 * @param lInstance The instance of the controller.
9081 * @param pcszDevice The name of the controller type.
9082 * @param enmBus The storage bus type of the controller.
9083 * @param fSetupMerge Whether to set up a medium merge
9084 * @param uMergeSource Merge source image index
9085 * @param uMergeTarget Merge target image index
9086 * @param aMediumAtt The medium attachment.
9087 * @param aMachineState The current machine state.
9088 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
9089 * @return VBox status code.
9090 */
9091/* static */
9092DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
9093 PVM pVM,
9094 const char *pcszDevice,
9095 unsigned uInstance,
9096 StorageBus_T enmBus,
9097 bool fUseHostIOCache,
9098 bool fBuiltinIOCache,
9099 bool fSetupMerge,
9100 unsigned uMergeSource,
9101 unsigned uMergeTarget,
9102 IMediumAttachment *aMediumAtt,
9103 MachineState_T aMachineState,
9104 HRESULT *phrc)
9105{
9106 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
9107
9108 int rc;
9109 HRESULT hrc;
9110 Bstr bstr;
9111 *phrc = S_OK;
9112#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
9113#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
9114
9115 /* Ignore attachments other than hard disks, since at the moment they are
9116 * not subject to snapshotting in general. */
9117 DeviceType_T lType;
9118 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
9119 if (lType != DeviceType_HardDisk)
9120 return VINF_SUCCESS;
9121
9122 /* Determine the base path for the device instance. */
9123 PCFGMNODE pCtlInst;
9124 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
9125 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
9126
9127 /* Update the device instance configuration. */
9128 rc = pConsole->configMediumAttachment(pCtlInst,
9129 pcszDevice,
9130 uInstance,
9131 enmBus,
9132 fUseHostIOCache,
9133 fBuiltinIOCache,
9134 fSetupMerge,
9135 uMergeSource,
9136 uMergeTarget,
9137 aMediumAtt,
9138 aMachineState,
9139 phrc,
9140 true /* fAttachDetach */,
9141 false /* fForceUnmount */,
9142 false /* fHotplug */,
9143 pVM,
9144 NULL /* paLedDevType */);
9145 /** @todo this dumps everything attached to this device instance, which
9146 * is more than necessary. Dumping the changed LUN would be enough. */
9147 CFGMR3Dump(pCtlInst);
9148 RC_CHECK();
9149
9150#undef RC_CHECK
9151#undef H
9152
9153 LogFlowFunc(("Returns success\n"));
9154 return VINF_SUCCESS;
9155}
9156
9157/**
9158 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
9159 */
9160static void takesnapshotProgressCancelCallback(void *pvUser)
9161{
9162 PUVM pUVM = (PUVM)pvUser;
9163 SSMR3Cancel(VMR3GetVM(pUVM));
9164}
9165
9166/**
9167 * Worker thread created by Console::TakeSnapshot.
9168 * @param Thread The current thread (ignored).
9169 * @param pvUser The task.
9170 * @return VINF_SUCCESS (ignored).
9171 */
9172/*static*/
9173DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
9174{
9175 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
9176
9177 // taking a snapshot consists of the following:
9178
9179 // 1) creating a diff image for each virtual hard disk, into which write operations go after
9180 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
9181 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
9182 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
9183 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
9184
9185 Console *that = pTask->mConsole;
9186 bool fBeganTakingSnapshot = false;
9187 bool fSuspenededBySave = false;
9188
9189 AutoCaller autoCaller(that);
9190 if (FAILED(autoCaller.rc()))
9191 {
9192 that->mptrCancelableProgress.setNull();
9193 return autoCaller.rc();
9194 }
9195
9196 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
9197
9198 HRESULT rc = S_OK;
9199
9200 try
9201 {
9202 /* STEP 1 + 2:
9203 * request creating the diff images on the server and create the snapshot object
9204 * (this will set the machine state to Saving on the server to block
9205 * others from accessing this machine)
9206 */
9207 rc = that->mControl->BeginTakingSnapshot(that,
9208 pTask->bstrName.raw(),
9209 pTask->bstrDescription.raw(),
9210 pTask->mProgress,
9211 pTask->fTakingSnapshotOnline,
9212 pTask->bstrSavedStateFile.asOutParam());
9213 if (FAILED(rc))
9214 throw rc;
9215
9216 fBeganTakingSnapshot = true;
9217
9218 /*
9219 * state file is non-null only when the VM is paused
9220 * (i.e. creating a snapshot online)
9221 */
9222 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
9223 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
9224 if (!f)
9225 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
9226
9227 /* sync the state with the server */
9228 if (pTask->lastMachineState == MachineState_Running)
9229 that->setMachineStateLocally(MachineState_LiveSnapshotting);
9230 else
9231 that->setMachineStateLocally(MachineState_Saving);
9232
9233 // STEP 3: save the VM state (if online)
9234 if (pTask->fTakingSnapshotOnline)
9235 {
9236 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
9237
9238 SafeVMPtr ptrVM(that);
9239 if (!ptrVM.isOk())
9240 throw ptrVM.rc();
9241
9242 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
9243 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
9244 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
9245
9246 alock.release();
9247 LogFlowFunc(("VMR3Save...\n"));
9248 int vrc = VMR3Save(ptrVM,
9249 strSavedStateFile.c_str(),
9250 true /*fContinueAfterwards*/,
9251 Console::stateProgressCallback,
9252 static_cast<IProgress *>(pTask->mProgress),
9253 &fSuspenededBySave);
9254 alock.acquire();
9255 if (RT_FAILURE(vrc))
9256 throw setErrorStatic(E_FAIL,
9257 tr("Failed to save the machine state to '%s' (%Rrc)"),
9258 strSavedStateFile.c_str(), vrc);
9259
9260 pTask->mProgress->setCancelCallback(NULL, NULL);
9261 if (!pTask->mProgress->notifyPointOfNoReturn())
9262 throw setErrorStatic(E_FAIL, tr("Canceled"));
9263 that->mptrCancelableProgress.setNull();
9264
9265 // STEP 4: reattach hard disks
9266 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
9267
9268 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
9269 1); // operation weight, same as computed when setting up progress object
9270
9271 com::SafeIfaceArray<IMediumAttachment> atts;
9272 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
9273 if (FAILED(rc))
9274 throw rc;
9275
9276 for (size_t i = 0;
9277 i < atts.size();
9278 ++i)
9279 {
9280 ComPtr<IStorageController> pStorageController;
9281 Bstr controllerName;
9282 ULONG lInstance;
9283 StorageControllerType_T enmController;
9284 StorageBus_T enmBus;
9285 BOOL fUseHostIOCache;
9286
9287 /*
9288 * We can't pass a storage controller object directly
9289 * (g++ complains about not being able to pass non POD types through '...')
9290 * so we have to query needed values here and pass them.
9291 */
9292 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
9293 if (FAILED(rc))
9294 throw rc;
9295
9296 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
9297 pStorageController.asOutParam());
9298 if (FAILED(rc))
9299 throw rc;
9300
9301 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
9302 if (FAILED(rc))
9303 throw rc;
9304 rc = pStorageController->COMGETTER(Instance)(&lInstance);
9305 if (FAILED(rc))
9306 throw rc;
9307 rc = pStorageController->COMGETTER(Bus)(&enmBus);
9308 if (FAILED(rc))
9309 throw rc;
9310 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9311 if (FAILED(rc))
9312 throw rc;
9313
9314 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
9315
9316 BOOL fBuiltinIOCache;
9317 rc = that->mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
9318 if (FAILED(rc))
9319 throw rc;
9320
9321 /*
9322 * don't release the lock since reconfigureMediumAttachment
9323 * isn't going to need the Console lock.
9324 */
9325 vrc = VMR3ReqCallWait(ptrVM,
9326 VMCPUID_ANY,
9327 (PFNRT)reconfigureMediumAttachment,
9328 13,
9329 that,
9330 ptrVM.raw(),
9331 pcszDevice,
9332 lInstance,
9333 enmBus,
9334 fUseHostIOCache,
9335 fBuiltinIOCache,
9336 false /* fSetupMerge */,
9337 0 /* uMergeSource */,
9338 0 /* uMergeTarget */,
9339 atts[i],
9340 that->mMachineState,
9341 &rc);
9342 if (RT_FAILURE(vrc))
9343 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
9344 if (FAILED(rc))
9345 throw rc;
9346 }
9347 }
9348
9349 /*
9350 * finalize the requested snapshot object.
9351 * This will reset the machine state to the state it had right
9352 * before calling mControl->BeginTakingSnapshot().
9353 */
9354 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
9355 // do not throw rc here because we can't call EndTakingSnapshot() twice
9356 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9357 }
9358 catch (HRESULT rcThrown)
9359 {
9360 /* preserve existing error info */
9361 ErrorInfoKeeper eik;
9362
9363 if (fBeganTakingSnapshot)
9364 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
9365
9366 rc = rcThrown;
9367 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
9368 }
9369 Assert(alock.isWriteLockOnCurrentThread());
9370
9371 if (FAILED(rc)) /* Must come before calling setMachineState. */
9372 pTask->mProgress->notifyComplete(rc);
9373
9374 /*
9375 * Fix up the machine state.
9376 *
9377 * For live snapshots we do all the work, for the two other variations we
9378 * just update the local copy.
9379 */
9380 MachineState_T enmMachineState;
9381 that->mMachine->COMGETTER(State)(&enmMachineState);
9382 if ( that->mMachineState == MachineState_LiveSnapshotting
9383 || that->mMachineState == MachineState_Saving)
9384 {
9385
9386 if (!pTask->fTakingSnapshotOnline)
9387 that->setMachineStateLocally(pTask->lastMachineState);
9388 else if (SUCCEEDED(rc))
9389 {
9390 Assert( pTask->lastMachineState == MachineState_Running
9391 || pTask->lastMachineState == MachineState_Paused);
9392 Assert(that->mMachineState == MachineState_Saving);
9393 if (pTask->lastMachineState == MachineState_Running)
9394 {
9395 LogFlowFunc(("VMR3Resume...\n"));
9396 SafeVMPtr ptrVM(that);
9397 alock.release();
9398 int vrc = VMR3Resume(ptrVM);
9399 alock.acquire();
9400 if (RT_FAILURE(vrc))
9401 {
9402 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
9403 pTask->mProgress->notifyComplete(rc);
9404 if (that->mMachineState == MachineState_Saving)
9405 that->setMachineStateLocally(MachineState_Paused);
9406 }
9407 }
9408 else
9409 that->setMachineStateLocally(MachineState_Paused);
9410 }
9411 else
9412 {
9413 /** @todo this could probably be made more generic and reused elsewhere. */
9414 /* paranoid cleanup on for a failed online snapshot. */
9415 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
9416 switch (enmVMState)
9417 {
9418 case VMSTATE_RUNNING:
9419 case VMSTATE_RUNNING_LS:
9420 case VMSTATE_DEBUGGING:
9421 case VMSTATE_DEBUGGING_LS:
9422 case VMSTATE_POWERING_OFF:
9423 case VMSTATE_POWERING_OFF_LS:
9424 case VMSTATE_RESETTING:
9425 case VMSTATE_RESETTING_LS:
9426 Assert(!fSuspenededBySave);
9427 that->setMachineState(MachineState_Running);
9428 break;
9429
9430 case VMSTATE_GURU_MEDITATION:
9431 case VMSTATE_GURU_MEDITATION_LS:
9432 that->setMachineState(MachineState_Stuck);
9433 break;
9434
9435 case VMSTATE_FATAL_ERROR:
9436 case VMSTATE_FATAL_ERROR_LS:
9437 if (pTask->lastMachineState == MachineState_Paused)
9438 that->setMachineStateLocally(pTask->lastMachineState);
9439 else
9440 that->setMachineState(MachineState_Paused);
9441 break;
9442
9443 default:
9444 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
9445 case VMSTATE_SUSPENDED:
9446 case VMSTATE_SUSPENDED_LS:
9447 case VMSTATE_SUSPENDING:
9448 case VMSTATE_SUSPENDING_LS:
9449 case VMSTATE_SUSPENDING_EXT_LS:
9450 if (fSuspenededBySave)
9451 {
9452 Assert(pTask->lastMachineState == MachineState_Running);
9453 LogFlowFunc(("VMR3Resume (on failure)...\n"));
9454 SafeVMPtr ptrVM(that);
9455 alock.release();
9456 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
9457 alock.acquire();
9458 if (RT_FAILURE(vrc))
9459 that->setMachineState(MachineState_Paused);
9460 }
9461 else if (pTask->lastMachineState == MachineState_Paused)
9462 that->setMachineStateLocally(pTask->lastMachineState);
9463 else
9464 that->setMachineState(MachineState_Paused);
9465 break;
9466 }
9467
9468 }
9469 }
9470 /*else: somebody else has change the state... Leave it. */
9471
9472 /* check the remote state to see that we got it right. */
9473 that->mMachine->COMGETTER(State)(&enmMachineState);
9474 AssertLogRelMsg(that->mMachineState == enmMachineState,
9475 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
9476 Global::stringifyMachineState(enmMachineState) ));
9477
9478
9479 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
9480 pTask->mProgress->notifyComplete(rc);
9481
9482 delete pTask;
9483
9484 LogFlowFuncLeave();
9485 return VINF_SUCCESS;
9486}
9487
9488/**
9489 * Thread for executing the saved state operation.
9490 *
9491 * @param Thread The thread handle.
9492 * @param pvUser Pointer to a VMSaveTask structure.
9493 * @return VINF_SUCCESS (ignored).
9494 *
9495 * @note Locks the Console object for writing.
9496 */
9497/*static*/
9498DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
9499{
9500 LogFlowFuncEnter();
9501
9502 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
9503 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9504
9505 Assert(task->mSavedStateFile.length());
9506 Assert(task->mProgress.isNull());
9507 Assert(!task->mServerProgress.isNull());
9508
9509 const ComObjPtr<Console> &that = task->mConsole;
9510 Utf8Str errMsg;
9511 HRESULT rc = S_OK;
9512
9513 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
9514
9515 bool fSuspenededBySave;
9516 int vrc = VMR3Save(task->mpVM,
9517 task->mSavedStateFile.c_str(),
9518 false, /*fContinueAfterwards*/
9519 Console::stateProgressCallback,
9520 static_cast<IProgress *>(task->mServerProgress),
9521 &fSuspenededBySave);
9522 if (RT_FAILURE(vrc))
9523 {
9524 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
9525 task->mSavedStateFile.c_str(), vrc);
9526 rc = E_FAIL;
9527 }
9528 Assert(!fSuspenededBySave);
9529
9530 /* lock the console once we're going to access it */
9531 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9532
9533 /* synchronize the state with the server */
9534 if (SUCCEEDED(rc))
9535 {
9536 /*
9537 * The machine has been successfully saved, so power it down
9538 * (vmstateChangeCallback() will set state to Saved on success).
9539 * Note: we release the task's VM caller, otherwise it will
9540 * deadlock.
9541 */
9542 task->releaseVMCaller();
9543 thatLock.release();
9544 rc = that->powerDown();
9545 thatLock.acquire();
9546 }
9547
9548 /*
9549 * If we failed, reset the local machine state.
9550 */
9551 if (FAILED(rc))
9552 that->setMachineStateLocally(task->mMachineStateBefore);
9553
9554 /*
9555 * Finalize the requested save state procedure. In case of failure it will
9556 * reset the machine state to the state it had right before calling
9557 * mControl->BeginSavingState(). This must be the last thing because it
9558 * will set the progress to completed, and that means that the frontend
9559 * can immediately uninit the associated console object.
9560 */
9561 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
9562
9563 LogFlowFuncLeave();
9564 return VINF_SUCCESS;
9565}
9566
9567/**
9568 * Thread for powering down the Console.
9569 *
9570 * @param Thread The thread handle.
9571 * @param pvUser Pointer to the VMTask structure.
9572 * @return VINF_SUCCESS (ignored).
9573 *
9574 * @note Locks the Console object for writing.
9575 */
9576/*static*/
9577DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
9578{
9579 LogFlowFuncEnter();
9580
9581 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
9582 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
9583
9584 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
9585
9586 Assert(task->mProgress.isNull());
9587
9588 const ComObjPtr<Console> &that = task->mConsole;
9589
9590 /* Note: no need to use addCaller() to protect Console because VMTask does
9591 * that */
9592
9593 /* wait until the method tat started us returns */
9594 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
9595
9596 /* release VM caller to avoid the powerDown() deadlock */
9597 task->releaseVMCaller();
9598
9599 thatLock.release();
9600
9601 that->powerDown(task->mServerProgress);
9602
9603 /* complete the operation */
9604 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
9605
9606 LogFlowFuncLeave();
9607 return VINF_SUCCESS;
9608}
9609
9610
9611/**
9612 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
9613 */
9614/*static*/ DECLCALLBACK(int)
9615Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
9616{
9617 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
9618 NOREF(pUVM);
9619
9620 /*
9621 * For now, just call SaveState. We should probably try notify the GUI so
9622 * it can pop up a progress object and stuff.
9623 */
9624 HRESULT hrc = pConsole->SaveState(NULL);
9625 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
9626}
9627
9628/**
9629 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
9630 */
9631/*static*/ DECLCALLBACK(void)
9632Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9633{
9634 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9635 VirtualBoxBase::initializeComForThread();
9636}
9637
9638/**
9639 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
9640 */
9641/*static*/ DECLCALLBACK(void)
9642Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
9643{
9644 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
9645 VirtualBoxBase::uninitializeComForThread();
9646}
9647
9648/**
9649 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
9650 */
9651/*static*/ DECLCALLBACK(void)
9652Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
9653{
9654 NOREF(pThis); NOREF(pUVM);
9655 VirtualBoxBase::initializeComForThread();
9656}
9657
9658/**
9659 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
9660 */
9661/*static*/ DECLCALLBACK(void)
9662Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
9663{
9664 NOREF(pThis); NOREF(pUVM);
9665 VirtualBoxBase::uninitializeComForThread();
9666}
9667
9668
9669
9670
9671/**
9672 * The Main status driver instance data.
9673 */
9674typedef struct DRVMAINSTATUS
9675{
9676 /** The LED connectors. */
9677 PDMILEDCONNECTORS ILedConnectors;
9678 /** Pointer to the LED ports interface above us. */
9679 PPDMILEDPORTS pLedPorts;
9680 /** Pointer to the array of LED pointers. */
9681 PPDMLED *papLeds;
9682 /** The unit number corresponding to the first entry in the LED array. */
9683 RTUINT iFirstLUN;
9684 /** The unit number corresponding to the last entry in the LED array.
9685 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
9686 RTUINT iLastLUN;
9687 /** Pointer to the driver instance. */
9688 PPDMDRVINS pDrvIns;
9689 /** The Media Notify interface. */
9690 PDMIMEDIANOTIFY IMediaNotify;
9691 /** Map for translating PDM storage controller/LUN information to
9692 * IMediumAttachment references. */
9693 Console::MediumAttachmentMap *pmapMediumAttachments;
9694 /** Device name+instance for mapping */
9695 char *pszDeviceInstance;
9696 /** Pointer to the Console object, for driver triggered activities. */
9697 Console *pConsole;
9698} DRVMAINSTATUS, *PDRVMAINSTATUS;
9699
9700
9701/**
9702 * Notification about a unit which have been changed.
9703 *
9704 * The driver must discard any pointers to data owned by
9705 * the unit and requery it.
9706 *
9707 * @param pInterface Pointer to the interface structure containing the called function pointer.
9708 * @param iLUN The unit number.
9709 */
9710DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
9711{
9712 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors));
9713 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
9714 {
9715 PPDMLED pLed;
9716 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
9717 if (RT_FAILURE(rc))
9718 pLed = NULL;
9719 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
9720 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
9721 }
9722}
9723
9724
9725/**
9726 * Notification about a medium eject.
9727 *
9728 * @returns VBox status.
9729 * @param pInterface Pointer to the interface structure containing the called function pointer.
9730 * @param uLUN The unit number.
9731 */
9732DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN)
9733{
9734 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify));
9735 PPDMDRVINS pDrvIns = pData->pDrvIns;
9736 LogFunc(("uLUN=%d\n", uLUN));
9737 if (pData->pmapMediumAttachments)
9738 {
9739 AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS);
9740
9741 ComPtr<IMediumAttachment> pMediumAtt;
9742 Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN);
9743 Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end();
9744 Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath);
9745 if (it != end)
9746 pMediumAtt = it->second;
9747 Assert(!pMediumAtt.isNull());
9748 if (!pMediumAtt.isNull())
9749 {
9750 IMedium *pMedium = NULL;
9751 HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium);
9752 AssertComRC(rc);
9753 if (SUCCEEDED(rc) && pMedium)
9754 {
9755 BOOL fHostDrive = FALSE;
9756 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
9757 AssertComRC(rc);
9758 if (!fHostDrive)
9759 {
9760 alock.release();
9761
9762 ComPtr<IMediumAttachment> pNewMediumAtt;
9763 rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam());
9764 if (SUCCEEDED(rc))
9765 fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt);
9766
9767 alock.acquire();
9768 if (pNewMediumAtt != pMediumAtt)
9769 {
9770 pData->pmapMediumAttachments->erase(devicePath);
9771 pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt));
9772 }
9773 }
9774 }
9775 }
9776 }
9777 return VINF_SUCCESS;
9778}
9779
9780
9781/**
9782 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
9783 */
9784DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
9785{
9786 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
9787 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9788 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
9789 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
9790 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify);
9791 return NULL;
9792}
9793
9794
9795/**
9796 * Destruct a status driver instance.
9797 *
9798 * @returns VBox status.
9799 * @param pDrvIns The driver instance data.
9800 */
9801DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
9802{
9803 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9804 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9805 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
9806
9807 if (pData->papLeds)
9808 {
9809 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
9810 while (iLed-- > 0)
9811 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
9812 }
9813}
9814
9815
9816/**
9817 * Construct a status driver instance.
9818 *
9819 * @copydoc FNPDMDRVCONSTRUCT
9820 */
9821DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
9822{
9823 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
9824 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
9825 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
9826
9827 /*
9828 * Validate configuration.
9829 */
9830 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0"))
9831 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
9832 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
9833 ("Configuration error: Not possible to attach anything to this driver!\n"),
9834 VERR_PDM_DRVINS_NO_ATTACH);
9835
9836 /*
9837 * Data.
9838 */
9839 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
9840 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
9841 pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected;
9842 pData->pDrvIns = pDrvIns;
9843 pData->pszDeviceInstance = NULL;
9844
9845 /*
9846 * Read config.
9847 */
9848 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
9849 if (RT_FAILURE(rc))
9850 {
9851 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
9852 return rc;
9853 }
9854
9855 rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL);
9856 if (RT_FAILURE(rc))
9857 {
9858 AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc));
9859 return rc;
9860 }
9861 if (pData->pmapMediumAttachments)
9862 {
9863 rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance);
9864 if (RT_FAILURE(rc))
9865 {
9866 AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc));
9867 return rc;
9868 }
9869 rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole);
9870 if (RT_FAILURE(rc))
9871 {
9872 AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc));
9873 return rc;
9874 }
9875 }
9876
9877 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
9878 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9879 pData->iFirstLUN = 0;
9880 else if (RT_FAILURE(rc))
9881 {
9882 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
9883 return rc;
9884 }
9885
9886 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
9887 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
9888 pData->iLastLUN = 0;
9889 else if (RT_FAILURE(rc))
9890 {
9891 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
9892 return rc;
9893 }
9894 if (pData->iFirstLUN > pData->iLastLUN)
9895 {
9896 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
9897 return VERR_GENERAL_FAILURE;
9898 }
9899
9900 /*
9901 * Get the ILedPorts interface of the above driver/device and
9902 * query the LEDs we want.
9903 */
9904 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
9905 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
9906 VERR_PDM_MISSING_INTERFACE_ABOVE);
9907
9908 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
9909 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9910
9911 return VINF_SUCCESS;
9912}
9913
9914
9915/**
9916 * Console status driver (LED) registration record.
9917 */
9918const PDMDRVREG Console::DrvStatusReg =
9919{
9920 /* u32Version */
9921 PDM_DRVREG_VERSION,
9922 /* szName */
9923 "MainStatus",
9924 /* szRCMod */
9925 "",
9926 /* szR0Mod */
9927 "",
9928 /* pszDescription */
9929 "Main status driver (Main as in the API).",
9930 /* fFlags */
9931 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9932 /* fClass. */
9933 PDM_DRVREG_CLASS_STATUS,
9934 /* cMaxInstances */
9935 ~0U,
9936 /* cbInstance */
9937 sizeof(DRVMAINSTATUS),
9938 /* pfnConstruct */
9939 Console::drvStatus_Construct,
9940 /* pfnDestruct */
9941 Console::drvStatus_Destruct,
9942 /* pfnRelocate */
9943 NULL,
9944 /* pfnIOCtl */
9945 NULL,
9946 /* pfnPowerOn */
9947 NULL,
9948 /* pfnReset */
9949 NULL,
9950 /* pfnSuspend */
9951 NULL,
9952 /* pfnResume */
9953 NULL,
9954 /* pfnAttach */
9955 NULL,
9956 /* pfnDetach */
9957 NULL,
9958 /* pfnPowerOff */
9959 NULL,
9960 /* pfnSoftReset */
9961 NULL,
9962 /* u32EndVersion */
9963 PDM_DRVREG_VERSION
9964};
9965
9966/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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