VirtualBox

source: vbox/trunk/src/VBox/Main/ConsoleImpl.cpp@ 34244

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

Main,Config.kmk,VBoxManage,ExtPacks: Moved the VRDE bits from IVirtualBox to the extension packs; changed ISystemProperties and IVRDEServer to talk about VRDE extension packs instead of VRDE libraries.

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