VirtualBox

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

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

Build fix

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