VirtualBox

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

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

PDMDevHlpVMSuspendSaveAndPowerOff/Main: Implemented the main bits - untested.

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