VirtualBox

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

Last change on this file since 36976 was 36976, checked in by vboxsync, 14 years ago

Main: when mounting shared folders, don't break the loop if one folder fails

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