VirtualBox

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

Last change on this file since 30430 was 30430, checked in by vboxsync, 15 years ago

Log virtual address space limit for win32 hosts

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 273.0 KB
Line 
1/* $Id: ConsoleImpl.cpp 30430 2010-06-24 12:41:57Z 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#if defined(RT_OS_WINDOWS) && HC_ARCH_BITS == 32
5166 /* @todo move this in RT, but too lazy now */
5167 uint32_t maxRAMArch;
5168 SYSTEM_INFO sysInfo;
5169 GetSystemInfo(&sysInfo);
5170
5171 if (sysInfo.lpMaximumApplicationAddress >= (LPVOID)0xC0000000) /* 3.0 GB */
5172 maxRAMArch = UINT32_C(2560);
5173 else
5174 if (sysInfo.lpMaximumApplicationAddress > (LPVOID)0xA0000000) /* 2.5 GB */
5175 maxRAMArch = UINT32_C(2048);
5176 else
5177 maxRAMArch = UINT32_C(1500);
5178
5179 RTLogRelLogger(loggerRelease, 0, ~0U, "Maximum user application address: %p\n", sysInfo.lpMaximumApplicationAddress);
5180 RTLogRelLogger(loggerRelease, 0, ~0U, "Maximum allowed guest RAM size: %d MB\n", maxRAMArch);
5181#endif
5182
5183 /* the package type is interesting for Linux distributions */
5184 char szExecName[RTPATH_MAX];
5185 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
5186 RTLogRelLogger(loggerRelease, 0, ~0U,
5187 "Executable: %s\n"
5188 "Process ID: %u\n"
5189 "Package type: %s"
5190#ifdef VBOX_OSE
5191 " (OSE)"
5192#endif
5193 "\n",
5194 pszExecName ? pszExecName : "unknown",
5195 RTProcSelf(),
5196 VBOX_PACKAGE_STRING);
5197
5198 /* register this logger as the release logger */
5199 RTLogRelSetDefaultInstance(loggerRelease);
5200 hrc = S_OK;
5201
5202 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5203 RTLogFlush(loggerRelease);
5204 }
5205 else
5206 hrc = setError(E_FAIL,
5207 tr("Failed to open release log (%s, %Rrc)"),
5208 szError, vrc);
5209
5210 /* If we've made any directory changes, flush the directory to increase
5211 the likelyhood that the log file will be usable after a system panic.
5212
5213 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5214 is missing. Just don't have too high hopes for this to help. */
5215 if (SUCCEEDED(hrc) || cHistoryFiles)
5216 RTDirFlush(logDir.c_str());
5217
5218 return hrc;
5219}
5220
5221/**
5222 * Common worker for PowerUp and PowerUpPaused.
5223 *
5224 * @returns COM status code.
5225 *
5226 * @param aProgress Where to return the progress object.
5227 * @param aPaused true if PowerUpPaused called.
5228 *
5229 * @todo move down to powerDown();
5230 */
5231HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5232{
5233 if (aProgress == NULL)
5234 return E_POINTER;
5235
5236 LogFlowThisFuncEnter();
5237 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5238
5239 AutoCaller autoCaller(this);
5240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5241
5242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5243
5244 if (Global::IsOnlineOrTransient(mMachineState))
5245 return setError(VBOX_E_INVALID_VM_STATE,
5246 tr("The virtual machine is already running or busy (machine state: %s)"),
5247 Global::stringifyMachineState(mMachineState));
5248
5249 HRESULT rc = S_OK;
5250
5251 /* the network cards will undergo a quick consistency check */
5252 for (ULONG slot = 0;
5253 slot < SchemaDefs::NetworkAdapterCount;
5254 ++slot)
5255 {
5256 ComPtr<INetworkAdapter> adapter;
5257 mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
5258 BOOL enabled = FALSE;
5259 adapter->COMGETTER(Enabled)(&enabled);
5260 if (!enabled)
5261 continue;
5262
5263 NetworkAttachmentType_T netattach;
5264 adapter->COMGETTER(AttachmentType)(&netattach);
5265 switch (netattach)
5266 {
5267 case NetworkAttachmentType_Bridged:
5268 {
5269#ifdef RT_OS_WINDOWS
5270 /* a valid host interface must have been set */
5271 Bstr hostif;
5272 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
5273 if (!hostif)
5274 {
5275 return setError(VBOX_E_HOST_ERROR,
5276 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5277 }
5278 ComPtr<IVirtualBox> virtualBox;
5279 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5280 ComPtr<IHost> host;
5281 virtualBox->COMGETTER(Host)(host.asOutParam());
5282 ComPtr<IHostNetworkInterface> hostInterface;
5283 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
5284 {
5285 return setError(VBOX_E_HOST_ERROR,
5286 tr("VM cannot start because the host interface '%ls' does not exist"),
5287 hostif.raw());
5288 }
5289#endif /* RT_OS_WINDOWS */
5290 break;
5291 }
5292 default:
5293 break;
5294 }
5295 }
5296
5297 /* Read console data stored in the saved state file (if not yet done) */
5298 rc = loadDataFromSavedState();
5299 if (FAILED(rc)) return rc;
5300
5301 /* Check all types of shared folders and compose a single list */
5302 SharedFolderDataMap sharedFolders;
5303 {
5304 /* first, insert global folders */
5305 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5306 it != mGlobalSharedFolders.end(); ++ it)
5307 sharedFolders[it->first] = it->second;
5308
5309 /* second, insert machine folders */
5310 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5311 it != mMachineSharedFolders.end(); ++ it)
5312 sharedFolders[it->first] = it->second;
5313
5314 /* third, insert console folders */
5315 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5316 it != mSharedFolders.end(); ++ it)
5317 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(), it->second->isWritable());
5318 }
5319
5320 Bstr savedStateFile;
5321
5322 /*
5323 * Saved VMs will have to prove that their saved states seem kosher.
5324 */
5325 if (mMachineState == MachineState_Saved)
5326 {
5327 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5328 if (FAILED(rc)) return rc;
5329 ComAssertRet(!!savedStateFile, E_FAIL);
5330 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5331 if (RT_FAILURE(vrc))
5332 return setError(VBOX_E_FILE_ERROR,
5333 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5334 savedStateFile.raw(), vrc);
5335 }
5336
5337 /* test and clear the TeleporterEnabled property */
5338 BOOL fTeleporterEnabled;
5339 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5340 if (FAILED(rc)) return rc;
5341#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5342 if (fTeleporterEnabled)
5343 {
5344 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5345 if (FAILED(rc)) return rc;
5346 }
5347#endif
5348
5349 /* create a progress object to track progress of this operation */
5350 ComObjPtr<Progress> powerupProgress;
5351 powerupProgress.createObject();
5352 Bstr progressDesc;
5353 if (mMachineState == MachineState_Saved)
5354 progressDesc = tr("Restoring virtual machine");
5355 else if (fTeleporterEnabled)
5356 progressDesc = tr("Teleporting virtual machine");
5357 else
5358 progressDesc = tr("Starting virtual machine");
5359 if (mMachineState == MachineState_Saved || !fTeleporterEnabled)
5360 rc = powerupProgress->init(static_cast<IConsole *>(this),
5361 progressDesc,
5362 FALSE /* aCancelable */);
5363 else
5364 rc = powerupProgress->init(static_cast<IConsole *>(this),
5365 progressDesc,
5366 TRUE /* aCancelable */,
5367 3 /* cOperations */,
5368 10 /* ulTotalOperationsWeight */,
5369 Bstr(tr("Teleporting virtual machine")),
5370 1 /* ulFirstOperationWeight */,
5371 NULL);
5372 if (FAILED(rc))
5373 return rc;
5374
5375 /* Tell VBoxSVC and Machine about the progress object so they can combine
5376 proxy it to any openRemoteSession caller. */
5377 rc = mControl->BeginPowerUp(powerupProgress);
5378 if (FAILED(rc))
5379 {
5380 LogFlowThisFunc(("BeginPowerUp failed\n"));
5381 return rc;
5382 }
5383
5384 BOOL fCanceled;
5385 rc = powerupProgress->COMGETTER(Canceled)(&fCanceled);
5386 if (FAILED(rc))
5387 return rc;
5388 if (fCanceled)
5389 {
5390 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5391 return setError(E_FAIL, tr("Powerup was canceled"));
5392 }
5393
5394 /* setup task object and thread to carry out the operation
5395 * asynchronously */
5396
5397 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
5398 ComAssertComRCRetRC(task->rc());
5399
5400 task->mConfigConstructor = configConstructor;
5401 task->mSharedFolders = sharedFolders;
5402 task->mStartPaused = aPaused;
5403 if (mMachineState == MachineState_Saved)
5404 task->mSavedStateFile = savedStateFile;
5405 task->mTeleporterEnabled = fTeleporterEnabled;
5406
5407 /* Reset differencing hard disks for which autoReset is true,
5408 * but only if the machine has no snapshots OR the current snapshot
5409 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5410 * image of an ONLINE snapshot which contains the disk state of the machine
5411 * while it was previously running, but without the corresponding machine
5412 * state, which is equivalent to powering off a running machine and not
5413 * good idea
5414 */
5415 ComPtr<ISnapshot> pCurrentSnapshot;
5416 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5417 if (FAILED(rc)) return rc;
5418
5419 BOOL fCurrentSnapshotIsOnline = false;
5420 if (pCurrentSnapshot)
5421 {
5422 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5423 if (FAILED(rc)) return rc;
5424 }
5425
5426 if (!fCurrentSnapshotIsOnline)
5427 {
5428 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5429
5430 com::SafeIfaceArray<IMediumAttachment> atts;
5431 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5432 if (FAILED(rc)) return rc;
5433
5434 for (size_t i = 0;
5435 i < atts.size();
5436 ++i)
5437 {
5438 DeviceType_T devType;
5439 rc = atts[i]->COMGETTER(Type)(&devType);
5440 /** @todo later applies to floppies as well */
5441 if (devType == DeviceType_HardDisk)
5442 {
5443 ComPtr<IMedium> medium;
5444 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
5445 if (FAILED(rc)) return rc;
5446
5447 /* needs autoreset? */
5448 BOOL autoReset = FALSE;
5449 rc = medium->COMGETTER(AutoReset)(&autoReset);
5450 if (FAILED(rc)) return rc;
5451
5452 if (autoReset)
5453 {
5454 ComPtr<IProgress> resetProgress;
5455 rc = medium->Reset(resetProgress.asOutParam());
5456 if (FAILED(rc)) return rc;
5457
5458 /* save for later use on the powerup thread */
5459 task->hardDiskProgresses.push_back(resetProgress);
5460 }
5461 }
5462 }
5463 }
5464 else
5465 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5466
5467 rc = consoleInitReleaseLog(mMachine);
5468 if (FAILED(rc)) return rc;
5469
5470 /* pass the progress object to the caller if requested */
5471 if (aProgress)
5472 {
5473 if (task->hardDiskProgresses.size() == 0)
5474 {
5475 /* there are no other operations to track, return the powerup
5476 * progress only */
5477 powerupProgress.queryInterfaceTo(aProgress);
5478 }
5479 else
5480 {
5481 /* create a combined progress object */
5482 ComObjPtr<CombinedProgress> progress;
5483 progress.createObject();
5484 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5485 progresses.push_back(ComPtr<IProgress> (powerupProgress));
5486 rc = progress->init(static_cast<IConsole *>(this),
5487 progressDesc, progresses.begin(),
5488 progresses.end());
5489 AssertComRCReturnRC(rc);
5490 progress.queryInterfaceTo(aProgress);
5491 }
5492 }
5493
5494 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5495 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5496
5497 ComAssertMsgRCRet(vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
5498 E_FAIL);
5499
5500 /* task is now owned by powerUpThread(), so release it */
5501 task.release();
5502
5503 /* finally, set the state: no right to fail in this method afterwards
5504 * since we've already started the thread and it is now responsible for
5505 * any error reporting and appropriate state change! */
5506
5507 if (mMachineState == MachineState_Saved)
5508 setMachineState(MachineState_Restoring);
5509 else if (fTeleporterEnabled)
5510 setMachineState(MachineState_TeleportingIn);
5511 else
5512 setMachineState(MachineState_Starting);
5513
5514 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5515 LogFlowThisFuncLeave();
5516 return S_OK;
5517}
5518
5519/**
5520 * Internal power off worker routine.
5521 *
5522 * This method may be called only at certain places with the following meaning
5523 * as shown below:
5524 *
5525 * - if the machine state is either Running or Paused, a normal
5526 * Console-initiated powerdown takes place (e.g. PowerDown());
5527 * - if the machine state is Saving, saveStateThread() has successfully done its
5528 * job;
5529 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5530 * to start/load the VM;
5531 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5532 * as a result of the powerDown() call).
5533 *
5534 * Calling it in situations other than the above will cause unexpected behavior.
5535 *
5536 * Note that this method should be the only one that destroys mpVM and sets it
5537 * to NULL.
5538 *
5539 * @param aProgress Progress object to run (may be NULL).
5540 *
5541 * @note Locks this object for writing.
5542 *
5543 * @note Never call this method from a thread that called addVMCaller() or
5544 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5545 * release(). Otherwise it will deadlock.
5546 */
5547HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5548{
5549 LogFlowThisFuncEnter();
5550
5551 AutoCaller autoCaller(this);
5552 AssertComRCReturnRC(autoCaller.rc());
5553
5554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5555
5556 /* Total # of steps for the progress object. Must correspond to the
5557 * number of "advance percent count" comments in this method! */
5558 enum { StepCount = 7 };
5559 /* current step */
5560 ULONG step = 0;
5561
5562 HRESULT rc = S_OK;
5563 int vrc = VINF_SUCCESS;
5564
5565 /* sanity */
5566 Assert(mVMDestroying == false);
5567
5568 Assert(mpVM != NULL);
5569
5570 AssertMsg( mMachineState == MachineState_Running
5571 || mMachineState == MachineState_Paused
5572 || mMachineState == MachineState_Stuck
5573 || mMachineState == MachineState_Starting
5574 || mMachineState == MachineState_Stopping
5575 || mMachineState == MachineState_Saving
5576 || mMachineState == MachineState_Restoring
5577 || mMachineState == MachineState_TeleportingPausedVM
5578 || mMachineState == MachineState_TeleportingIn
5579 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5580
5581 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5582 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5583
5584 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5585 * VM has already powered itself off in vmstateChangeCallback() and is just
5586 * notifying Console about that. In case of Starting or Restoring,
5587 * powerUpThread() is calling us on failure, so the VM is already off at
5588 * that point. */
5589 if ( !mVMPoweredOff
5590 && ( mMachineState == MachineState_Starting
5591 || mMachineState == MachineState_Restoring
5592 || mMachineState == MachineState_TeleportingIn)
5593 )
5594 mVMPoweredOff = true;
5595
5596 /*
5597 * Go to Stopping state if not already there.
5598 *
5599 * Note that we don't go from Saving/Restoring to Stopping because
5600 * vmstateChangeCallback() needs it to set the state to Saved on
5601 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5602 * while leaving the lock below, Saving or Restoring should be fine too.
5603 * Ditto for TeleportingPausedVM -> Teleported.
5604 */
5605 if ( mMachineState != MachineState_Saving
5606 && mMachineState != MachineState_Restoring
5607 && mMachineState != MachineState_Stopping
5608 && mMachineState != MachineState_TeleportingIn
5609 && mMachineState != MachineState_TeleportingPausedVM
5610 )
5611 setMachineState(MachineState_Stopping);
5612
5613 /* ----------------------------------------------------------------------
5614 * DONE with necessary state changes, perform the power down actions (it's
5615 * safe to leave the object lock now if needed)
5616 * ---------------------------------------------------------------------- */
5617
5618 /* Stop the VRDP server to prevent new clients connection while VM is being
5619 * powered off. */
5620 if (mConsoleVRDPServer)
5621 {
5622 LogFlowThisFunc(("Stopping VRDP server...\n"));
5623
5624 /* Leave the lock since EMT will call us back as addVMCaller()
5625 * in updateDisplayData(). */
5626 alock.leave();
5627
5628 mConsoleVRDPServer->Stop();
5629
5630 alock.enter();
5631 }
5632
5633 /* advance percent count */
5634 if (aProgress)
5635 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5636
5637
5638 /* ----------------------------------------------------------------------
5639 * Now, wait for all mpVM callers to finish their work if there are still
5640 * some on other threads. NO methods that need mpVM (or initiate other calls
5641 * that need it) may be called after this point
5642 * ---------------------------------------------------------------------- */
5643
5644 /* go to the destroying state to prevent from adding new callers */
5645 mVMDestroying = true;
5646
5647 if (mVMCallers > 0)
5648 {
5649 /* lazy creation */
5650 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5651 RTSemEventCreate(&mVMZeroCallersSem);
5652
5653 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5654 mVMCallers));
5655
5656 alock.leave();
5657
5658 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5659
5660 alock.enter();
5661 }
5662
5663 /* advance percent count */
5664 if (aProgress)
5665 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5666
5667 vrc = VINF_SUCCESS;
5668
5669 /*
5670 * Power off the VM if not already done that.
5671 * Leave the lock since EMT will call vmstateChangeCallback.
5672 *
5673 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5674 * VM-(guest-)initiated power off happened in parallel a ms before this
5675 * call. So far, we let this error pop up on the user's side.
5676 */
5677 if (!mVMPoweredOff)
5678 {
5679 LogFlowThisFunc(("Powering off the VM...\n"));
5680 alock.leave();
5681 vrc = VMR3PowerOff(mpVM);
5682 alock.enter();
5683 }
5684 else
5685 {
5686 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5687 * off. */
5688 /* reset the flag for future re-use */
5689 mVMPoweredOff = false;
5690 }
5691
5692 /* advance percent count */
5693 if (aProgress)
5694 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5695
5696#ifdef VBOX_WITH_HGCM
5697 /* Shutdown HGCM services before destroying the VM. */
5698 if (mVMMDev)
5699 {
5700 LogFlowThisFunc(("Shutdown HGCM...\n"));
5701
5702 /* Leave the lock since EMT will call us back as addVMCaller() */
5703 alock.leave();
5704
5705 mVMMDev->hgcmShutdown();
5706
5707 alock.enter();
5708 }
5709
5710 /* advance percent count */
5711 if (aProgress)
5712 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5713
5714#endif /* VBOX_WITH_HGCM */
5715
5716 LogFlowThisFunc(("Ready for VM destruction.\n"));
5717
5718 /* If we are called from Console::uninit(), then try to destroy the VM even
5719 * on failure (this will most likely fail too, but what to do?..) */
5720 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5721 {
5722 /* If the machine has an USB controller, release all USB devices
5723 * (symmetric to the code in captureUSBDevices()) */
5724 bool fHasUSBController = false;
5725 {
5726 PPDMIBASE pBase;
5727 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5728 if (RT_SUCCESS(vrc))
5729 {
5730 fHasUSBController = true;
5731 detachAllUSBDevices(false /* aDone */);
5732 }
5733 }
5734
5735 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5736 * this point). We leave the lock before calling VMR3Destroy() because
5737 * it will result into calling destructors of drivers associated with
5738 * Console children which may in turn try to lock Console (e.g. by
5739 * instantiating SafeVMPtr to access mpVM). It's safe here because
5740 * mVMDestroying is set which should prevent any activity. */
5741
5742 /* Set mpVM to NULL early just in case if some old code is not using
5743 * addVMCaller()/releaseVMCaller(). */
5744 PVM pVM = mpVM;
5745 mpVM = NULL;
5746
5747 LogFlowThisFunc(("Destroying the VM...\n"));
5748
5749 alock.leave();
5750
5751 vrc = VMR3Destroy(pVM);
5752
5753 /* take the lock again */
5754 alock.enter();
5755
5756 /* advance percent count */
5757 if (aProgress)
5758 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5759
5760 if (RT_SUCCESS(vrc))
5761 {
5762 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5763 mMachineState));
5764 /* Note: the Console-level machine state change happens on the
5765 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5766 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5767 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5768 * occurred yet. This is okay, because mMachineState is already
5769 * Stopping in this case, so any other attempt to call PowerDown()
5770 * will be rejected. */
5771 }
5772 else
5773 {
5774 /* bad bad bad, but what to do? */
5775 mpVM = pVM;
5776 rc = setError(VBOX_E_VM_ERROR,
5777 tr("Could not destroy the machine. (Error: %Rrc)"),
5778 vrc);
5779 }
5780
5781 /* Complete the detaching of the USB devices. */
5782 if (fHasUSBController)
5783 detachAllUSBDevices(true /* aDone */);
5784
5785 /* advance percent count */
5786 if (aProgress)
5787 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5788 }
5789 else
5790 {
5791 rc = setError(VBOX_E_VM_ERROR,
5792 tr("Could not power off the machine. (Error: %Rrc)"),
5793 vrc);
5794 }
5795
5796 /* Finished with destruction. Note that if something impossible happened and
5797 * we've failed to destroy the VM, mVMDestroying will remain true and
5798 * mMachineState will be something like Stopping, so most Console methods
5799 * will return an error to the caller. */
5800 if (mpVM == NULL)
5801 mVMDestroying = false;
5802
5803 if (SUCCEEDED(rc))
5804 mCallbackData.clear();
5805
5806 /* complete the progress */
5807 if (aProgress)
5808 aProgress->notifyComplete(rc);
5809
5810 LogFlowThisFuncLeave();
5811 return rc;
5812}
5813
5814/**
5815 * @note Locks this object for writing.
5816 */
5817HRESULT Console::setMachineState(MachineState_T aMachineState,
5818 bool aUpdateServer /* = true */)
5819{
5820 AutoCaller autoCaller(this);
5821 AssertComRCReturnRC(autoCaller.rc());
5822
5823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5824
5825 HRESULT rc = S_OK;
5826
5827 if (mMachineState != aMachineState)
5828 {
5829 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5830 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5831 mMachineState = aMachineState;
5832
5833 /// @todo (dmik)
5834 // possibly, we need to redo onStateChange() using the dedicated
5835 // Event thread, like it is done in VirtualBox. This will make it
5836 // much safer (no deadlocks possible if someone tries to use the
5837 // console from the callback), however, listeners will lose the
5838 // ability to synchronously react to state changes (is it really
5839 // necessary??)
5840 LogFlowThisFunc(("Doing onStateChange()...\n"));
5841 onStateChange(aMachineState);
5842 LogFlowThisFunc(("Done onStateChange()\n"));
5843
5844 if (aUpdateServer)
5845 {
5846 /* Server notification MUST be done from under the lock; otherwise
5847 * the machine state here and on the server might go out of sync
5848 * which can lead to various unexpected results (like the machine
5849 * state being >= MachineState_Running on the server, while the
5850 * session state is already SessionState_Closed at the same time
5851 * there).
5852 *
5853 * Cross-lock conditions should be carefully watched out: calling
5854 * UpdateState we will require Machine and SessionMachine locks
5855 * (remember that here we're holding the Console lock here, and also
5856 * all locks that have been entered by the thread before calling
5857 * this method).
5858 */
5859 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5860 rc = mControl->UpdateState(aMachineState);
5861 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5862 }
5863 }
5864
5865 return rc;
5866}
5867
5868/**
5869 * Searches for a shared folder with the given logical name
5870 * in the collection of shared folders.
5871 *
5872 * @param aName logical name of the shared folder
5873 * @param aSharedFolder where to return the found object
5874 * @param aSetError whether to set the error info if the folder is
5875 * not found
5876 * @return
5877 * S_OK when found or E_INVALIDARG when not found
5878 *
5879 * @note The caller must lock this object for writing.
5880 */
5881HRESULT Console::findSharedFolder(CBSTR aName,
5882 ComObjPtr<SharedFolder> &aSharedFolder,
5883 bool aSetError /* = false */)
5884{
5885 /* sanity check */
5886 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5887
5888 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5889 if (it != mSharedFolders.end())
5890 {
5891 aSharedFolder = it->second;
5892 return S_OK;
5893 }
5894
5895 if (aSetError)
5896 setError(VBOX_E_FILE_ERROR,
5897 tr("Could not find a shared folder named '%ls'."),
5898 aName);
5899
5900 return VBOX_E_FILE_ERROR;
5901}
5902
5903/**
5904 * Fetches the list of global or machine shared folders from the server.
5905 *
5906 * @param aGlobal true to fetch global folders.
5907 *
5908 * @note The caller must lock this object for writing.
5909 */
5910HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5911{
5912 /* sanity check */
5913 AssertReturn(AutoCaller(this).state() == InInit ||
5914 isWriteLockOnCurrentThread(), E_FAIL);
5915
5916 /* protect mpVM (if not NULL) */
5917 AutoVMCallerQuietWeak autoVMCaller(this);
5918
5919 HRESULT rc = S_OK;
5920
5921 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5922
5923 if (aGlobal)
5924 {
5925 /// @todo grab & process global folders when they are done
5926 }
5927 else
5928 {
5929 SharedFolderDataMap oldFolders;
5930 if (online)
5931 oldFolders = mMachineSharedFolders;
5932
5933 mMachineSharedFolders.clear();
5934
5935 SafeIfaceArray<ISharedFolder> folders;
5936 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
5937 AssertComRCReturnRC(rc);
5938
5939 for (size_t i = 0; i < folders.size(); ++i)
5940 {
5941 ComPtr<ISharedFolder> folder = folders[i];
5942
5943 Bstr name;
5944 Bstr hostPath;
5945 BOOL writable;
5946
5947 rc = folder->COMGETTER(Name)(name.asOutParam());
5948 if (FAILED(rc)) break;
5949 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
5950 if (FAILED(rc)) break;
5951 rc = folder->COMGETTER(Writable)(&writable);
5952
5953 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable)));
5954
5955 /* send changes to HGCM if the VM is running */
5956 /// @todo report errors as runtime warnings through VMSetError
5957 if (online)
5958 {
5959 SharedFolderDataMap::iterator it = oldFolders.find(name);
5960 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5961 {
5962 /* a new machine folder is added or
5963 * the existing machine folder is changed */
5964 if (mSharedFolders.find(name) != mSharedFolders.end())
5965 ; /* the console folder exists, nothing to do */
5966 else
5967 {
5968 /* remove the old machine folder (when changed)
5969 * or the global folder if any (when new) */
5970 if (it != oldFolders.end() ||
5971 mGlobalSharedFolders.find(name) !=
5972 mGlobalSharedFolders.end())
5973 rc = removeSharedFolder(name);
5974 /* create the new machine folder */
5975 rc = createSharedFolder(name, SharedFolderData(hostPath, writable));
5976 }
5977 }
5978 /* forget the processed (or identical) folder */
5979 if (it != oldFolders.end())
5980 oldFolders.erase(it);
5981
5982 rc = S_OK;
5983 }
5984 }
5985
5986 AssertComRCReturnRC(rc);
5987
5988 /* process outdated (removed) folders */
5989 /// @todo report errors as runtime warnings through VMSetError
5990 if (online)
5991 {
5992 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5993 it != oldFolders.end(); ++ it)
5994 {
5995 if (mSharedFolders.find(it->first) != mSharedFolders.end())
5996 ; /* the console folder exists, nothing to do */
5997 else
5998 {
5999 /* remove the outdated machine folder */
6000 rc = removeSharedFolder(it->first);
6001 /* create the global folder if there is any */
6002 SharedFolderDataMap::const_iterator git =
6003 mGlobalSharedFolders.find(it->first);
6004 if (git != mGlobalSharedFolders.end())
6005 rc = createSharedFolder(git->first, git->second);
6006 }
6007 }
6008
6009 rc = S_OK;
6010 }
6011 }
6012
6013 return rc;
6014}
6015
6016/**
6017 * Searches for a shared folder with the given name in the list of machine
6018 * shared folders and then in the list of the global shared folders.
6019 *
6020 * @param aName Name of the folder to search for.
6021 * @param aIt Where to store the pointer to the found folder.
6022 * @return @c true if the folder was found and @c false otherwise.
6023 *
6024 * @note The caller must lock this object for reading.
6025 */
6026bool Console::findOtherSharedFolder(IN_BSTR aName,
6027 SharedFolderDataMap::const_iterator &aIt)
6028{
6029 /* sanity check */
6030 AssertReturn(isWriteLockOnCurrentThread(), false);
6031
6032 /* first, search machine folders */
6033 aIt = mMachineSharedFolders.find(aName);
6034 if (aIt != mMachineSharedFolders.end())
6035 return true;
6036
6037 /* second, search machine folders */
6038 aIt = mGlobalSharedFolders.find(aName);
6039 if (aIt != mGlobalSharedFolders.end())
6040 return true;
6041
6042 return false;
6043}
6044
6045/**
6046 * Calls the HGCM service to add a shared folder definition.
6047 *
6048 * @param aName Shared folder name.
6049 * @param aHostPath Shared folder path.
6050 *
6051 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6052 * @note Doesn't lock anything.
6053 */
6054HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
6055{
6056 ComAssertRet(aName && *aName, E_FAIL);
6057 ComAssertRet(aData.mHostPath, E_FAIL);
6058
6059 /* sanity checks */
6060 AssertReturn(mpVM, E_FAIL);
6061 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6062
6063 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
6064 SHFLSTRING *pFolderName, *pMapName;
6065 size_t cbString;
6066
6067 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
6068
6069 cbString = (RTUtf16Len(aData.mHostPath) + 1) * sizeof(RTUTF16);
6070 if (cbString >= UINT16_MAX)
6071 return setError(E_INVALIDARG, tr("The name is too long"));
6072 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6073 Assert(pFolderName);
6074 memcpy(pFolderName->String.ucs2, aData.mHostPath, cbString);
6075
6076 pFolderName->u16Size = (uint16_t)cbString;
6077 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6078
6079 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
6080 parms[0].u.pointer.addr = pFolderName;
6081 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6082
6083 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6084 if (cbString >= UINT16_MAX)
6085 {
6086 RTMemFree(pFolderName);
6087 return setError(E_INVALIDARG, tr("The host path is too long"));
6088 }
6089 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6090 Assert(pMapName);
6091 memcpy(pMapName->String.ucs2, aName, cbString);
6092
6093 pMapName->u16Size = (uint16_t)cbString;
6094 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6095
6096 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
6097 parms[1].u.pointer.addr = pMapName;
6098 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6099
6100 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
6101 parms[2].u.uint32 = aData.mWritable;
6102
6103 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6104 SHFL_FN_ADD_MAPPING,
6105 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
6106 RTMemFree(pFolderName);
6107 RTMemFree(pMapName);
6108
6109 if (RT_FAILURE(vrc))
6110 return setError(E_FAIL,
6111 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
6112 aName, aData.mHostPath.raw(), vrc);
6113
6114 return S_OK;
6115}
6116
6117/**
6118 * Calls the HGCM service to remove the shared folder definition.
6119 *
6120 * @param aName Shared folder name.
6121 *
6122 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6123 * @note Doesn't lock anything.
6124 */
6125HRESULT Console::removeSharedFolder(CBSTR aName)
6126{
6127 ComAssertRet(aName && *aName, E_FAIL);
6128
6129 /* sanity checks */
6130 AssertReturn(mpVM, E_FAIL);
6131 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6132
6133 VBOXHGCMSVCPARM parms;
6134 SHFLSTRING *pMapName;
6135 size_t cbString;
6136
6137 Log(("Removing shared folder '%ls'\n", aName));
6138
6139 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6140 if (cbString >= UINT16_MAX)
6141 return setError(E_INVALIDARG, tr("The name is too long"));
6142 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6143 Assert(pMapName);
6144 memcpy(pMapName->String.ucs2, aName, cbString);
6145
6146 pMapName->u16Size = (uint16_t)cbString;
6147 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6148
6149 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6150 parms.u.pointer.addr = pMapName;
6151 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6152
6153 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6154 SHFL_FN_REMOVE_MAPPING,
6155 1, &parms);
6156 RTMemFree(pMapName);
6157 if (RT_FAILURE(vrc))
6158 return setError(E_FAIL,
6159 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6160 aName, vrc);
6161
6162 return S_OK;
6163}
6164
6165/**
6166 * VM state callback function. Called by the VMM
6167 * using its state machine states.
6168 *
6169 * Primarily used to handle VM initiated power off, suspend and state saving,
6170 * but also for doing termination completed work (VMSTATE_TERMINATE).
6171 *
6172 * In general this function is called in the context of the EMT.
6173 *
6174 * @param aVM The VM handle.
6175 * @param aState The new state.
6176 * @param aOldState The old state.
6177 * @param aUser The user argument (pointer to the Console object).
6178 *
6179 * @note Locks the Console object for writing.
6180 */
6181DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6182 VMSTATE aState,
6183 VMSTATE aOldState,
6184 void *aUser)
6185{
6186 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6187 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6188
6189 Console *that = static_cast<Console *>(aUser);
6190 AssertReturnVoid(that);
6191
6192 AutoCaller autoCaller(that);
6193
6194 /* Note that we must let this method proceed even if Console::uninit() has
6195 * been already called. In such case this VMSTATE change is a result of:
6196 * 1) powerDown() called from uninit() itself, or
6197 * 2) VM-(guest-)initiated power off. */
6198 AssertReturnVoid( autoCaller.isOk()
6199 || autoCaller.state() == InUninit);
6200
6201 switch (aState)
6202 {
6203 /*
6204 * The VM has terminated
6205 */
6206 case VMSTATE_OFF:
6207 {
6208 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6209
6210 if (that->mVMStateChangeCallbackDisabled)
6211 break;
6212
6213 /* Do we still think that it is running? It may happen if this is a
6214 * VM-(guest-)initiated shutdown/poweroff.
6215 */
6216 if ( that->mMachineState != MachineState_Stopping
6217 && that->mMachineState != MachineState_Saving
6218 && that->mMachineState != MachineState_Restoring
6219 && that->mMachineState != MachineState_TeleportingIn
6220 && that->mMachineState != MachineState_TeleportingPausedVM
6221 && !that->mVMIsAlreadyPoweringOff
6222 )
6223 {
6224 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6225
6226 /* prevent powerDown() from calling VMR3PowerOff() again */
6227 Assert(that->mVMPoweredOff == false);
6228 that->mVMPoweredOff = true;
6229
6230 /* we are stopping now */
6231 that->setMachineState(MachineState_Stopping);
6232
6233 /* Setup task object and thread to carry out the operation
6234 * asynchronously (if we call powerDown() right here but there
6235 * is one or more mpVM callers (added with addVMCaller()) we'll
6236 * deadlock).
6237 */
6238 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6239 true /* aUsesVMPtr */));
6240
6241 /* If creating a task is falied, this can currently mean one of
6242 * two: either Console::uninit() has been called just a ms
6243 * before (so a powerDown() call is already on the way), or
6244 * powerDown() itself is being already executed. Just do
6245 * nothing.
6246 */
6247 if (!task->isOk())
6248 {
6249 LogFlowFunc(("Console is already being uninitialized.\n"));
6250 break;
6251 }
6252
6253 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6254 (void *) task.get(), 0,
6255 RTTHREADTYPE_MAIN_WORKER, 0,
6256 "VMPowerDown");
6257 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6258
6259 /* task is now owned by powerDownThread(), so release it */
6260 task.release();
6261 }
6262 break;
6263 }
6264
6265 /* The VM has been completely destroyed.
6266 *
6267 * Note: This state change can happen at two points:
6268 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6269 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6270 * called by EMT.
6271 */
6272 case VMSTATE_TERMINATED:
6273 {
6274 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6275
6276 if (that->mVMStateChangeCallbackDisabled)
6277 break;
6278
6279 /* Terminate host interface networking. If aVM is NULL, we've been
6280 * manually called from powerUpThread() either before calling
6281 * VMR3Create() or after VMR3Create() failed, so no need to touch
6282 * networking.
6283 */
6284 if (aVM)
6285 that->powerDownHostInterfaces();
6286
6287 /* From now on the machine is officially powered down or remains in
6288 * the Saved state.
6289 */
6290 switch (that->mMachineState)
6291 {
6292 default:
6293 AssertFailed();
6294 /* fall through */
6295 case MachineState_Stopping:
6296 /* successfully powered down */
6297 that->setMachineState(MachineState_PoweredOff);
6298 break;
6299 case MachineState_Saving:
6300 /* successfully saved (note that the machine is already in
6301 * the Saved state on the server due to EndSavingState()
6302 * called from saveStateThread(), so only change the local
6303 * state) */
6304 that->setMachineStateLocally(MachineState_Saved);
6305 break;
6306 case MachineState_Starting:
6307 /* failed to start, but be patient: set back to PoweredOff
6308 * (for similarity with the below) */
6309 that->setMachineState(MachineState_PoweredOff);
6310 break;
6311 case MachineState_Restoring:
6312 /* failed to load the saved state file, but be patient: set
6313 * back to Saved (to preserve the saved state file) */
6314 that->setMachineState(MachineState_Saved);
6315 break;
6316 case MachineState_TeleportingIn:
6317 /* Teleportation failed or was cancelled. Back to powered off. */
6318 that->setMachineState(MachineState_PoweredOff);
6319 break;
6320 case MachineState_TeleportingPausedVM:
6321 /* Successfully teleported the VM. */
6322 that->setMachineState(MachineState_Teleported);
6323 break;
6324 }
6325 break;
6326 }
6327
6328 case VMSTATE_SUSPENDED:
6329 {
6330 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6331
6332 if (that->mVMStateChangeCallbackDisabled)
6333 break;
6334
6335 switch (that->mMachineState)
6336 {
6337 case MachineState_Teleporting:
6338 that->setMachineState(MachineState_TeleportingPausedVM);
6339 break;
6340
6341 case MachineState_LiveSnapshotting:
6342 that->setMachineState(MachineState_Saving);
6343 break;
6344
6345 case MachineState_TeleportingPausedVM:
6346 case MachineState_Saving:
6347 case MachineState_Restoring:
6348 case MachineState_Stopping:
6349 case MachineState_TeleportingIn:
6350 /* The worker threads handles the transition. */
6351 break;
6352
6353 default:
6354 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6355 case MachineState_Running:
6356 that->setMachineState(MachineState_Paused);
6357 break;
6358 }
6359 break;
6360 }
6361
6362 case VMSTATE_SUSPENDED_LS:
6363 case VMSTATE_SUSPENDED_EXT_LS:
6364 {
6365 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6366 if (that->mVMStateChangeCallbackDisabled)
6367 break;
6368 switch (that->mMachineState)
6369 {
6370 case MachineState_Teleporting:
6371 that->setMachineState(MachineState_TeleportingPausedVM);
6372 break;
6373
6374 case MachineState_LiveSnapshotting:
6375 that->setMachineState(MachineState_Saving);
6376 break;
6377
6378 case MachineState_TeleportingPausedVM:
6379 case MachineState_Saving:
6380 /* ignore */
6381 break;
6382
6383 default:
6384 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6385 that->setMachineState(MachineState_Paused);
6386 break;
6387 }
6388 break;
6389 }
6390
6391 case VMSTATE_RUNNING:
6392 {
6393 if ( aOldState == VMSTATE_POWERING_ON
6394 || aOldState == VMSTATE_RESUMING)
6395 {
6396 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6397
6398 if (that->mVMStateChangeCallbackDisabled)
6399 break;
6400
6401 Assert( ( ( that->mMachineState == MachineState_Starting
6402 || that->mMachineState == MachineState_Paused)
6403 && aOldState == VMSTATE_POWERING_ON)
6404 || ( ( that->mMachineState == MachineState_Restoring
6405 || that->mMachineState == MachineState_TeleportingIn
6406 || that->mMachineState == MachineState_Paused
6407 || that->mMachineState == MachineState_Saving
6408 )
6409 && aOldState == VMSTATE_RESUMING));
6410
6411 that->setMachineState(MachineState_Running);
6412 }
6413
6414 break;
6415 }
6416
6417 case VMSTATE_RUNNING_LS:
6418 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6419 || that->mMachineState == MachineState_Teleporting,
6420 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6421 break;
6422
6423 case VMSTATE_FATAL_ERROR:
6424 {
6425 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6426
6427 if (that->mVMStateChangeCallbackDisabled)
6428 break;
6429
6430 /* Fatal errors are only for running VMs. */
6431 Assert(Global::IsOnline(that->mMachineState));
6432
6433 /* Note! 'Pause' is used here in want of something better. There
6434 * are currently only two places where fatal errors might be
6435 * raised, so it is not worth adding a new externally
6436 * visible state for this yet. */
6437 that->setMachineState(MachineState_Paused);
6438 break;
6439 }
6440
6441 case VMSTATE_GURU_MEDITATION:
6442 {
6443 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6444
6445 if (that->mVMStateChangeCallbackDisabled)
6446 break;
6447
6448 /* Guru are only for running VMs */
6449 Assert(Global::IsOnline(that->mMachineState));
6450
6451 that->setMachineState(MachineState_Stuck);
6452 break;
6453 }
6454
6455 default: /* shut up gcc */
6456 break;
6457 }
6458}
6459
6460#ifdef VBOX_WITH_USB
6461
6462/**
6463 * Sends a request to VMM to attach the given host device.
6464 * After this method succeeds, the attached device will appear in the
6465 * mUSBDevices collection.
6466 *
6467 * @param aHostDevice device to attach
6468 *
6469 * @note Synchronously calls EMT.
6470 * @note Must be called from under this object's lock.
6471 */
6472HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6473{
6474 AssertReturn(aHostDevice, E_FAIL);
6475 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6476
6477 /* still want a lock object because we need to leave it */
6478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 HRESULT hrc;
6481
6482 /*
6483 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6484 * method in EMT (using usbAttachCallback()).
6485 */
6486 Bstr BstrAddress;
6487 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6488 ComAssertComRCRetRC(hrc);
6489
6490 Utf8Str Address(BstrAddress);
6491
6492 Bstr id;
6493 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6494 ComAssertComRCRetRC(hrc);
6495 Guid uuid(id);
6496
6497 BOOL fRemote = FALSE;
6498 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6499 ComAssertComRCRetRC(hrc);
6500
6501 /* protect mpVM */
6502 AutoVMCaller autoVMCaller(this);
6503 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6504
6505 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6506 Address.raw(), uuid.ptr()));
6507
6508 /* leave the lock before a VMR3* call (EMT will call us back)! */
6509 alock.leave();
6510
6511/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6512 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6513 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
6514
6515 /* restore the lock */
6516 alock.enter();
6517
6518 /* hrc is S_OK here */
6519
6520 if (RT_FAILURE(vrc))
6521 {
6522 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6523 Address.raw(), uuid.ptr(), vrc));
6524
6525 switch (vrc)
6526 {
6527 case VERR_VUSB_NO_PORTS:
6528 hrc = setError(E_FAIL,
6529 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6530 break;
6531 case VERR_VUSB_USBFS_PERMISSION:
6532 hrc = setError(E_FAIL,
6533 tr("Not permitted to open the USB device, check usbfs options"));
6534 break;
6535 default:
6536 hrc = setError(E_FAIL,
6537 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6538 vrc);
6539 break;
6540 }
6541 }
6542
6543 return hrc;
6544}
6545
6546/**
6547 * USB device attach callback used by AttachUSBDevice().
6548 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6549 * so we don't use AutoCaller and don't care about reference counters of
6550 * interface pointers passed in.
6551 *
6552 * @thread EMT
6553 * @note Locks the console object for writing.
6554 */
6555//static
6556DECLCALLBACK(int)
6557Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6558{
6559 LogFlowFuncEnter();
6560 LogFlowFunc(("that={%p}\n", that));
6561
6562 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6563
6564 void *pvRemoteBackend = NULL;
6565 if (aRemote)
6566 {
6567 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6568 Guid guid(*aUuid);
6569
6570 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6571 if (!pvRemoteBackend)
6572 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6573 }
6574
6575 USHORT portVersion = 1;
6576 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6577 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6578 Assert(portVersion == 1 || portVersion == 2);
6579
6580 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6581 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6582 if (RT_SUCCESS(vrc))
6583 {
6584 /* Create a OUSBDevice and add it to the device list */
6585 ComObjPtr<OUSBDevice> device;
6586 device.createObject();
6587 hrc = device->init(aHostDevice);
6588 AssertComRC(hrc);
6589
6590 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6591 that->mUSBDevices.push_back(device);
6592 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6593
6594 /* notify callbacks */
6595 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6596 }
6597
6598 LogFlowFunc(("vrc=%Rrc\n", vrc));
6599 LogFlowFuncLeave();
6600 return vrc;
6601}
6602
6603/**
6604 * Sends a request to VMM to detach the given host device. After this method
6605 * succeeds, the detached device will disappear from the mUSBDevices
6606 * collection.
6607 *
6608 * @param aIt Iterator pointing to the device to detach.
6609 *
6610 * @note Synchronously calls EMT.
6611 * @note Must be called from under this object's lock.
6612 */
6613HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6614{
6615 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6616
6617 /* still want a lock object because we need to leave it */
6618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6619
6620 /* protect mpVM */
6621 AutoVMCaller autoVMCaller(this);
6622 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6623
6624 /* if the device is attached, then there must at least one USB hub. */
6625 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6626
6627 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6628 (*aIt)->id().raw()));
6629
6630 /* leave the lock before a VMR3* call (EMT will call us back)! */
6631 alock.leave();
6632
6633/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6634 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6635 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6636 ComAssertRCRet(vrc, E_FAIL);
6637
6638 return S_OK;
6639}
6640
6641/**
6642 * USB device detach callback used by DetachUSBDevice().
6643 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6644 * so we don't use AutoCaller and don't care about reference counters of
6645 * interface pointers passed in.
6646 *
6647 * @thread EMT
6648 * @note Locks the console object for writing.
6649 */
6650//static
6651DECLCALLBACK(int)
6652Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6653{
6654 LogFlowFuncEnter();
6655 LogFlowFunc(("that={%p}\n", that));
6656
6657 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6658 ComObjPtr<OUSBDevice> device = **aIt;
6659
6660 /*
6661 * If that was a remote device, release the backend pointer.
6662 * The pointer was requested in usbAttachCallback.
6663 */
6664 BOOL fRemote = FALSE;
6665
6666 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6667 ComAssertComRC(hrc2);
6668
6669 if (fRemote)
6670 {
6671 Guid guid(*aUuid);
6672 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6673 }
6674
6675 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6676
6677 if (RT_SUCCESS(vrc))
6678 {
6679 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6680
6681 /* Remove the device from the collection */
6682 that->mUSBDevices.erase(*aIt);
6683 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6684
6685 /* notify callbacks */
6686 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6687 }
6688
6689 LogFlowFunc(("vrc=%Rrc\n", vrc));
6690 LogFlowFuncLeave();
6691 return vrc;
6692}
6693
6694#endif /* VBOX_WITH_USB */
6695#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6696
6697/**
6698 * Helper function to handle host interface device creation and attachment.
6699 *
6700 * @param networkAdapter the network adapter which attachment should be reset
6701 * @return COM status code
6702 *
6703 * @note The caller must lock this object for writing.
6704 *
6705 * @todo Move this back into the driver!
6706 */
6707HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6708{
6709 LogFlowThisFunc(("\n"));
6710 /* sanity check */
6711 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6712
6713# ifdef VBOX_STRICT
6714 /* paranoia */
6715 NetworkAttachmentType_T attachment;
6716 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6717 Assert(attachment == NetworkAttachmentType_Bridged);
6718# endif /* VBOX_STRICT */
6719
6720 HRESULT rc = S_OK;
6721
6722 ULONG slot = 0;
6723 rc = networkAdapter->COMGETTER(Slot)(&slot);
6724 AssertComRC(rc);
6725
6726# ifdef RT_OS_LINUX
6727 /*
6728 * Allocate a host interface device
6729 */
6730 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6731 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6732 if (RT_SUCCESS(rcVBox))
6733 {
6734 /*
6735 * Set/obtain the tap interface.
6736 */
6737 struct ifreq IfReq;
6738 memset(&IfReq, 0, sizeof(IfReq));
6739 /* The name of the TAP interface we are using */
6740 Bstr tapDeviceName;
6741 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6742 if (FAILED(rc))
6743 tapDeviceName.setNull(); /* Is this necessary? */
6744 if (tapDeviceName.isEmpty())
6745 {
6746 LogRel(("No TAP device name was supplied.\n"));
6747 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6748 }
6749
6750 if (SUCCEEDED(rc))
6751 {
6752 /* If we are using a static TAP device then try to open it. */
6753 Utf8Str str(tapDeviceName);
6754 if (str.length() <= sizeof(IfReq.ifr_name))
6755 strcpy(IfReq.ifr_name, str.raw());
6756 else
6757 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6758 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6759 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6760 if (rcVBox != 0)
6761 {
6762 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6763 rc = setError(E_FAIL,
6764 tr("Failed to open the host network interface %ls"),
6765 tapDeviceName.raw());
6766 }
6767 }
6768 if (SUCCEEDED(rc))
6769 {
6770 /*
6771 * Make it pollable.
6772 */
6773 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6774 {
6775 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6776 /*
6777 * Here is the right place to communicate the TAP file descriptor and
6778 * the host interface name to the server if/when it becomes really
6779 * necessary.
6780 */
6781 maTAPDeviceName[slot] = tapDeviceName;
6782 rcVBox = VINF_SUCCESS;
6783 }
6784 else
6785 {
6786 int iErr = errno;
6787
6788 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6789 rcVBox = VERR_HOSTIF_BLOCKING;
6790 rc = setError(E_FAIL,
6791 tr("could not set up the host networking device for non blocking access: %s"),
6792 strerror(errno));
6793 }
6794 }
6795 }
6796 else
6797 {
6798 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6799 switch (rcVBox)
6800 {
6801 case VERR_ACCESS_DENIED:
6802 /* will be handled by our caller */
6803 rc = rcVBox;
6804 break;
6805 default:
6806 rc = setError(E_FAIL,
6807 tr("Could not set up the host networking device: %Rrc"),
6808 rcVBox);
6809 break;
6810 }
6811 }
6812
6813# elif defined(RT_OS_FREEBSD)
6814 /*
6815 * Set/obtain the tap interface.
6816 */
6817 /* The name of the TAP interface we are using */
6818 Bstr tapDeviceName;
6819 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6820 if (FAILED(rc))
6821 tapDeviceName.setNull(); /* Is this necessary? */
6822 if (tapDeviceName.isEmpty())
6823 {
6824 LogRel(("No TAP device name was supplied.\n"));
6825 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6826 }
6827 char szTapdev[1024] = "/dev/";
6828 /* If we are using a static TAP device then try to open it. */
6829 Utf8Str str(tapDeviceName);
6830 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6831 strcat(szTapdev, str.raw());
6832 else
6833 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6834 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6835 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6836
6837 if (RT_SUCCESS(rcVBox))
6838 maTAPDeviceName[slot] = tapDeviceName;
6839 else
6840 {
6841 switch (rcVBox)
6842 {
6843 case VERR_ACCESS_DENIED:
6844 /* will be handled by our caller */
6845 rc = rcVBox;
6846 break;
6847 default:
6848 rc = setError(E_FAIL,
6849 tr("Failed to open the host network interface %ls"),
6850 tapDeviceName.raw());
6851 break;
6852 }
6853 }
6854# else
6855# error "huh?"
6856# endif
6857 /* in case of failure, cleanup. */
6858 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6859 {
6860 LogRel(("General failure attaching to host interface\n"));
6861 rc = setError(E_FAIL,
6862 tr("General failure attaching to host interface"));
6863 }
6864 LogFlowThisFunc(("rc=%d\n", rc));
6865 return rc;
6866}
6867
6868
6869/**
6870 * Helper function to handle detachment from a host interface
6871 *
6872 * @param networkAdapter the network adapter which attachment should be reset
6873 * @return COM status code
6874 *
6875 * @note The caller must lock this object for writing.
6876 *
6877 * @todo Move this back into the driver!
6878 */
6879HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6880{
6881 /* sanity check */
6882 LogFlowThisFunc(("\n"));
6883 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6884
6885 HRESULT rc = S_OK;
6886# ifdef VBOX_STRICT
6887 /* paranoia */
6888 NetworkAttachmentType_T attachment;
6889 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6890 Assert(attachment == NetworkAttachmentType_Bridged);
6891# endif /* VBOX_STRICT */
6892
6893 ULONG slot = 0;
6894 rc = networkAdapter->COMGETTER(Slot)(&slot);
6895 AssertComRC(rc);
6896
6897 /* is there an open TAP device? */
6898 if (maTapFD[slot] != NIL_RTFILE)
6899 {
6900 /*
6901 * Close the file handle.
6902 */
6903 Bstr tapDeviceName, tapTerminateApplication;
6904 bool isStatic = true;
6905 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6906 if (FAILED(rc) || tapDeviceName.isEmpty())
6907 {
6908 /* If the name is empty, this is a dynamic TAP device, so close it now,
6909 so that the termination script can remove the interface. Otherwise we still
6910 need the FD to pass to the termination script. */
6911 isStatic = false;
6912 int rcVBox = RTFileClose(maTapFD[slot]);
6913 AssertRC(rcVBox);
6914 maTapFD[slot] = NIL_RTFILE;
6915 }
6916 if (isStatic)
6917 {
6918 /* If we are using a static TAP device, we close it now, after having called the
6919 termination script. */
6920 int rcVBox = RTFileClose(maTapFD[slot]);
6921 AssertRC(rcVBox);
6922 }
6923 /* the TAP device name and handle are no longer valid */
6924 maTapFD[slot] = NIL_RTFILE;
6925 maTAPDeviceName[slot] = "";
6926 }
6927 LogFlowThisFunc(("returning %d\n", rc));
6928 return rc;
6929}
6930
6931#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6932
6933/**
6934 * Called at power down to terminate host interface networking.
6935 *
6936 * @note The caller must lock this object for writing.
6937 */
6938HRESULT Console::powerDownHostInterfaces()
6939{
6940 LogFlowThisFunc(("\n"));
6941
6942 /* sanity check */
6943 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6944
6945 /*
6946 * host interface termination handling
6947 */
6948 HRESULT rc;
6949 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6950 {
6951 ComPtr<INetworkAdapter> networkAdapter;
6952 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6953 if (FAILED(rc)) break;
6954
6955 BOOL enabled = FALSE;
6956 networkAdapter->COMGETTER(Enabled)(&enabled);
6957 if (!enabled)
6958 continue;
6959
6960 NetworkAttachmentType_T attachment;
6961 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6962 if (attachment == NetworkAttachmentType_Bridged)
6963 {
6964#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6965 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6966 if (FAILED(rc2) && SUCCEEDED(rc))
6967 rc = rc2;
6968#endif
6969 }
6970 }
6971
6972 return rc;
6973}
6974
6975
6976/**
6977 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
6978 * and VMR3Teleport.
6979 *
6980 * @param pVM The VM handle.
6981 * @param uPercent Completetion precentage (0-100).
6982 * @param pvUser Pointer to the VMProgressTask structure.
6983 * @return VINF_SUCCESS.
6984 */
6985/*static*/
6986DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6987{
6988 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6989 AssertReturn(task, VERR_INVALID_PARAMETER);
6990
6991 /* update the progress object */
6992 if (task->mProgress)
6993 task->mProgress->SetCurrentOperationProgress(uPercent);
6994
6995 return VINF_SUCCESS;
6996}
6997
6998/**
6999 * @copydoc FNVMATERROR
7000 *
7001 * @remarks Might be some tiny serialization concerns with access to the string
7002 * object here...
7003 */
7004/*static*/ DECLCALLBACK(void)
7005Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
7006 const char *pszErrorFmt, va_list va)
7007{
7008 Utf8Str *pErrorText = (Utf8Str *)pvUser;
7009 AssertPtr(pErrorText);
7010
7011 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
7012 va_list va2;
7013 va_copy(va2, va);
7014
7015 /* Append to any the existing error message. */
7016 if (pErrorText->length())
7017 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
7018 pszErrorFmt, &va2, rc, rc);
7019 else
7020 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
7021
7022 va_end(va2);
7023}
7024
7025/**
7026 * VM runtime error callback function.
7027 * See VMSetRuntimeError for the detailed description of parameters.
7028 *
7029 * @param pVM The VM handle.
7030 * @param pvUser The user argument.
7031 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
7032 * @param pszErrorId Error ID string.
7033 * @param pszFormat Error message format string.
7034 * @param va Error message arguments.
7035 * @thread EMT.
7036 */
7037/* static */ DECLCALLBACK(void)
7038Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
7039 const char *pszErrorId,
7040 const char *pszFormat, va_list va)
7041{
7042 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
7043 LogFlowFuncEnter();
7044
7045 Console *that = static_cast<Console *>(pvUser);
7046 AssertReturnVoid(that);
7047
7048 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
7049
7050 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
7051 fFatal, pszErrorId, message.raw()));
7052
7053 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
7054
7055 LogFlowFuncLeave();
7056}
7057
7058/**
7059 * Captures USB devices that match filters of the VM.
7060 * Called at VM startup.
7061 *
7062 * @param pVM The VM handle.
7063 *
7064 * @note The caller must lock this object for writing.
7065 */
7066HRESULT Console::captureUSBDevices(PVM pVM)
7067{
7068 LogFlowThisFunc(("\n"));
7069
7070 /* sanity check */
7071 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
7072
7073 /* If the machine has an USB controller, ask the USB proxy service to
7074 * capture devices */
7075 PPDMIBASE pBase;
7076 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7077 if (RT_SUCCESS(vrc))
7078 {
7079 /* leave the lock before calling Host in VBoxSVC since Host may call
7080 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7081 * produce an inter-process dead-lock otherwise. */
7082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7083 alock.leave();
7084
7085 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7086 ComAssertComRCRetRC(hrc);
7087 }
7088 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7089 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7090 vrc = VINF_SUCCESS;
7091 else
7092 AssertRC(vrc);
7093
7094 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7095}
7096
7097
7098/**
7099 * Detach all USB device which are attached to the VM for the
7100 * purpose of clean up and such like.
7101 *
7102 * @note The caller must lock this object for writing.
7103 */
7104void Console::detachAllUSBDevices(bool aDone)
7105{
7106 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7107
7108 /* sanity check */
7109 AssertReturnVoid(isWriteLockOnCurrentThread());
7110
7111 mUSBDevices.clear();
7112
7113 /* leave the lock before calling Host in VBoxSVC since Host may call
7114 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7115 * produce an inter-process dead-lock otherwise. */
7116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7117 alock.leave();
7118
7119 mControl->DetachAllUSBDevices(aDone);
7120}
7121
7122/**
7123 * @note Locks this object for writing.
7124 */
7125void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7126{
7127 LogFlowThisFuncEnter();
7128 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7129
7130 AutoCaller autoCaller(this);
7131 if (!autoCaller.isOk())
7132 {
7133 /* Console has been already uninitialized, deny request */
7134 AssertMsgFailed(("Console is already uninitialized\n"));
7135 LogFlowThisFunc(("Console is already uninitialized\n"));
7136 LogFlowThisFuncLeave();
7137 return;
7138 }
7139
7140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7141
7142 /*
7143 * Mark all existing remote USB devices as dirty.
7144 */
7145 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7146 it != mRemoteUSBDevices.end();
7147 ++it)
7148 {
7149 (*it)->dirty(true);
7150 }
7151
7152 /*
7153 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7154 */
7155 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7156 VRDPUSBDEVICEDESC *e = pDevList;
7157
7158 /* The cbDevList condition must be checked first, because the function can
7159 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7160 */
7161 while (cbDevList >= 2 && e->oNext)
7162 {
7163 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7164 e->idVendor, e->idProduct,
7165 e->oProduct? (char *)e + e->oProduct: ""));
7166
7167 bool fNewDevice = true;
7168
7169 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7170 it != mRemoteUSBDevices.end();
7171 ++it)
7172 {
7173 if ((*it)->devId() == e->id
7174 && (*it)->clientId() == u32ClientId)
7175 {
7176 /* The device is already in the list. */
7177 (*it)->dirty(false);
7178 fNewDevice = false;
7179 break;
7180 }
7181 }
7182
7183 if (fNewDevice)
7184 {
7185 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7186 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7187
7188 /* Create the device object and add the new device to list. */
7189 ComObjPtr<RemoteUSBDevice> device;
7190 device.createObject();
7191 device->init(u32ClientId, e);
7192
7193 mRemoteUSBDevices.push_back(device);
7194
7195 /* Check if the device is ok for current USB filters. */
7196 BOOL fMatched = FALSE;
7197 ULONG fMaskedIfs = 0;
7198
7199 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7200
7201 AssertComRC(hrc);
7202
7203 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7204
7205 if (fMatched)
7206 {
7207 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7208
7209 /// @todo (r=dmik) warning reporting subsystem
7210
7211 if (hrc == S_OK)
7212 {
7213 LogFlowThisFunc(("Device attached\n"));
7214 device->captured(true);
7215 }
7216 }
7217 }
7218
7219 if (cbDevList < e->oNext)
7220 {
7221 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7222 cbDevList, e->oNext));
7223 break;
7224 }
7225
7226 cbDevList -= e->oNext;
7227
7228 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7229 }
7230
7231 /*
7232 * Remove dirty devices, that is those which are not reported by the server anymore.
7233 */
7234 for (;;)
7235 {
7236 ComObjPtr<RemoteUSBDevice> device;
7237
7238 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7239 while (it != mRemoteUSBDevices.end())
7240 {
7241 if ((*it)->dirty())
7242 {
7243 device = *it;
7244 break;
7245 }
7246
7247 ++ it;
7248 }
7249
7250 if (!device)
7251 {
7252 break;
7253 }
7254
7255 USHORT vendorId = 0;
7256 device->COMGETTER(VendorId)(&vendorId);
7257
7258 USHORT productId = 0;
7259 device->COMGETTER(ProductId)(&productId);
7260
7261 Bstr product;
7262 device->COMGETTER(Product)(product.asOutParam());
7263
7264 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7265 vendorId, productId, product.raw()));
7266
7267 /* Detach the device from VM. */
7268 if (device->captured())
7269 {
7270 Bstr uuid;
7271 device->COMGETTER(Id)(uuid.asOutParam());
7272 onUSBDeviceDetach(uuid, NULL);
7273 }
7274
7275 /* And remove it from the list. */
7276 mRemoteUSBDevices.erase(it);
7277 }
7278
7279 LogFlowThisFuncLeave();
7280}
7281
7282/**
7283 * Thread function which starts the VM (also from saved state) and
7284 * track progress.
7285 *
7286 * @param Thread The thread id.
7287 * @param pvUser Pointer to a VMPowerUpTask structure.
7288 * @return VINF_SUCCESS (ignored).
7289 *
7290 * @note Locks the Console object for writing.
7291 */
7292/*static*/
7293DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7294{
7295 LogFlowFuncEnter();
7296
7297 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7298 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7299
7300 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7301 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7302
7303#if defined(RT_OS_WINDOWS)
7304 {
7305 /* initialize COM */
7306 HRESULT hrc = CoInitializeEx(NULL,
7307 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7308 COINIT_SPEED_OVER_MEMORY);
7309 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7310 }
7311#endif
7312
7313 HRESULT rc = S_OK;
7314 int vrc = VINF_SUCCESS;
7315
7316 /* Set up a build identifier so that it can be seen from core dumps what
7317 * exact build was used to produce the core. */
7318 static char saBuildID[40];
7319 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7320 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7321
7322 ComObjPtr<Console> console = task->mConsole;
7323
7324 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7325
7326 /* The lock is also used as a signal from the task initiator (which
7327 * releases it only after RTThreadCreate()) that we can start the job */
7328 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7329
7330 /* sanity */
7331 Assert(console->mpVM == NULL);
7332
7333 try
7334 {
7335 /* wait for auto reset ops to complete so that we can successfully lock
7336 * the attached hard disks by calling LockMedia() below */
7337 for (VMPowerUpTask::ProgressList::const_iterator
7338 it = task->hardDiskProgresses.begin();
7339 it != task->hardDiskProgresses.end(); ++ it)
7340 {
7341 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7342 AssertComRC(rc2);
7343 }
7344
7345 /*
7346 * Lock attached media. This method will also check their accessibility.
7347 * If we're a teleporter, we'll have to postpone this action so we can
7348 * migrate between local processes.
7349 *
7350 * Note! The media will be unlocked automatically by
7351 * SessionMachine::setMachineState() when the VM is powered down.
7352 */
7353 if (!task->mTeleporterEnabled)
7354 {
7355 rc = console->mControl->LockMedia();
7356 if (FAILED(rc)) throw rc;
7357 }
7358
7359#ifdef VBOX_WITH_VRDP
7360
7361 /* Create the VRDP server. In case of headless operation, this will
7362 * also create the framebuffer, required at VM creation.
7363 */
7364 ConsoleVRDPServer *server = console->consoleVRDPServer();
7365 Assert(server);
7366
7367 /* Does VRDP server call Console from the other thread?
7368 * Not sure (and can change), so leave the lock just in case.
7369 */
7370 alock.leave();
7371 vrc = server->Launch();
7372 alock.enter();
7373
7374 if (vrc == VERR_NET_ADDRESS_IN_USE)
7375 {
7376 Utf8Str errMsg;
7377 Bstr bstr;
7378 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7379 Utf8Str ports = bstr;
7380 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7381 ports.raw());
7382 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7383 vrc, errMsg.raw()));
7384 }
7385 else if (RT_FAILURE(vrc))
7386 {
7387 Utf8Str errMsg;
7388 switch (vrc)
7389 {
7390 case VERR_FILE_NOT_FOUND:
7391 {
7392 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7393 break;
7394 }
7395 default:
7396 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7397 vrc);
7398 }
7399 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7400 vrc, errMsg.raw()));
7401 throw setError(E_FAIL, errMsg.c_str());
7402 }
7403
7404#endif /* VBOX_WITH_VRDP */
7405
7406 ComPtr<IMachine> pMachine = console->machine();
7407 ULONG cCpus = 1;
7408 pMachine->COMGETTER(CPUCount)(&cCpus);
7409
7410 /*
7411 * Create the VM
7412 */
7413 PVM pVM;
7414 /*
7415 * leave the lock since EMT will call Console. It's safe because
7416 * mMachineState is either Starting or Restoring state here.
7417 */
7418 alock.leave();
7419
7420 vrc = VMR3Create(cCpus, Console::genericVMSetErrorCallback, &task->mErrorMsg,
7421 task->mConfigConstructor, static_cast<Console *>(console),
7422 &pVM);
7423
7424 alock.enter();
7425
7426#ifdef VBOX_WITH_VRDP
7427 /* Enable client connections to the server. */
7428 console->consoleVRDPServer()->EnableConnections();
7429#endif /* VBOX_WITH_VRDP */
7430
7431 if (RT_SUCCESS(vrc))
7432 {
7433 do
7434 {
7435 /*
7436 * Register our load/save state file handlers
7437 */
7438 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7439 NULL, NULL, NULL,
7440 NULL, saveStateFileExec, NULL,
7441 NULL, loadStateFileExec, NULL,
7442 static_cast<Console *>(console));
7443 AssertRCBreak(vrc);
7444
7445 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7446 AssertRC(vrc);
7447 if (RT_FAILURE(vrc))
7448 break;
7449
7450 /*
7451 * Synchronize debugger settings
7452 */
7453 MachineDebugger *machineDebugger = console->getMachineDebugger();
7454 if (machineDebugger)
7455 {
7456 machineDebugger->flushQueuedSettings();
7457 }
7458
7459 /*
7460 * Shared Folders
7461 */
7462 if (console->getVMMDev()->isShFlActive())
7463 {
7464 /* Does the code below call Console from the other thread?
7465 * Not sure, so leave the lock just in case. */
7466 alock.leave();
7467
7468 for (SharedFolderDataMap::const_iterator
7469 it = task->mSharedFolders.begin();
7470 it != task->mSharedFolders.end();
7471 ++ it)
7472 {
7473 rc = console->createSharedFolder((*it).first, (*it).second);
7474 if (FAILED(rc)) break;
7475 }
7476 if (FAILED(rc)) break;
7477
7478 /* enter the lock again */
7479 alock.enter();
7480 }
7481
7482 /*
7483 * Capture USB devices.
7484 */
7485 rc = console->captureUSBDevices(pVM);
7486 if (FAILED(rc)) break;
7487
7488 /* leave the lock before a lengthy operation */
7489 alock.leave();
7490
7491 /* Load saved state? */
7492 if (task->mSavedStateFile.length())
7493 {
7494 LogFlowFunc(("Restoring saved state from '%s'...\n",
7495 task->mSavedStateFile.raw()));
7496
7497 vrc = VMR3LoadFromFile(pVM,
7498 task->mSavedStateFile.c_str(),
7499 Console::stateProgressCallback,
7500 static_cast<VMProgressTask*>(task.get()));
7501
7502 if (RT_SUCCESS(vrc))
7503 {
7504 if (task->mStartPaused)
7505 /* done */
7506 console->setMachineState(MachineState_Paused);
7507 else
7508 {
7509 /* Start/Resume the VM execution */
7510 vrc = VMR3Resume(pVM);
7511 AssertRC(vrc);
7512 }
7513 }
7514
7515 /* Power off in case we failed loading or resuming the VM */
7516 if (RT_FAILURE(vrc))
7517 {
7518 int vrc2 = VMR3PowerOff(pVM);
7519 AssertRC(vrc2);
7520 }
7521 }
7522 else if (task->mTeleporterEnabled)
7523 {
7524 /* -> ConsoleImplTeleporter.cpp */
7525 bool fPowerOffOnFailure;
7526 rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7527 task->mProgress, &fPowerOffOnFailure);
7528 if (FAILED(rc) && fPowerOffOnFailure)
7529 {
7530 ErrorInfoKeeper eik;
7531 int vrc2 = VMR3PowerOff(pVM);
7532 AssertRC(vrc2);
7533 }
7534 }
7535 else if (task->mStartPaused)
7536 /* done */
7537 console->setMachineState(MachineState_Paused);
7538 else
7539 {
7540 /* Power on the VM (i.e. start executing) */
7541 vrc = VMR3PowerOn(pVM);
7542 AssertRC(vrc);
7543 }
7544
7545 /* enter the lock again */
7546 alock.enter();
7547 }
7548 while (0);
7549
7550 /* On failure, destroy the VM */
7551 if (FAILED(rc) || RT_FAILURE(vrc))
7552 {
7553 /* preserve existing error info */
7554 ErrorInfoKeeper eik;
7555
7556 /* powerDown() will call VMR3Destroy() and do all necessary
7557 * cleanup (VRDP, USB devices) */
7558 HRESULT rc2 = console->powerDown();
7559 AssertComRC(rc2);
7560 }
7561 else
7562 {
7563 /*
7564 * Deregister the VMSetError callback. This is necessary as the
7565 * pfnVMAtError() function passed to VMR3Create() is supposed to
7566 * be sticky but our error callback isn't.
7567 */
7568 alock.leave();
7569 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7570 /** @todo register another VMSetError callback? */
7571 alock.enter();
7572 }
7573 }
7574 else
7575 {
7576 /*
7577 * If VMR3Create() failed it has released the VM memory.
7578 */
7579 console->mpVM = NULL;
7580 }
7581
7582 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7583 {
7584 /* If VMR3Create() or one of the other calls in this function fail,
7585 * an appropriate error message has been set in task->mErrorMsg.
7586 * However since that happens via a callback, the rc status code in
7587 * this function is not updated.
7588 */
7589 if (!task->mErrorMsg.length())
7590 {
7591 /* If the error message is not set but we've got a failure,
7592 * convert the VBox status code into a meaningful error message.
7593 * This becomes unused once all the sources of errors set the
7594 * appropriate error message themselves.
7595 */
7596 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7597 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7598 vrc);
7599 }
7600
7601 /* Set the error message as the COM error.
7602 * Progress::notifyComplete() will pick it up later. */
7603 throw setError(E_FAIL, task->mErrorMsg.c_str());
7604 }
7605 }
7606 catch (HRESULT aRC) { rc = aRC; }
7607
7608 if ( console->mMachineState == MachineState_Starting
7609 || console->mMachineState == MachineState_Restoring
7610 || console->mMachineState == MachineState_TeleportingIn
7611 )
7612 {
7613 /* We are still in the Starting/Restoring state. This means one of:
7614 *
7615 * 1) we failed before VMR3Create() was called;
7616 * 2) VMR3Create() failed.
7617 *
7618 * In both cases, there is no need to call powerDown(), but we still
7619 * need to go back to the PoweredOff/Saved state. Reuse
7620 * vmstateChangeCallback() for that purpose.
7621 */
7622
7623 /* preserve existing error info */
7624 ErrorInfoKeeper eik;
7625
7626 Assert(console->mpVM == NULL);
7627 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7628 console);
7629 }
7630
7631 /*
7632 * Evaluate the final result. Note that the appropriate mMachineState value
7633 * is already set by vmstateChangeCallback() in all cases.
7634 */
7635
7636 /* leave the lock, don't need it any more */
7637 alock.leave();
7638
7639 if (SUCCEEDED(rc))
7640 {
7641 /* Notify the progress object of the success */
7642 task->mProgress->notifyComplete(S_OK);
7643 }
7644 else
7645 {
7646 /* The progress object will fetch the current error info */
7647 task->mProgress->notifyComplete(rc);
7648 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7649 }
7650
7651 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7652 console->mControl->EndPowerUp(rc);
7653
7654#if defined(RT_OS_WINDOWS)
7655 /* uninitialize COM */
7656 CoUninitialize();
7657#endif
7658
7659 LogFlowFuncLeave();
7660
7661 return VINF_SUCCESS;
7662}
7663
7664
7665/**
7666 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7667 *
7668 * @param pConsole Reference to the console object.
7669 * @param pVM The VM handle.
7670 * @param lInstance The instance of the controller.
7671 * @param pcszDevice The name of the controller type.
7672 * @param enmBus The storage bus type of the controller.
7673 * @param fSetupMerge Whether to set up a medium merge
7674 * @param uMergeSource Merge source image index
7675 * @param uMergeTarget Merge target image index
7676 * @param aMediumAtt The medium attachment.
7677 * @param aMachineState The current machine state.
7678 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7679 * @return VBox status code.
7680 */
7681/* static */
7682DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7683 PVM pVM,
7684 const char *pcszDevice,
7685 unsigned uInstance,
7686 StorageBus_T enmBus,
7687 bool fUseHostIOCache,
7688 bool fSetupMerge,
7689 unsigned uMergeSource,
7690 unsigned uMergeTarget,
7691 IMediumAttachment *aMediumAtt,
7692 MachineState_T aMachineState,
7693 HRESULT *phrc)
7694{
7695 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7696
7697 int rc;
7698 HRESULT hrc;
7699 Bstr bstr;
7700 *phrc = S_OK;
7701#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7702#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7703
7704 /* Ignore attachments other than hard disks, since at the moment they are
7705 * not subject to snapshotting in general. */
7706 DeviceType_T lType;
7707 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7708 if (lType != DeviceType_HardDisk)
7709 return VINF_SUCCESS;
7710
7711 /* Determine the base path for the device instance. */
7712 PCFGMNODE pCtlInst;
7713 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7714 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7715
7716 /* Update the device instance configuration. */
7717 rc = pConsole->configMediumAttachment(pCtlInst,
7718 pcszDevice,
7719 uInstance,
7720 enmBus,
7721 fUseHostIOCache,
7722 fSetupMerge,
7723 uMergeSource,
7724 uMergeTarget,
7725 aMediumAtt,
7726 aMachineState,
7727 phrc,
7728 true /* fAttachDetach */,
7729 false /* fForceUnmount */,
7730 pVM,
7731 NULL /* paLedDevType */);
7732 /** @todo this dumps everything attached to this device instance, which
7733 * is more than necessary. Dumping the changed LUN would be enough. */
7734 CFGMR3Dump(pCtlInst);
7735 RC_CHECK();
7736
7737#undef RC_CHECK
7738#undef H
7739
7740 LogFlowFunc(("Returns success\n"));
7741 return VINF_SUCCESS;
7742}
7743
7744/**
7745 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7746 */
7747static void takesnapshotProgressCancelCallback(void *pvUser)
7748{
7749 PVM pVM = (PVM)pvUser;
7750 SSMR3Cancel(pVM);
7751}
7752
7753/**
7754 * Worker thread created by Console::TakeSnapshot.
7755 * @param Thread The current thread (ignored).
7756 * @param pvUser The task.
7757 * @return VINF_SUCCESS (ignored).
7758 */
7759/*static*/
7760DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7761{
7762 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7763
7764 // taking a snapshot consists of the following:
7765
7766 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7767 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7768 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7769 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7770 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7771
7772 Console *that = pTask->mConsole;
7773 bool fBeganTakingSnapshot = false;
7774 bool fSuspenededBySave = false;
7775
7776 AutoCaller autoCaller(that);
7777 if (FAILED(autoCaller.rc()))
7778 {
7779 that->mptrCancelableProgress.setNull();
7780 return autoCaller.rc();
7781 }
7782
7783 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7784
7785 HRESULT rc = S_OK;
7786
7787 try
7788 {
7789 /* STEP 1 + 2:
7790 * request creating the diff images on the server and create the snapshot object
7791 * (this will set the machine state to Saving on the server to block
7792 * others from accessing this machine)
7793 */
7794 rc = that->mControl->BeginTakingSnapshot(that,
7795 pTask->bstrName,
7796 pTask->bstrDescription,
7797 pTask->mProgress,
7798 pTask->fTakingSnapshotOnline,
7799 pTask->bstrSavedStateFile.asOutParam());
7800 if (FAILED(rc))
7801 throw rc;
7802
7803 fBeganTakingSnapshot = true;
7804
7805 /*
7806 * state file is non-null only when the VM is paused
7807 * (i.e. creating a snapshot online)
7808 */
7809 ComAssertThrow( (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7810 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline),
7811 rc = E_FAIL);
7812
7813 /* sync the state with the server */
7814 if (pTask->lastMachineState == MachineState_Running)
7815 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7816 else
7817 that->setMachineStateLocally(MachineState_Saving);
7818
7819 // STEP 3: save the VM state (if online)
7820 if (pTask->fTakingSnapshotOnline)
7821 {
7822 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7823
7824 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7825 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7826 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7827
7828 alock.leave();
7829 LogFlowFunc(("VMR3Save...\n"));
7830 int vrc = VMR3Save(that->mpVM,
7831 strSavedStateFile.c_str(),
7832 true /*fContinueAfterwards*/,
7833 Console::stateProgressCallback,
7834 (void*)pTask,
7835 &fSuspenededBySave);
7836 alock.enter();
7837 if (RT_FAILURE(vrc))
7838 throw setError(E_FAIL,
7839 tr("Failed to save the machine state to '%s' (%Rrc)"),
7840 strSavedStateFile.c_str(), vrc);
7841
7842 pTask->mProgress->setCancelCallback(NULL, NULL);
7843 if (!pTask->mProgress->notifyPointOfNoReturn())
7844 throw setError(E_FAIL, tr("Cancelled"));
7845 that->mptrCancelableProgress.setNull();
7846
7847 // STEP 4: reattach hard disks
7848 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7849
7850 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7851 1); // operation weight, same as computed when setting up progress object
7852
7853 com::SafeIfaceArray<IMediumAttachment> atts;
7854 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7855 if (FAILED(rc))
7856 throw rc;
7857
7858 for (size_t i = 0;
7859 i < atts.size();
7860 ++i)
7861 {
7862 ComPtr<IStorageController> controller;
7863 Bstr controllerName;
7864 ULONG lInstance;
7865 StorageControllerType_T enmController;
7866 StorageBus_T enmBus;
7867 BOOL fUseHostIOCache;
7868
7869 /*
7870 * We can't pass a storage controller object directly
7871 * (g++ complains about not being able to pass non POD types through '...')
7872 * so we have to query needed values here and pass them.
7873 */
7874 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
7875 if (FAILED(rc))
7876 throw rc;
7877
7878 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7879 if (FAILED(rc))
7880 throw rc;
7881
7882 rc = controller->COMGETTER(ControllerType)(&enmController);
7883 if (FAILED(rc))
7884 throw rc;
7885 rc = controller->COMGETTER(Instance)(&lInstance);
7886 if (FAILED(rc))
7887 throw rc;
7888 rc = controller->COMGETTER(Bus)(&enmBus);
7889 if (FAILED(rc))
7890 throw rc;
7891 rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
7892 if (FAILED(rc))
7893 throw rc;
7894
7895 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7896
7897 /*
7898 * don't leave the lock since reconfigureMediumAttachment
7899 * isn't going to need the Console lock.
7900 */
7901 vrc = VMR3ReqCallWait(that->mpVM,
7902 VMCPUID_ANY,
7903 (PFNRT)reconfigureMediumAttachment,
7904 12,
7905 that,
7906 that->mpVM,
7907 pcszDevice,
7908 lInstance,
7909 enmBus,
7910 fUseHostIOCache,
7911 false /* fSetupMerge */,
7912 0 /* uMergeSource */,
7913 0 /* uMergeTarget */,
7914 atts[i],
7915 that->mMachineState,
7916 &rc);
7917 if (RT_FAILURE(vrc))
7918 throw setError(E_FAIL, Console::tr("%Rrc"), vrc);
7919 if (FAILED(rc))
7920 throw rc;
7921 }
7922 }
7923
7924 /*
7925 * finalize the requested snapshot object.
7926 * This will reset the machine state to the state it had right
7927 * before calling mControl->BeginTakingSnapshot().
7928 */
7929 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
7930 // do not throw rc here because we can't call EndTakingSnapshot() twice
7931 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7932 }
7933 catch (HRESULT rcThrown)
7934 {
7935 /* preserve existing error info */
7936 ErrorInfoKeeper eik;
7937
7938 if (fBeganTakingSnapshot)
7939 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
7940
7941 rc = rcThrown;
7942 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7943 }
7944 Assert(alock.isWriteLockOnCurrentThread());
7945
7946 if (FAILED(rc)) /* Must come before calling setMachineState. */
7947 pTask->mProgress->notifyComplete(rc);
7948
7949 /*
7950 * Fix up the machine state.
7951 *
7952 * For live snapshots we do all the work, for the two other variantions we
7953 * just update the local copy.
7954 */
7955 MachineState_T enmMachineState;
7956 that->mMachine->COMGETTER(State)(&enmMachineState);
7957 if ( that->mMachineState == MachineState_LiveSnapshotting
7958 || that->mMachineState == MachineState_Saving)
7959 {
7960
7961 if (!pTask->fTakingSnapshotOnline)
7962 that->setMachineStateLocally(pTask->lastMachineState);
7963 else if (SUCCEEDED(rc))
7964 {
7965 Assert( pTask->lastMachineState == MachineState_Running
7966 || pTask->lastMachineState == MachineState_Paused);
7967 Assert(that->mMachineState == MachineState_Saving);
7968 if (pTask->lastMachineState == MachineState_Running)
7969 {
7970 LogFlowFunc(("VMR3Resume...\n"));
7971 alock.leave();
7972 int vrc = VMR3Resume(that->mpVM);
7973 alock.enter();
7974 if (RT_FAILURE(vrc))
7975 {
7976 rc = setError(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
7977 pTask->mProgress->notifyComplete(rc);
7978 if (that->mMachineState == MachineState_Saving)
7979 that->setMachineStateLocally(MachineState_Paused);
7980 }
7981 }
7982 else
7983 that->setMachineStateLocally(MachineState_Paused);
7984 }
7985 else
7986 {
7987 /** @todo this could probably be made more generic and reused elsewhere. */
7988 /* paranoid cleanup on for a failed online snapshot. */
7989 VMSTATE enmVMState = VMR3GetState(that->mpVM);
7990 switch (enmVMState)
7991 {
7992 case VMSTATE_RUNNING:
7993 case VMSTATE_RUNNING_LS:
7994 case VMSTATE_DEBUGGING:
7995 case VMSTATE_DEBUGGING_LS:
7996 case VMSTATE_POWERING_OFF:
7997 case VMSTATE_POWERING_OFF_LS:
7998 case VMSTATE_RESETTING:
7999 case VMSTATE_RESETTING_LS:
8000 Assert(!fSuspenededBySave);
8001 that->setMachineState(MachineState_Running);
8002 break;
8003
8004 case VMSTATE_GURU_MEDITATION:
8005 case VMSTATE_GURU_MEDITATION_LS:
8006 that->setMachineState(MachineState_Stuck);
8007 break;
8008
8009 case VMSTATE_FATAL_ERROR:
8010 case VMSTATE_FATAL_ERROR_LS:
8011 if (pTask->lastMachineState == MachineState_Paused)
8012 that->setMachineStateLocally(pTask->lastMachineState);
8013 else
8014 that->setMachineState(MachineState_Paused);
8015 break;
8016
8017 default:
8018 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8019 case VMSTATE_SUSPENDED:
8020 case VMSTATE_SUSPENDED_LS:
8021 case VMSTATE_SUSPENDING:
8022 case VMSTATE_SUSPENDING_LS:
8023 case VMSTATE_SUSPENDING_EXT_LS:
8024 if (fSuspenededBySave)
8025 {
8026 Assert(pTask->lastMachineState == MachineState_Running);
8027 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8028 alock.leave();
8029 int vrc = VMR3Resume(that->mpVM);
8030 alock.enter();
8031 AssertLogRelRC(vrc);
8032 if (RT_FAILURE(vrc))
8033 that->setMachineState(MachineState_Paused);
8034 }
8035 else if (pTask->lastMachineState == MachineState_Paused)
8036 that->setMachineStateLocally(pTask->lastMachineState);
8037 else
8038 that->setMachineState(MachineState_Paused);
8039 break;
8040 }
8041
8042 }
8043 }
8044 /*else: somebody else has change the state... Leave it. */
8045
8046 /* check the remote state to see that we got it right. */
8047 that->mMachine->COMGETTER(State)(&enmMachineState);
8048 AssertLogRelMsg(that->mMachineState == enmMachineState,
8049 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8050 Global::stringifyMachineState(enmMachineState) ));
8051
8052
8053 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8054 pTask->mProgress->notifyComplete(rc);
8055
8056 delete pTask;
8057
8058 LogFlowFuncLeave();
8059 return VINF_SUCCESS;
8060}
8061
8062/**
8063 * Thread for executing the saved state operation.
8064 *
8065 * @param Thread The thread handle.
8066 * @param pvUser Pointer to a VMSaveTask structure.
8067 * @return VINF_SUCCESS (ignored).
8068 *
8069 * @note Locks the Console object for writing.
8070 */
8071/*static*/
8072DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8073{
8074 LogFlowFuncEnter();
8075
8076 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8077 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8078
8079 Assert(task->mSavedStateFile.length());
8080 Assert(!task->mProgress.isNull());
8081
8082 const ComObjPtr<Console> &that = task->mConsole;
8083 Utf8Str errMsg;
8084 HRESULT rc = S_OK;
8085
8086 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
8087
8088 bool fSuspenededBySave;
8089 int vrc = VMR3Save(that->mpVM,
8090 task->mSavedStateFile.c_str(),
8091 false, /*fContinueAfterwards*/
8092 Console::stateProgressCallback,
8093 static_cast<VMProgressTask*>(task.get()),
8094 &fSuspenededBySave);
8095 if (RT_FAILURE(vrc))
8096 {
8097 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8098 task->mSavedStateFile.raw(), vrc);
8099 rc = E_FAIL;
8100 }
8101 Assert(!fSuspenededBySave);
8102
8103 /* lock the console once we're going to access it */
8104 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8105
8106 /*
8107 * finalize the requested save state procedure.
8108 * In case of success, the server will set the machine state to Saved;
8109 * in case of failure it will reset the it to the state it had right
8110 * before calling mControl->BeginSavingState().
8111 */
8112 that->mControl->EndSavingState(SUCCEEDED(rc));
8113
8114 /* synchronize the state with the server */
8115 if (!FAILED(rc))
8116 {
8117 /*
8118 * The machine has been successfully saved, so power it down
8119 * (vmstateChangeCallback() will set state to Saved on success).
8120 * Note: we release the task's VM caller, otherwise it will
8121 * deadlock.
8122 */
8123 task->releaseVMCaller();
8124
8125 rc = that->powerDown();
8126 }
8127
8128 /* notify the progress object about operation completion */
8129 if (SUCCEEDED(rc))
8130 task->mProgress->notifyComplete(S_OK);
8131 else
8132 {
8133 if (errMsg.length())
8134 task->mProgress->notifyComplete(rc,
8135 COM_IIDOF(IConsole),
8136 (CBSTR)Console::getComponentName(),
8137 errMsg.c_str());
8138 else
8139 task->mProgress->notifyComplete(rc);
8140 }
8141
8142 LogFlowFuncLeave();
8143 return VINF_SUCCESS;
8144}
8145
8146/**
8147 * Thread for powering down the Console.
8148 *
8149 * @param Thread The thread handle.
8150 * @param pvUser Pointer to the VMTask structure.
8151 * @return VINF_SUCCESS (ignored).
8152 *
8153 * @note Locks the Console object for writing.
8154 */
8155/*static*/
8156DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8157{
8158 LogFlowFuncEnter();
8159
8160 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8161 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8162
8163 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8164
8165 const ComObjPtr<Console> &that = task->mConsole;
8166
8167 /* Note: no need to use addCaller() to protect Console because VMTask does
8168 * that */
8169
8170 /* wait until the method tat started us returns */
8171 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8172
8173 /* release VM caller to avoid the powerDown() deadlock */
8174 task->releaseVMCaller();
8175
8176 that->powerDown(task->mProgress);
8177
8178 LogFlowFuncLeave();
8179 return VINF_SUCCESS;
8180}
8181
8182/**
8183 * The Main status driver instance data.
8184 */
8185typedef struct DRVMAINSTATUS
8186{
8187 /** The LED connectors. */
8188 PDMILEDCONNECTORS ILedConnectors;
8189 /** Pointer to the LED ports interface above us. */
8190 PPDMILEDPORTS pLedPorts;
8191 /** Pointer to the array of LED pointers. */
8192 PPDMLED *papLeds;
8193 /** The unit number corresponding to the first entry in the LED array. */
8194 RTUINT iFirstLUN;
8195 /** The unit number corresponding to the last entry in the LED array.
8196 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8197 RTUINT iLastLUN;
8198} DRVMAINSTATUS, *PDRVMAINSTATUS;
8199
8200
8201/**
8202 * Notification about a unit which have been changed.
8203 *
8204 * The driver must discard any pointers to data owned by
8205 * the unit and requery it.
8206 *
8207 * @param pInterface Pointer to the interface structure containing the called function pointer.
8208 * @param iLUN The unit number.
8209 */
8210DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8211{
8212 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8213 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8214 {
8215 PPDMLED pLed;
8216 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8217 if (RT_FAILURE(rc))
8218 pLed = NULL;
8219 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8220 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8221 }
8222}
8223
8224
8225/**
8226 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8227 */
8228DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8229{
8230 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8231 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8232 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8233 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8234 return NULL;
8235}
8236
8237
8238/**
8239 * Destruct a status driver instance.
8240 *
8241 * @returns VBox status.
8242 * @param pDrvIns The driver instance data.
8243 */
8244DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8245{
8246 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8247 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8248 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8249
8250 if (pData->papLeds)
8251 {
8252 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8253 while (iLed-- > 0)
8254 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8255 }
8256}
8257
8258
8259/**
8260 * Construct a status driver instance.
8261 *
8262 * @copydoc FNPDMDRVCONSTRUCT
8263 */
8264DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8265{
8266 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8267 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8268 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8269
8270 /*
8271 * Validate configuration.
8272 */
8273 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8274 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8275 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8276 ("Configuration error: Not possible to attach anything to this driver!\n"),
8277 VERR_PDM_DRVINS_NO_ATTACH);
8278
8279 /*
8280 * Data.
8281 */
8282 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8283 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8284
8285 /*
8286 * Read config.
8287 */
8288 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8289 if (RT_FAILURE(rc))
8290 {
8291 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8292 return rc;
8293 }
8294
8295 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8296 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8297 pData->iFirstLUN = 0;
8298 else if (RT_FAILURE(rc))
8299 {
8300 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8301 return rc;
8302 }
8303
8304 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8305 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8306 pData->iLastLUN = 0;
8307 else if (RT_FAILURE(rc))
8308 {
8309 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8310 return rc;
8311 }
8312 if (pData->iFirstLUN > pData->iLastLUN)
8313 {
8314 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8315 return VERR_GENERAL_FAILURE;
8316 }
8317
8318 /*
8319 * Get the ILedPorts interface of the above driver/device and
8320 * query the LEDs we want.
8321 */
8322 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8323 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8324 VERR_PDM_MISSING_INTERFACE_ABOVE);
8325
8326 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8327 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8328
8329 return VINF_SUCCESS;
8330}
8331
8332
8333/**
8334 * Keyboard driver registration record.
8335 */
8336const PDMDRVREG Console::DrvStatusReg =
8337{
8338 /* u32Version */
8339 PDM_DRVREG_VERSION,
8340 /* szName */
8341 "MainStatus",
8342 /* szRCMod */
8343 "",
8344 /* szR0Mod */
8345 "",
8346 /* pszDescription */
8347 "Main status driver (Main as in the API).",
8348 /* fFlags */
8349 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8350 /* fClass. */
8351 PDM_DRVREG_CLASS_STATUS,
8352 /* cMaxInstances */
8353 ~0,
8354 /* cbInstance */
8355 sizeof(DRVMAINSTATUS),
8356 /* pfnConstruct */
8357 Console::drvStatus_Construct,
8358 /* pfnDestruct */
8359 Console::drvStatus_Destruct,
8360 /* pfnRelocate */
8361 NULL,
8362 /* pfnIOCtl */
8363 NULL,
8364 /* pfnPowerOn */
8365 NULL,
8366 /* pfnReset */
8367 NULL,
8368 /* pfnSuspend */
8369 NULL,
8370 /* pfnResume */
8371 NULL,
8372 /* pfnAttach */
8373 NULL,
8374 /* pfnDetach */
8375 NULL,
8376 /* pfnPowerOff */
8377 NULL,
8378 /* pfnSoftReset */
8379 NULL,
8380 /* u32EndVersion */
8381 PDM_DRVREG_VERSION
8382};
8383
8384/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette