VirtualBox

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

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

Main: fixed non-Windows burns

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