VirtualBox

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

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

Main: another minor BSTR/Bstr fix

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

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