VirtualBox

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

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

Main, Runtime: refined file system check to warn for disabled host cache on the xfs file system as well

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