VirtualBox

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

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

*: Fixes for incorrect RTStrAPrintf usage (it does NOT return an IPRT status code) and the odd bugs nearby.

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

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