VirtualBox

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

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

Removed VBOX_DYNAMIC_NET_ATTACH, we don't need to keep this optional.

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