VirtualBox

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

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

Main,NAT: Don't bother NAT back-end with rule name.

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