VirtualBox

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

最後變更 在這個檔案從33784是 33784,由 vboxsync 提交於 14 年 前

Main: Added ExtPackManager to Console and implemented the Console and VirtualBox hooks.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette