VirtualBox

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

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

Utf8Str: Use printfV in Utf8StrFmt, eliminated Utf8StrFmtVA by replacing it by a Utf8Str(str,va) constructor.

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