VirtualBox

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

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

ExtPack: Don't hold any locks when calling extension pack hooks (except for pfnUnload and pfnUninstall).

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