VirtualBox

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

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

Main: Eliminate the last bits of VirtualBoxBaseWithChildrenNEXT. It won't be missed.

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