VirtualBox

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

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

Main: console COM events, cleanup, locking

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

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