VirtualBox

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

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

Main: move handleUnexpectedExceptions method to VirtualBoxBase

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