VirtualBox

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

Last change on this file since 30583 was 30564, checked in by vboxsync, 14 years ago

Main: safearray event attributes work

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