VirtualBox

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

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

Disabled the mCallbackData cache until it's purpose has been found.

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

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