VirtualBox

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

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

Main: events work, firing of events enabled

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