VirtualBox

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

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

Main: defer creation of the VMMDev instance in Console to Console::powerUpThread so that the VMMDev with the HGCM thread only gets created for session/console pairs any more which actually power up a VM; this avoids creating dozens of HGCM threads for every single session even if it never starts a VM; some more Console code cleanup while we're at it

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