VirtualBox

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

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

Main: remove SupportErrorInfo template magic

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

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