VirtualBox

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

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

Main: code for generic listener creation

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