VirtualBox

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

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

Main: Sanitize incoming VRDP USB descriptor strings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 298.8 KB
Line 
1/* $Id: ConsoleImpl.cpp 36481 2011-03-31 14:11:14Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @todo Move the TAP mess back into the driver! */
19#if defined(RT_OS_WINDOWS)
20#elif defined(RT_OS_LINUX)
21# include <errno.h>
22# include <sys/ioctl.h>
23# include <sys/poll.h>
24# include <sys/fcntl.h>
25# include <sys/types.h>
26# include <sys/wait.h>
27# include <net/if.h>
28# include <linux/if_tun.h>
29# include <stdio.h>
30# include <stdlib.h>
31# include <string.h>
32#elif defined(RT_OS_FREEBSD)
33# include <errno.h>
34# include <sys/ioctl.h>
35# include <sys/poll.h>
36# include <sys/fcntl.h>
37# include <sys/types.h>
38# include <sys/wait.h>
39# include <stdio.h>
40# include <stdlib.h>
41# include <string.h>
42#elif defined(RT_OS_SOLARIS)
43# include <iprt/coredumper.h>
44#endif
45
46#include "ConsoleImpl.h"
47
48#include "Global.h"
49#include "VirtualBoxErrorInfoImpl.h"
50#include "GuestImpl.h"
51#include "KeyboardImpl.h"
52#include "MouseImpl.h"
53#include "DisplayImpl.h"
54#include "MachineDebuggerImpl.h"
55#include "USBDeviceImpl.h"
56#include "RemoteUSBDeviceImpl.h"
57#include "SharedFolderImpl.h"
58#include "AudioSnifferInterface.h"
59#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 "
8016 "fix the shared folder settings while the machine is not running."),
8017 it->first.c_str(), eik.getText().raw());
8018 break;
8019 }
8020 }
8021 if (FAILED(rc))
8022 rc = S_OK; // do not fail with broken shared folders
8023
8024 /* enter the lock again */
8025 alock.enter();
8026 }
8027
8028 /*
8029 * Capture USB devices.
8030 */
8031 rc = pConsole->captureUSBDevices(pVM);
8032 if (FAILED(rc)) break;
8033
8034 /* leave the lock before a lengthy operation */
8035 alock.leave();
8036
8037 /* Load saved state? */
8038 if (task->mSavedStateFile.length())
8039 {
8040 LogFlowFunc(("Restoring saved state from '%s'...\n",
8041 task->mSavedStateFile.c_str()));
8042
8043 vrc = VMR3LoadFromFile(pVM,
8044 task->mSavedStateFile.c_str(),
8045 Console::stateProgressCallback,
8046 static_cast<IProgress *>(task->mProgress));
8047
8048 if (RT_SUCCESS(vrc))
8049 {
8050 if (task->mStartPaused)
8051 /* done */
8052 pConsole->setMachineState(MachineState_Paused);
8053 else
8054 {
8055 /* Start/Resume the VM execution */
8056#ifdef VBOX_WITH_EXTPACK
8057 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8058#endif
8059 if (RT_SUCCESS(vrc))
8060 vrc = VMR3Resume(pVM);
8061 AssertLogRelRC(vrc);
8062 }
8063 }
8064
8065 /* Power off in case we failed loading or resuming the VM */
8066 if (RT_FAILURE(vrc))
8067 {
8068 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8069#ifdef VBOX_WITH_EXTPACK
8070 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8071#endif
8072 }
8073 }
8074 else if (task->mTeleporterEnabled)
8075 {
8076 /* -> ConsoleImplTeleporter.cpp */
8077 bool fPowerOffOnFailure;
8078 rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused,
8079 task->mProgress, &fPowerOffOnFailure);
8080 if (FAILED(rc) && fPowerOffOnFailure)
8081 {
8082 ErrorInfoKeeper eik;
8083 int vrc2 = VMR3PowerOff(pVM); AssertLogRelRC(vrc2);
8084#ifdef VBOX_WITH_EXTPACK
8085 pConsole->mptrExtPackManager->callAllVmPowerOffHooks(pConsole, pVM);
8086#endif
8087 }
8088 }
8089 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
8090 {
8091 /*
8092 * Get the config.
8093 */
8094 ULONG uPort;
8095 ULONG uInterval;
8096 Bstr bstrAddress, bstrPassword;
8097
8098 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
8099 if (SUCCEEDED(rc))
8100 {
8101 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
8102 if (SUCCEEDED(rc))
8103 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
8104 if (SUCCEEDED(rc))
8105 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
8106 }
8107 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
8108 {
8109 if (SUCCEEDED(rc))
8110 {
8111 Utf8Str strAddress(bstrAddress);
8112 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
8113 Utf8Str strPassword(bstrPassword);
8114 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
8115
8116 /* Power on the FT enabled VM. */
8117#ifdef VBOX_WITH_EXTPACK
8118 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8119#endif
8120 if (RT_SUCCESS(vrc))
8121 vrc = FTMR3PowerOn(pVM,
8122 task->mEnmFaultToleranceState == FaultToleranceState_Master /* fMaster */,
8123 uInterval,
8124 pszAddress,
8125 uPort,
8126 pszPassword);
8127 AssertLogRelRC(vrc);
8128 }
8129 task->mProgress->setCancelCallback(NULL, NULL);
8130 }
8131 else
8132 rc = E_FAIL;
8133 }
8134 else if (task->mStartPaused)
8135 /* done */
8136 pConsole->setMachineState(MachineState_Paused);
8137 else
8138 {
8139 /* Power on the VM (i.e. start executing) */
8140#ifdef VBOX_WITH_EXTPACK
8141 vrc = pConsole->mptrExtPackManager->callAllVmPowerOnHooks(pConsole, pVM);
8142#endif
8143 if (RT_SUCCESS(vrc))
8144 vrc = VMR3PowerOn(pVM);
8145 AssertLogRelRC(vrc);
8146 }
8147
8148 /* enter the lock again */
8149 alock.enter();
8150 }
8151 while (0);
8152
8153 /* On failure, destroy the VM */
8154 if (FAILED(rc) || RT_FAILURE(vrc))
8155 {
8156 /* preserve existing error info */
8157 ErrorInfoKeeper eik;
8158
8159 /* powerDown() will call VMR3Destroy() and do all necessary
8160 * cleanup (VRDP, USB devices) */
8161 HRESULT rc2 = pConsole->powerDown();
8162 AssertComRC(rc2);
8163 }
8164 else
8165 {
8166 /*
8167 * Deregister the VMSetError callback. This is necessary as the
8168 * pfnVMAtError() function passed to VMR3Create() is supposed to
8169 * be sticky but our error callback isn't.
8170 */
8171 alock.leave();
8172 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
8173 /** @todo register another VMSetError callback? */
8174 alock.enter();
8175 }
8176 }
8177 else
8178 {
8179 /*
8180 * If VMR3Create() failed it has released the VM memory.
8181 */
8182 VMR3ReleaseUVM(pConsole->mpUVM);
8183 pConsole->mpUVM = NULL;
8184 }
8185
8186 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
8187 {
8188 /* If VMR3Create() or one of the other calls in this function fail,
8189 * an appropriate error message has been set in task->mErrorMsg.
8190 * However since that happens via a callback, the rc status code in
8191 * this function is not updated.
8192 */
8193 if (!task->mErrorMsg.length())
8194 {
8195 /* If the error message is not set but we've got a failure,
8196 * convert the VBox status code into a meaningful error message.
8197 * This becomes unused once all the sources of errors set the
8198 * appropriate error message themselves.
8199 */
8200 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
8201 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
8202 vrc);
8203 }
8204
8205 /* Set the error message as the COM error.
8206 * Progress::notifyComplete() will pick it up later. */
8207 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
8208 }
8209 }
8210 catch (HRESULT aRC) { rc = aRC; }
8211
8212 if ( pConsole->mMachineState == MachineState_Starting
8213 || pConsole->mMachineState == MachineState_Restoring
8214 || pConsole->mMachineState == MachineState_TeleportingIn
8215 )
8216 {
8217 /* We are still in the Starting/Restoring state. This means one of:
8218 *
8219 * 1) we failed before VMR3Create() was called;
8220 * 2) VMR3Create() failed.
8221 *
8222 * In both cases, there is no need to call powerDown(), but we still
8223 * need to go back to the PoweredOff/Saved state. Reuse
8224 * vmstateChangeCallback() for that purpose.
8225 */
8226
8227 /* preserve existing error info */
8228 ErrorInfoKeeper eik;
8229
8230 Assert(pConsole->mpUVM == NULL);
8231 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
8232 pConsole);
8233 }
8234
8235 /*
8236 * Evaluate the final result. Note that the appropriate mMachineState value
8237 * is already set by vmstateChangeCallback() in all cases.
8238 */
8239
8240 /* leave the lock, don't need it any more */
8241 alock.leave();
8242
8243 if (SUCCEEDED(rc))
8244 {
8245 /* Notify the progress object of the success */
8246 task->mProgress->notifyComplete(S_OK);
8247 }
8248 else
8249 {
8250 /* The progress object will fetch the current error info */
8251 task->mProgress->notifyComplete(rc);
8252 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
8253 }
8254
8255 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
8256 pConsole->mControl->EndPowerUp(rc);
8257
8258#if defined(RT_OS_WINDOWS)
8259 /* uninitialize COM */
8260 CoUninitialize();
8261#endif
8262
8263 LogFlowFuncLeave();
8264
8265 return VINF_SUCCESS;
8266}
8267
8268
8269/**
8270 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
8271 *
8272 * @param pConsole Reference to the console object.
8273 * @param pVM The VM handle.
8274 * @param lInstance The instance of the controller.
8275 * @param pcszDevice The name of the controller type.
8276 * @param enmBus The storage bus type of the controller.
8277 * @param fSetupMerge Whether to set up a medium merge
8278 * @param uMergeSource Merge source image index
8279 * @param uMergeTarget Merge target image index
8280 * @param aMediumAtt The medium attachment.
8281 * @param aMachineState The current machine state.
8282 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
8283 * @return VBox status code.
8284 */
8285/* static */
8286DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
8287 PVM pVM,
8288 const char *pcszDevice,
8289 unsigned uInstance,
8290 StorageBus_T enmBus,
8291 bool fUseHostIOCache,
8292 bool fBuiltinIoCache,
8293 bool fSetupMerge,
8294 unsigned uMergeSource,
8295 unsigned uMergeTarget,
8296 IMediumAttachment *aMediumAtt,
8297 MachineState_T aMachineState,
8298 HRESULT *phrc)
8299{
8300 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
8301
8302 int rc;
8303 HRESULT hrc;
8304 Bstr bstr;
8305 *phrc = S_OK;
8306#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
8307#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
8308
8309 /* Ignore attachments other than hard disks, since at the moment they are
8310 * not subject to snapshotting in general. */
8311 DeviceType_T lType;
8312 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
8313 if (lType != DeviceType_HardDisk)
8314 return VINF_SUCCESS;
8315
8316 /* Determine the base path for the device instance. */
8317 PCFGMNODE pCtlInst;
8318 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
8319 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
8320
8321 /* Update the device instance configuration. */
8322 rc = pConsole->configMediumAttachment(pCtlInst,
8323 pcszDevice,
8324 uInstance,
8325 enmBus,
8326 fUseHostIOCache,
8327 fBuiltinIoCache,
8328 fSetupMerge,
8329 uMergeSource,
8330 uMergeTarget,
8331 aMediumAtt,
8332 aMachineState,
8333 phrc,
8334 true /* fAttachDetach */,
8335 false /* fForceUnmount */,
8336 pVM,
8337 NULL /* paLedDevType */);
8338 /** @todo this dumps everything attached to this device instance, which
8339 * is more than necessary. Dumping the changed LUN would be enough. */
8340 CFGMR3Dump(pCtlInst);
8341 RC_CHECK();
8342
8343#undef RC_CHECK
8344#undef H
8345
8346 LogFlowFunc(("Returns success\n"));
8347 return VINF_SUCCESS;
8348}
8349
8350/**
8351 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
8352 */
8353static void takesnapshotProgressCancelCallback(void *pvUser)
8354{
8355 PUVM pUVM = (PUVM)pvUser;
8356 SSMR3Cancel(VMR3GetVM(pUVM));
8357}
8358
8359/**
8360 * Worker thread created by Console::TakeSnapshot.
8361 * @param Thread The current thread (ignored).
8362 * @param pvUser The task.
8363 * @return VINF_SUCCESS (ignored).
8364 */
8365/*static*/
8366DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
8367{
8368 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
8369
8370 // taking a snapshot consists of the following:
8371
8372 // 1) creating a diff image for each virtual hard disk, into which write operations go after
8373 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
8374 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
8375 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
8376 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
8377
8378 Console *that = pTask->mConsole;
8379 bool fBeganTakingSnapshot = false;
8380 bool fSuspenededBySave = false;
8381
8382 AutoCaller autoCaller(that);
8383 if (FAILED(autoCaller.rc()))
8384 {
8385 that->mptrCancelableProgress.setNull();
8386 return autoCaller.rc();
8387 }
8388
8389 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
8390
8391 HRESULT rc = S_OK;
8392
8393 try
8394 {
8395 /* STEP 1 + 2:
8396 * request creating the diff images on the server and create the snapshot object
8397 * (this will set the machine state to Saving on the server to block
8398 * others from accessing this machine)
8399 */
8400 rc = that->mControl->BeginTakingSnapshot(that,
8401 pTask->bstrName.raw(),
8402 pTask->bstrDescription.raw(),
8403 pTask->mProgress,
8404 pTask->fTakingSnapshotOnline,
8405 pTask->bstrSavedStateFile.asOutParam());
8406 if (FAILED(rc))
8407 throw rc;
8408
8409 fBeganTakingSnapshot = true;
8410
8411 /*
8412 * state file is non-null only when the VM is paused
8413 * (i.e. creating a snapshot online)
8414 */
8415 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
8416 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
8417 if (!f)
8418 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
8419
8420 /* sync the state with the server */
8421 if (pTask->lastMachineState == MachineState_Running)
8422 that->setMachineStateLocally(MachineState_LiveSnapshotting);
8423 else
8424 that->setMachineStateLocally(MachineState_Saving);
8425
8426 // STEP 3: save the VM state (if online)
8427 if (pTask->fTakingSnapshotOnline)
8428 {
8429 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
8430
8431 SafeVMPtr ptrVM(that);
8432 if (!ptrVM.isOk())
8433 throw ptrVM.rc();
8434
8435 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
8436 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
8437 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
8438
8439 alock.leave();
8440 LogFlowFunc(("VMR3Save...\n"));
8441 int vrc = VMR3Save(ptrVM,
8442 strSavedStateFile.c_str(),
8443 true /*fContinueAfterwards*/,
8444 Console::stateProgressCallback,
8445 static_cast<IProgress *>(pTask->mProgress),
8446 &fSuspenededBySave);
8447 alock.enter();
8448 if (RT_FAILURE(vrc))
8449 throw setErrorStatic(E_FAIL,
8450 tr("Failed to save the machine state to '%s' (%Rrc)"),
8451 strSavedStateFile.c_str(), vrc);
8452
8453 pTask->mProgress->setCancelCallback(NULL, NULL);
8454 if (!pTask->mProgress->notifyPointOfNoReturn())
8455 throw setErrorStatic(E_FAIL, tr("Canceled"));
8456 that->mptrCancelableProgress.setNull();
8457
8458 // STEP 4: reattach hard disks
8459 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
8460
8461 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
8462 1); // operation weight, same as computed when setting up progress object
8463
8464 com::SafeIfaceArray<IMediumAttachment> atts;
8465 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
8466 if (FAILED(rc))
8467 throw rc;
8468
8469 for (size_t i = 0;
8470 i < atts.size();
8471 ++i)
8472 {
8473 ComPtr<IStorageController> pStorageController;
8474 Bstr controllerName;
8475 ULONG lInstance;
8476 StorageControllerType_T enmController;
8477 StorageBus_T enmBus;
8478 BOOL fUseHostIOCache;
8479
8480 /*
8481 * We can't pass a storage controller object directly
8482 * (g++ complains about not being able to pass non POD types through '...')
8483 * so we have to query needed values here and pass them.
8484 */
8485 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
8486 if (FAILED(rc))
8487 throw rc;
8488
8489 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
8490 pStorageController.asOutParam());
8491 if (FAILED(rc))
8492 throw rc;
8493
8494 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
8495 if (FAILED(rc))
8496 throw rc;
8497 rc = pStorageController->COMGETTER(Instance)(&lInstance);
8498 if (FAILED(rc))
8499 throw rc;
8500 rc = pStorageController->COMGETTER(Bus)(&enmBus);
8501 if (FAILED(rc))
8502 throw rc;
8503 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
8504 if (FAILED(rc))
8505 throw rc;
8506
8507 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
8508
8509 BOOL fBuiltinIoCache;
8510 rc = that->mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache);
8511 if (FAILED(rc))
8512 throw rc;
8513
8514 /*
8515 * don't leave the lock since reconfigureMediumAttachment
8516 * isn't going to need the Console lock.
8517 */
8518 vrc = VMR3ReqCallWait(ptrVM,
8519 VMCPUID_ANY,
8520 (PFNRT)reconfigureMediumAttachment,
8521 13,
8522 that,
8523 ptrVM.raw(),
8524 pcszDevice,
8525 lInstance,
8526 enmBus,
8527 fUseHostIOCache,
8528 fBuiltinIoCache,
8529 false /* fSetupMerge */,
8530 0 /* uMergeSource */,
8531 0 /* uMergeTarget */,
8532 atts[i],
8533 that->mMachineState,
8534 &rc);
8535 if (RT_FAILURE(vrc))
8536 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
8537 if (FAILED(rc))
8538 throw rc;
8539 }
8540 }
8541
8542 /*
8543 * finalize the requested snapshot object.
8544 * This will reset the machine state to the state it had right
8545 * before calling mControl->BeginTakingSnapshot().
8546 */
8547 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
8548 // do not throw rc here because we can't call EndTakingSnapshot() twice
8549 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8550 }
8551 catch (HRESULT rcThrown)
8552 {
8553 /* preserve existing error info */
8554 ErrorInfoKeeper eik;
8555
8556 if (fBeganTakingSnapshot)
8557 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
8558
8559 rc = rcThrown;
8560 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8561 }
8562 Assert(alock.isWriteLockOnCurrentThread());
8563
8564 if (FAILED(rc)) /* Must come before calling setMachineState. */
8565 pTask->mProgress->notifyComplete(rc);
8566
8567 /*
8568 * Fix up the machine state.
8569 *
8570 * For live snapshots we do all the work, for the two other variations we
8571 * just update the local copy.
8572 */
8573 MachineState_T enmMachineState;
8574 that->mMachine->COMGETTER(State)(&enmMachineState);
8575 if ( that->mMachineState == MachineState_LiveSnapshotting
8576 || that->mMachineState == MachineState_Saving)
8577 {
8578
8579 if (!pTask->fTakingSnapshotOnline)
8580 that->setMachineStateLocally(pTask->lastMachineState);
8581 else if (SUCCEEDED(rc))
8582 {
8583 Assert( pTask->lastMachineState == MachineState_Running
8584 || pTask->lastMachineState == MachineState_Paused);
8585 Assert(that->mMachineState == MachineState_Saving);
8586 if (pTask->lastMachineState == MachineState_Running)
8587 {
8588 LogFlowFunc(("VMR3Resume...\n"));
8589 SafeVMPtr ptrVM(that);
8590 alock.leave();
8591 int vrc = VMR3Resume(ptrVM);
8592 alock.enter();
8593 if (RT_FAILURE(vrc))
8594 {
8595 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
8596 pTask->mProgress->notifyComplete(rc);
8597 if (that->mMachineState == MachineState_Saving)
8598 that->setMachineStateLocally(MachineState_Paused);
8599 }
8600 }
8601 else
8602 that->setMachineStateLocally(MachineState_Paused);
8603 }
8604 else
8605 {
8606 /** @todo this could probably be made more generic and reused elsewhere. */
8607 /* paranoid cleanup on for a failed online snapshot. */
8608 VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
8609 switch (enmVMState)
8610 {
8611 case VMSTATE_RUNNING:
8612 case VMSTATE_RUNNING_LS:
8613 case VMSTATE_DEBUGGING:
8614 case VMSTATE_DEBUGGING_LS:
8615 case VMSTATE_POWERING_OFF:
8616 case VMSTATE_POWERING_OFF_LS:
8617 case VMSTATE_RESETTING:
8618 case VMSTATE_RESETTING_LS:
8619 Assert(!fSuspenededBySave);
8620 that->setMachineState(MachineState_Running);
8621 break;
8622
8623 case VMSTATE_GURU_MEDITATION:
8624 case VMSTATE_GURU_MEDITATION_LS:
8625 that->setMachineState(MachineState_Stuck);
8626 break;
8627
8628 case VMSTATE_FATAL_ERROR:
8629 case VMSTATE_FATAL_ERROR_LS:
8630 if (pTask->lastMachineState == MachineState_Paused)
8631 that->setMachineStateLocally(pTask->lastMachineState);
8632 else
8633 that->setMachineState(MachineState_Paused);
8634 break;
8635
8636 default:
8637 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8638 case VMSTATE_SUSPENDED:
8639 case VMSTATE_SUSPENDED_LS:
8640 case VMSTATE_SUSPENDING:
8641 case VMSTATE_SUSPENDING_LS:
8642 case VMSTATE_SUSPENDING_EXT_LS:
8643 if (fSuspenededBySave)
8644 {
8645 Assert(pTask->lastMachineState == MachineState_Running);
8646 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8647 SafeVMPtr ptrVM(that);
8648 alock.leave();
8649 int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc);
8650 alock.enter();
8651 if (RT_FAILURE(vrc))
8652 that->setMachineState(MachineState_Paused);
8653 }
8654 else if (pTask->lastMachineState == MachineState_Paused)
8655 that->setMachineStateLocally(pTask->lastMachineState);
8656 else
8657 that->setMachineState(MachineState_Paused);
8658 break;
8659 }
8660
8661 }
8662 }
8663 /*else: somebody else has change the state... Leave it. */
8664
8665 /* check the remote state to see that we got it right. */
8666 that->mMachine->COMGETTER(State)(&enmMachineState);
8667 AssertLogRelMsg(that->mMachineState == enmMachineState,
8668 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8669 Global::stringifyMachineState(enmMachineState) ));
8670
8671
8672 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8673 pTask->mProgress->notifyComplete(rc);
8674
8675 delete pTask;
8676
8677 LogFlowFuncLeave();
8678 return VINF_SUCCESS;
8679}
8680
8681/**
8682 * Thread for executing the saved state operation.
8683 *
8684 * @param Thread The thread handle.
8685 * @param pvUser Pointer to a VMSaveTask structure.
8686 * @return VINF_SUCCESS (ignored).
8687 *
8688 * @note Locks the Console object for writing.
8689 */
8690/*static*/
8691DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8692{
8693 LogFlowFuncEnter();
8694
8695 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8696 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8697
8698 Assert(task->mSavedStateFile.length());
8699 Assert(task->mProgress.isNull());
8700 Assert(!task->mServerProgress.isNull());
8701
8702 const ComObjPtr<Console> &that = task->mConsole;
8703 Utf8Str errMsg;
8704 HRESULT rc = S_OK;
8705
8706 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
8707
8708 bool fSuspenededBySave;
8709 int vrc = VMR3Save(task->mpVM,
8710 task->mSavedStateFile.c_str(),
8711 false, /*fContinueAfterwards*/
8712 Console::stateProgressCallback,
8713 static_cast<IProgress *>(task->mServerProgress),
8714 &fSuspenededBySave);
8715 if (RT_FAILURE(vrc))
8716 {
8717 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8718 task->mSavedStateFile.c_str(), vrc);
8719 rc = E_FAIL;
8720 }
8721 Assert(!fSuspenededBySave);
8722
8723 /* lock the console once we're going to access it */
8724 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8725
8726 /* synchronize the state with the server */
8727 if (SUCCEEDED(rc))
8728 {
8729 /*
8730 * The machine has been successfully saved, so power it down
8731 * (vmstateChangeCallback() will set state to Saved on success).
8732 * Note: we release the task's VM caller, otherwise it will
8733 * deadlock.
8734 */
8735 task->releaseVMCaller();
8736 rc = that->powerDown();
8737 }
8738
8739 /*
8740 * Finalize the requested save state procedure. In case of failure it will
8741 * reset the machine state to the state it had right before calling
8742 * mControl->BeginSavingState(). This must be the last thing because it
8743 * will set the progress to completed, and that means that the frontend
8744 * can immediately uninit the associated console object.
8745 */
8746 that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
8747
8748 LogFlowFuncLeave();
8749 return VINF_SUCCESS;
8750}
8751
8752/**
8753 * Thread for powering down the Console.
8754 *
8755 * @param Thread The thread handle.
8756 * @param pvUser Pointer to the VMTask structure.
8757 * @return VINF_SUCCESS (ignored).
8758 *
8759 * @note Locks the Console object for writing.
8760 */
8761/*static*/
8762DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8763{
8764 LogFlowFuncEnter();
8765
8766 std::auto_ptr<VMPowerDownTask> task(static_cast<VMPowerDownTask *>(pvUser));
8767 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8768
8769 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8770
8771 Assert(task->mProgress.isNull());
8772
8773 const ComObjPtr<Console> &that = task->mConsole;
8774
8775 /* Note: no need to use addCaller() to protect Console because VMTask does
8776 * that */
8777
8778 /* wait until the method tat started us returns */
8779 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8780
8781 /* release VM caller to avoid the powerDown() deadlock */
8782 task->releaseVMCaller();
8783
8784 that->powerDown(task->mServerProgress);
8785
8786 /* complete the operation */
8787 that->mControl->EndPoweringDown(S_OK, Bstr().raw());
8788
8789 LogFlowFuncLeave();
8790 return VINF_SUCCESS;
8791}
8792
8793
8794/**
8795 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
8796 */
8797/*static*/ DECLCALLBACK(int)
8798Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PUVM pUVM)
8799{
8800 Console *pConsole = ((MYVMM2USERMETHODS *)pThis)->pConsole;
8801 NOREF(pUVM);
8802
8803 /*
8804 * For now, just call SaveState. We should probably try notify the GUI so
8805 * it can pop up a progress object and stuff.
8806 */
8807 HRESULT hrc = pConsole->SaveState(NULL);
8808 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
8809}
8810
8811/**
8812 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtInit}
8813 */
8814/*static*/ DECLCALLBACK(void)
8815Console::vmm2User_NotifyEmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
8816{
8817 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
8818 VirtualBoxBase::initializeComForThread();
8819}
8820
8821/**
8822 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyEmtTerm}
8823 */
8824/*static*/ DECLCALLBACK(void)
8825Console::vmm2User_NotifyEmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM, PUVMCPU pUVCpu)
8826{
8827 NOREF(pThis); NOREF(pUVM); NOREF(pUVCpu);
8828 VirtualBoxBase::uninitializeComForThread();
8829}
8830
8831/**
8832 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtInit}
8833 */
8834/*static*/ DECLCALLBACK(void)
8835Console::vmm2User_NotifyPdmtInit(PCVMM2USERMETHODS pThis, PUVM pUVM)
8836{
8837 NOREF(pThis); NOREF(pUVM);
8838 VirtualBoxBase::initializeComForThread();
8839}
8840
8841/**
8842 * @interface_method_impl{VMM2USERMETHODS,pfnNotifyPdmtTerm}
8843 */
8844/*static*/ DECLCALLBACK(void)
8845Console::vmm2User_NotifyPdmtTerm(PCVMM2USERMETHODS pThis, PUVM pUVM)
8846{
8847 NOREF(pThis); NOREF(pUVM);
8848 VirtualBoxBase::uninitializeComForThread();
8849}
8850
8851
8852
8853
8854/**
8855 * The Main status driver instance data.
8856 */
8857typedef struct DRVMAINSTATUS
8858{
8859 /** The LED connectors. */
8860 PDMILEDCONNECTORS ILedConnectors;
8861 /** Pointer to the LED ports interface above us. */
8862 PPDMILEDPORTS pLedPorts;
8863 /** Pointer to the array of LED pointers. */
8864 PPDMLED *papLeds;
8865 /** The unit number corresponding to the first entry in the LED array. */
8866 RTUINT iFirstLUN;
8867 /** The unit number corresponding to the last entry in the LED array.
8868 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8869 RTUINT iLastLUN;
8870} DRVMAINSTATUS, *PDRVMAINSTATUS;
8871
8872
8873/**
8874 * Notification about a unit which have been changed.
8875 *
8876 * The driver must discard any pointers to data owned by
8877 * the unit and requery it.
8878 *
8879 * @param pInterface Pointer to the interface structure containing the called function pointer.
8880 * @param iLUN The unit number.
8881 */
8882DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8883{
8884 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8885 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8886 {
8887 PPDMLED pLed;
8888 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8889 if (RT_FAILURE(rc))
8890 pLed = NULL;
8891 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8892 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8893 }
8894}
8895
8896
8897/**
8898 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8899 */
8900DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8901{
8902 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8903 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8904 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8905 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8906 return NULL;
8907}
8908
8909
8910/**
8911 * Destruct a status driver instance.
8912 *
8913 * @returns VBox status.
8914 * @param pDrvIns The driver instance data.
8915 */
8916DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8917{
8918 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8919 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8920 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8921
8922 if (pData->papLeds)
8923 {
8924 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8925 while (iLed-- > 0)
8926 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8927 }
8928}
8929
8930
8931/**
8932 * Construct a status driver instance.
8933 *
8934 * @copydoc FNPDMDRVCONSTRUCT
8935 */
8936DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8937{
8938 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8939 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8940 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8941
8942 /*
8943 * Validate configuration.
8944 */
8945 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8946 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8947 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8948 ("Configuration error: Not possible to attach anything to this driver!\n"),
8949 VERR_PDM_DRVINS_NO_ATTACH);
8950
8951 /*
8952 * Data.
8953 */
8954 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8955 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8956
8957 /*
8958 * Read config.
8959 */
8960 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8961 if (RT_FAILURE(rc))
8962 {
8963 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8964 return rc;
8965 }
8966
8967 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8968 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8969 pData->iFirstLUN = 0;
8970 else if (RT_FAILURE(rc))
8971 {
8972 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8973 return rc;
8974 }
8975
8976 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8977 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8978 pData->iLastLUN = 0;
8979 else if (RT_FAILURE(rc))
8980 {
8981 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8982 return rc;
8983 }
8984 if (pData->iFirstLUN > pData->iLastLUN)
8985 {
8986 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8987 return VERR_GENERAL_FAILURE;
8988 }
8989
8990 /*
8991 * Get the ILedPorts interface of the above driver/device and
8992 * query the LEDs we want.
8993 */
8994 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8995 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8996 VERR_PDM_MISSING_INTERFACE_ABOVE);
8997
8998 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8999 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
9000
9001 return VINF_SUCCESS;
9002}
9003
9004
9005/**
9006 * Keyboard driver registration record.
9007 */
9008const PDMDRVREG Console::DrvStatusReg =
9009{
9010 /* u32Version */
9011 PDM_DRVREG_VERSION,
9012 /* szName */
9013 "MainStatus",
9014 /* szRCMod */
9015 "",
9016 /* szR0Mod */
9017 "",
9018 /* pszDescription */
9019 "Main status driver (Main as in the API).",
9020 /* fFlags */
9021 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
9022 /* fClass. */
9023 PDM_DRVREG_CLASS_STATUS,
9024 /* cMaxInstances */
9025 ~0,
9026 /* cbInstance */
9027 sizeof(DRVMAINSTATUS),
9028 /* pfnConstruct */
9029 Console::drvStatus_Construct,
9030 /* pfnDestruct */
9031 Console::drvStatus_Destruct,
9032 /* pfnRelocate */
9033 NULL,
9034 /* pfnIOCtl */
9035 NULL,
9036 /* pfnPowerOn */
9037 NULL,
9038 /* pfnReset */
9039 NULL,
9040 /* pfnSuspend */
9041 NULL,
9042 /* pfnResume */
9043 NULL,
9044 /* pfnAttach */
9045 NULL,
9046 /* pfnDetach */
9047 NULL,
9048 /* pfnPowerOff */
9049 NULL,
9050 /* pfnSoftReset */
9051 NULL,
9052 /* u32EndVersion */
9053 PDM_DRVREG_VERSION
9054};
9055
9056/* 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