VirtualBox

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

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

Main: events bits

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 275.6 KB
Line 
1/* $Id: ConsoleImpl.cpp 30591 2010-07-02 18:41:57Z 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(5000); /* Wait up to 5 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 *aCanShow = TRUE;
5000 }
5001 else
5002 {
5003 while (it != mCallbacks.end())
5004 {
5005 if (it->isWanted(ConsoleCallbackRegistration::kOnShowWindow))
5006 {
5007 ULONG64 winId = 0;
5008 HRESULT hrc = it->ptrICb->OnShowWindow(&winId);
5009 hrc = it->handleResult(ConsoleCallbackRegistration::kOnCanShowWindow, hrc);
5010 if (FAILED_DEAD_INTERFACE(hrc))
5011 {
5012 it = this->mCallbacks.erase(it);
5013 continue;
5014 }
5015 AssertComRC(hrc);
5016 if (FAILED(hrc))
5017 return hrc;
5018
5019 /* only one callback may return non-null winId */
5020 Assert(*aWinId == 0 || winId == 0);
5021 if (*aWinId == 0)
5022 *aWinId = winId;
5023 }
5024 ++it;
5025 }
5026 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, UINT64_C(0));
5027 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
5028 //Assert(fDelivered);
5029 if (fDelivered)
5030 {
5031 ComPtr<IEvent> aEvent;
5032 evDesc.getEvent(aEvent.asOutParam());
5033 ComPtr<IShowWindowEvent> aShowEvent = aEvent;
5034 ULONG64 aEvWinId = 0;
5035 if (aShowEvent)
5036 {
5037 aShowEvent->COMGETTER(WinId)(&aEvWinId);
5038 if ((aEvWinId != 0) && (*aWinId == 0))
5039 *aWinId = aEvWinId;
5040 }
5041 else
5042 Assert(FALSE);
5043 }
5044 }
5045
5046 return S_OK;
5047}
5048
5049// private methods
5050////////////////////////////////////////////////////////////////////////////////
5051
5052/**
5053 * Increases the usage counter of the mpVM pointer. Guarantees that
5054 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
5055 * is called.
5056 *
5057 * If this method returns a failure, the caller is not allowed to use mpVM
5058 * and may return the failed result code to the upper level. This method sets
5059 * the extended error info on failure if \a aQuiet is false.
5060 *
5061 * Setting \a aQuiet to true is useful for methods that don't want to return
5062 * the failed result code to the caller when this method fails (e.g. need to
5063 * silently check for the mpVM availability).
5064 *
5065 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
5066 * returned instead of asserting. Having it false is intended as a sanity check
5067 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
5068 *
5069 * @param aQuiet true to suppress setting error info
5070 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
5071 * (otherwise this method will assert if mpVM is NULL)
5072 *
5073 * @note Locks this object for writing.
5074 */
5075HRESULT Console::addVMCaller(bool aQuiet /* = false */,
5076 bool aAllowNullVM /* = false */)
5077{
5078 AutoCaller autoCaller(this);
5079 AssertComRCReturnRC(autoCaller.rc());
5080
5081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5082
5083 if (mVMDestroying)
5084 {
5085 /* powerDown() is waiting for all callers to finish */
5086 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5087 tr("The virtual machine is being powered down"));
5088 }
5089
5090 if (mpVM == NULL)
5091 {
5092 Assert(aAllowNullVM == true);
5093
5094 /* The machine is not powered up */
5095 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
5096 tr("The virtual machine is not powered up"));
5097 }
5098
5099 ++ mVMCallers;
5100
5101 return S_OK;
5102}
5103
5104/**
5105 * Decreases the usage counter of the mpVM pointer. Must always complete
5106 * the addVMCaller() call after the mpVM pointer is no more necessary.
5107 *
5108 * @note Locks this object for writing.
5109 */
5110void Console::releaseVMCaller()
5111{
5112 AutoCaller autoCaller(this);
5113 AssertComRCReturnVoid(autoCaller.rc());
5114
5115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5116
5117 AssertReturnVoid(mpVM != NULL);
5118
5119 Assert(mVMCallers > 0);
5120 --mVMCallers;
5121
5122 if (mVMCallers == 0 && mVMDestroying)
5123 {
5124 /* inform powerDown() there are no more callers */
5125 RTSemEventSignal(mVMZeroCallersSem);
5126 }
5127}
5128
5129/**
5130 * Initialize the release logging facility. In case something
5131 * goes wrong, there will be no release logging. Maybe in the future
5132 * we can add some logic to use different file names in this case.
5133 * Note that the logic must be in sync with Machine::DeleteSettings().
5134 */
5135HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
5136{
5137 HRESULT hrc = S_OK;
5138
5139 Bstr logFolder;
5140 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
5141 if (FAILED(hrc)) return hrc;
5142
5143 Utf8Str logDir = logFolder;
5144
5145 /* make sure the Logs folder exists */
5146 Assert(logDir.length());
5147 if (!RTDirExists(logDir.c_str()))
5148 RTDirCreateFullPath(logDir.c_str(), 0777);
5149
5150 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
5151 logDir.raw(), RTPATH_DELIMITER);
5152 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
5153 logDir.raw(), RTPATH_DELIMITER);
5154
5155 /*
5156 * Age the old log files
5157 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
5158 * Overwrite target files in case they exist.
5159 */
5160 ComPtr<IVirtualBox> virtualBox;
5161 aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5162 ComPtr<ISystemProperties> systemProperties;
5163 virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5164 ULONG cHistoryFiles = 3;
5165 systemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
5166 if (cHistoryFiles)
5167 {
5168 for (int i = cHistoryFiles-1; i >= 0; i--)
5169 {
5170 Utf8Str *files[] = { &logFile, &pngFile };
5171 Utf8Str oldName, newName;
5172
5173 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
5174 {
5175 if (i > 0)
5176 oldName = Utf8StrFmt("%s.%d", files[j]->raw(), i);
5177 else
5178 oldName = *files[j];
5179 newName = Utf8StrFmt("%s.%d", files[j]->raw(), i + 1);
5180 /* If the old file doesn't exist, delete the new file (if it
5181 * exists) to provide correct rotation even if the sequence is
5182 * broken */
5183 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
5184 == VERR_FILE_NOT_FOUND)
5185 RTFileDelete(newName.c_str());
5186 }
5187 }
5188 }
5189
5190 PRTLOGGER loggerRelease;
5191 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
5192 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
5193#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5194 fFlags |= RTLOGFLAGS_USECRLF;
5195#endif
5196 char szError[RTPATH_MAX + 128] = "";
5197 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
5198 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
5199 RTLOGDEST_FILE, szError, sizeof(szError), logFile.raw());
5200 if (RT_SUCCESS(vrc))
5201 {
5202 /* some introductory information */
5203 RTTIMESPEC timeSpec;
5204 char szTmp[256];
5205 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
5206 RTLogRelLogger(loggerRelease, 0, ~0U,
5207 "VirtualBox %s r%u %s (%s %s) release log\n"
5208#ifdef VBOX_BLEEDING_EDGE
5209 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
5210#endif
5211 "Log opened %s\n",
5212 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
5213 __DATE__, __TIME__, szTmp);
5214
5215 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
5216 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5217 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
5218 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
5219 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5220 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
5221 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
5222 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5223 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
5224 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
5225 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5226 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
5227 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
5228 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5229 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp);
5230 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
5231 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5232 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp);
5233
5234 ComPtr<IHost> host;
5235 virtualBox->COMGETTER(Host)(host.asOutParam());
5236 ULONG cMbHostRam = 0;
5237 ULONG cMbHostRamAvail = 0;
5238 host->COMGETTER(MemorySize)(&cMbHostRam);
5239 host->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5240 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5241 cMbHostRam, cMbHostRamAvail);
5242
5243#if defined(RT_OS_WINDOWS) && HC_ARCH_BITS == 32
5244 /* @todo move this in RT, but too lazy now */
5245 uint32_t maxRAMArch;
5246 SYSTEM_INFO sysInfo;
5247 GetSystemInfo(&sysInfo);
5248
5249 if (sysInfo.lpMaximumApplicationAddress >= (LPVOID)0xC0000000) /* 3.0 GB */
5250 maxRAMArch = UINT32_C(2560);
5251 else
5252 if (sysInfo.lpMaximumApplicationAddress > (LPVOID)0xA0000000) /* 2.5 GB */
5253 maxRAMArch = UINT32_C(2048);
5254 else
5255 maxRAMArch = UINT32_C(1500);
5256
5257 RTLogRelLogger(loggerRelease, 0, ~0U, "Maximum user application address: 0x%p\n", sysInfo.lpMaximumApplicationAddress);
5258 RTLogRelLogger(loggerRelease, 0, ~0U, "Maximum allowed guest RAM size: %dMB\n", maxRAMArch);
5259#endif
5260
5261 /* the package type is interesting for Linux distributions */
5262 char szExecName[RTPATH_MAX];
5263 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
5264 RTLogRelLogger(loggerRelease, 0, ~0U,
5265 "Executable: %s\n"
5266 "Process ID: %u\n"
5267 "Package type: %s"
5268#ifdef VBOX_OSE
5269 " (OSE)"
5270#endif
5271 "\n",
5272 pszExecName ? pszExecName : "unknown",
5273 RTProcSelf(),
5274 VBOX_PACKAGE_STRING);
5275
5276 /* register this logger as the release logger */
5277 RTLogRelSetDefaultInstance(loggerRelease);
5278 hrc = S_OK;
5279
5280 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5281 RTLogFlush(loggerRelease);
5282 }
5283 else
5284 hrc = setError(E_FAIL,
5285 tr("Failed to open release log (%s, %Rrc)"),
5286 szError, vrc);
5287
5288 /* If we've made any directory changes, flush the directory to increase
5289 the likelyhood that the log file will be usable after a system panic.
5290
5291 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5292 is missing. Just don't have too high hopes for this to help. */
5293 if (SUCCEEDED(hrc) || cHistoryFiles)
5294 RTDirFlush(logDir.c_str());
5295
5296 return hrc;
5297}
5298
5299/**
5300 * Common worker for PowerUp and PowerUpPaused.
5301 *
5302 * @returns COM status code.
5303 *
5304 * @param aProgress Where to return the progress object.
5305 * @param aPaused true if PowerUpPaused called.
5306 *
5307 * @todo move down to powerDown();
5308 */
5309HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5310{
5311 if (aProgress == NULL)
5312 return E_POINTER;
5313
5314 LogFlowThisFuncEnter();
5315 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5316
5317 AutoCaller autoCaller(this);
5318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5319
5320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5321
5322 if (Global::IsOnlineOrTransient(mMachineState))
5323 return setError(VBOX_E_INVALID_VM_STATE,
5324 tr("The virtual machine is already running or busy (machine state: %s)"),
5325 Global::stringifyMachineState(mMachineState));
5326
5327 HRESULT rc = S_OK;
5328
5329 /* the network cards will undergo a quick consistency check */
5330 for (ULONG slot = 0;
5331 slot < SchemaDefs::NetworkAdapterCount;
5332 ++slot)
5333 {
5334 ComPtr<INetworkAdapter> adapter;
5335 mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
5336 BOOL enabled = FALSE;
5337 adapter->COMGETTER(Enabled)(&enabled);
5338 if (!enabled)
5339 continue;
5340
5341 NetworkAttachmentType_T netattach;
5342 adapter->COMGETTER(AttachmentType)(&netattach);
5343 switch (netattach)
5344 {
5345 case NetworkAttachmentType_Bridged:
5346 {
5347#ifdef RT_OS_WINDOWS
5348 /* a valid host interface must have been set */
5349 Bstr hostif;
5350 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
5351 if (!hostif)
5352 {
5353 return setError(VBOX_E_HOST_ERROR,
5354 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5355 }
5356 ComPtr<IVirtualBox> virtualBox;
5357 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5358 ComPtr<IHost> host;
5359 virtualBox->COMGETTER(Host)(host.asOutParam());
5360 ComPtr<IHostNetworkInterface> hostInterface;
5361 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
5362 {
5363 return setError(VBOX_E_HOST_ERROR,
5364 tr("VM cannot start because the host interface '%ls' does not exist"),
5365 hostif.raw());
5366 }
5367#endif /* RT_OS_WINDOWS */
5368 break;
5369 }
5370 default:
5371 break;
5372 }
5373 }
5374
5375 /* Read console data stored in the saved state file (if not yet done) */
5376 rc = loadDataFromSavedState();
5377 if (FAILED(rc)) return rc;
5378
5379 /* Check all types of shared folders and compose a single list */
5380 SharedFolderDataMap sharedFolders;
5381 {
5382 /* first, insert global folders */
5383 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5384 it != mGlobalSharedFolders.end(); ++ it)
5385 sharedFolders[it->first] = it->second;
5386
5387 /* second, insert machine folders */
5388 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5389 it != mMachineSharedFolders.end(); ++ it)
5390 sharedFolders[it->first] = it->second;
5391
5392 /* third, insert console folders */
5393 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5394 it != mSharedFolders.end(); ++ it)
5395 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(), it->second->isWritable());
5396 }
5397
5398 Bstr savedStateFile;
5399
5400 /*
5401 * Saved VMs will have to prove that their saved states seem kosher.
5402 */
5403 if (mMachineState == MachineState_Saved)
5404 {
5405 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5406 if (FAILED(rc)) return rc;
5407 ComAssertRet(!!savedStateFile, E_FAIL);
5408 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5409 if (RT_FAILURE(vrc))
5410 return setError(VBOX_E_FILE_ERROR,
5411 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5412 savedStateFile.raw(), vrc);
5413 }
5414
5415 /* test and clear the TeleporterEnabled property */
5416 BOOL fTeleporterEnabled;
5417 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5418 if (FAILED(rc)) return rc;
5419#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5420 if (fTeleporterEnabled)
5421 {
5422 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5423 if (FAILED(rc)) return rc;
5424 }
5425#endif
5426
5427 /* create a progress object to track progress of this operation */
5428 ComObjPtr<Progress> powerupProgress;
5429 powerupProgress.createObject();
5430 Bstr progressDesc;
5431 if (mMachineState == MachineState_Saved)
5432 progressDesc = tr("Restoring virtual machine");
5433 else if (fTeleporterEnabled)
5434 progressDesc = tr("Teleporting virtual machine");
5435 else
5436 progressDesc = tr("Starting virtual machine");
5437 if (mMachineState == MachineState_Saved || !fTeleporterEnabled)
5438 rc = powerupProgress->init(static_cast<IConsole *>(this),
5439 progressDesc,
5440 FALSE /* aCancelable */);
5441 else
5442 rc = powerupProgress->init(static_cast<IConsole *>(this),
5443 progressDesc,
5444 TRUE /* aCancelable */,
5445 3 /* cOperations */,
5446 10 /* ulTotalOperationsWeight */,
5447 Bstr(tr("Teleporting virtual machine")),
5448 1 /* ulFirstOperationWeight */,
5449 NULL);
5450 if (FAILED(rc))
5451 return rc;
5452
5453 /* Tell VBoxSVC and Machine about the progress object so they can combine
5454 proxy it to any openRemoteSession caller. */
5455 rc = mControl->BeginPowerUp(powerupProgress);
5456 if (FAILED(rc))
5457 {
5458 LogFlowThisFunc(("BeginPowerUp failed\n"));
5459 return rc;
5460 }
5461
5462 BOOL fCanceled;
5463 rc = powerupProgress->COMGETTER(Canceled)(&fCanceled);
5464 if (FAILED(rc))
5465 return rc;
5466 if (fCanceled)
5467 {
5468 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5469 return setError(E_FAIL, tr("Powerup was canceled"));
5470 }
5471
5472 /* setup task object and thread to carry out the operation
5473 * asynchronously */
5474
5475 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
5476 ComAssertComRCRetRC(task->rc());
5477
5478 task->mConfigConstructor = configConstructor;
5479 task->mSharedFolders = sharedFolders;
5480 task->mStartPaused = aPaused;
5481 if (mMachineState == MachineState_Saved)
5482 task->mSavedStateFile = savedStateFile;
5483 task->mTeleporterEnabled = fTeleporterEnabled;
5484
5485 /* Reset differencing hard disks for which autoReset is true,
5486 * but only if the machine has no snapshots OR the current snapshot
5487 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5488 * image of an ONLINE snapshot which contains the disk state of the machine
5489 * while it was previously running, but without the corresponding machine
5490 * state, which is equivalent to powering off a running machine and not
5491 * good idea
5492 */
5493 ComPtr<ISnapshot> pCurrentSnapshot;
5494 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5495 if (FAILED(rc)) return rc;
5496
5497 BOOL fCurrentSnapshotIsOnline = false;
5498 if (pCurrentSnapshot)
5499 {
5500 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5501 if (FAILED(rc)) return rc;
5502 }
5503
5504 if (!fCurrentSnapshotIsOnline)
5505 {
5506 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5507
5508 com::SafeIfaceArray<IMediumAttachment> atts;
5509 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5510 if (FAILED(rc)) return rc;
5511
5512 for (size_t i = 0;
5513 i < atts.size();
5514 ++i)
5515 {
5516 DeviceType_T devType;
5517 rc = atts[i]->COMGETTER(Type)(&devType);
5518 /** @todo later applies to floppies as well */
5519 if (devType == DeviceType_HardDisk)
5520 {
5521 ComPtr<IMedium> medium;
5522 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
5523 if (FAILED(rc)) return rc;
5524
5525 /* needs autoreset? */
5526 BOOL autoReset = FALSE;
5527 rc = medium->COMGETTER(AutoReset)(&autoReset);
5528 if (FAILED(rc)) return rc;
5529
5530 if (autoReset)
5531 {
5532 ComPtr<IProgress> resetProgress;
5533 rc = medium->Reset(resetProgress.asOutParam());
5534 if (FAILED(rc)) return rc;
5535
5536 /* save for later use on the powerup thread */
5537 task->hardDiskProgresses.push_back(resetProgress);
5538 }
5539 }
5540 }
5541 }
5542 else
5543 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5544
5545 rc = consoleInitReleaseLog(mMachine);
5546 if (FAILED(rc)) return rc;
5547
5548 /* pass the progress object to the caller if requested */
5549 if (aProgress)
5550 {
5551 if (task->hardDiskProgresses.size() == 0)
5552 {
5553 /* there are no other operations to track, return the powerup
5554 * progress only */
5555 powerupProgress.queryInterfaceTo(aProgress);
5556 }
5557 else
5558 {
5559 /* create a combined progress object */
5560 ComObjPtr<CombinedProgress> progress;
5561 progress.createObject();
5562 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5563 progresses.push_back(ComPtr<IProgress> (powerupProgress));
5564 rc = progress->init(static_cast<IConsole *>(this),
5565 progressDesc, progresses.begin(),
5566 progresses.end());
5567 AssertComRCReturnRC(rc);
5568 progress.queryInterfaceTo(aProgress);
5569 }
5570 }
5571
5572 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5573 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5574
5575 ComAssertMsgRCRet(vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
5576 E_FAIL);
5577
5578 /* task is now owned by powerUpThread(), so release it */
5579 task.release();
5580
5581 /* finally, set the state: no right to fail in this method afterwards
5582 * since we've already started the thread and it is now responsible for
5583 * any error reporting and appropriate state change! */
5584
5585 if (mMachineState == MachineState_Saved)
5586 setMachineState(MachineState_Restoring);
5587 else if (fTeleporterEnabled)
5588 setMachineState(MachineState_TeleportingIn);
5589 else
5590 setMachineState(MachineState_Starting);
5591
5592 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5593 LogFlowThisFuncLeave();
5594 return S_OK;
5595}
5596
5597/**
5598 * Internal power off worker routine.
5599 *
5600 * This method may be called only at certain places with the following meaning
5601 * as shown below:
5602 *
5603 * - if the machine state is either Running or Paused, a normal
5604 * Console-initiated powerdown takes place (e.g. PowerDown());
5605 * - if the machine state is Saving, saveStateThread() has successfully done its
5606 * job;
5607 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5608 * to start/load the VM;
5609 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5610 * as a result of the powerDown() call).
5611 *
5612 * Calling it in situations other than the above will cause unexpected behavior.
5613 *
5614 * Note that this method should be the only one that destroys mpVM and sets it
5615 * to NULL.
5616 *
5617 * @param aProgress Progress object to run (may be NULL).
5618 *
5619 * @note Locks this object for writing.
5620 *
5621 * @note Never call this method from a thread that called addVMCaller() or
5622 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5623 * release(). Otherwise it will deadlock.
5624 */
5625HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5626{
5627 LogFlowThisFuncEnter();
5628
5629 AutoCaller autoCaller(this);
5630 AssertComRCReturnRC(autoCaller.rc());
5631
5632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5633
5634 /* Total # of steps for the progress object. Must correspond to the
5635 * number of "advance percent count" comments in this method! */
5636 enum { StepCount = 7 };
5637 /* current step */
5638 ULONG step = 0;
5639
5640 HRESULT rc = S_OK;
5641 int vrc = VINF_SUCCESS;
5642
5643 /* sanity */
5644 Assert(mVMDestroying == false);
5645
5646 Assert(mpVM != NULL);
5647
5648 AssertMsg( mMachineState == MachineState_Running
5649 || mMachineState == MachineState_Paused
5650 || mMachineState == MachineState_Stuck
5651 || mMachineState == MachineState_Starting
5652 || mMachineState == MachineState_Stopping
5653 || mMachineState == MachineState_Saving
5654 || mMachineState == MachineState_Restoring
5655 || mMachineState == MachineState_TeleportingPausedVM
5656 || mMachineState == MachineState_TeleportingIn
5657 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5658
5659 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5660 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5661
5662 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5663 * VM has already powered itself off in vmstateChangeCallback() and is just
5664 * notifying Console about that. In case of Starting or Restoring,
5665 * powerUpThread() is calling us on failure, so the VM is already off at
5666 * that point. */
5667 if ( !mVMPoweredOff
5668 && ( mMachineState == MachineState_Starting
5669 || mMachineState == MachineState_Restoring
5670 || mMachineState == MachineState_TeleportingIn)
5671 )
5672 mVMPoweredOff = true;
5673
5674 /*
5675 * Go to Stopping state if not already there.
5676 *
5677 * Note that we don't go from Saving/Restoring to Stopping because
5678 * vmstateChangeCallback() needs it to set the state to Saved on
5679 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5680 * while leaving the lock below, Saving or Restoring should be fine too.
5681 * Ditto for TeleportingPausedVM -> Teleported.
5682 */
5683 if ( mMachineState != MachineState_Saving
5684 && mMachineState != MachineState_Restoring
5685 && mMachineState != MachineState_Stopping
5686 && mMachineState != MachineState_TeleportingIn
5687 && mMachineState != MachineState_TeleportingPausedVM
5688 )
5689 setMachineState(MachineState_Stopping);
5690
5691 /* ----------------------------------------------------------------------
5692 * DONE with necessary state changes, perform the power down actions (it's
5693 * safe to leave the object lock now if needed)
5694 * ---------------------------------------------------------------------- */
5695
5696 /* Stop the VRDP server to prevent new clients connection while VM is being
5697 * powered off. */
5698 if (mConsoleVRDPServer)
5699 {
5700 LogFlowThisFunc(("Stopping VRDP server...\n"));
5701
5702 /* Leave the lock since EMT will call us back as addVMCaller()
5703 * in updateDisplayData(). */
5704 alock.leave();
5705
5706 mConsoleVRDPServer->Stop();
5707
5708 alock.enter();
5709 }
5710
5711 /* advance percent count */
5712 if (aProgress)
5713 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5714
5715
5716 /* ----------------------------------------------------------------------
5717 * Now, wait for all mpVM callers to finish their work if there are still
5718 * some on other threads. NO methods that need mpVM (or initiate other calls
5719 * that need it) may be called after this point
5720 * ---------------------------------------------------------------------- */
5721
5722 /* go to the destroying state to prevent from adding new callers */
5723 mVMDestroying = true;
5724
5725 if (mVMCallers > 0)
5726 {
5727 /* lazy creation */
5728 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5729 RTSemEventCreate(&mVMZeroCallersSem);
5730
5731 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5732 mVMCallers));
5733
5734 alock.leave();
5735
5736 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5737
5738 alock.enter();
5739 }
5740
5741 /* advance percent count */
5742 if (aProgress)
5743 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5744
5745 vrc = VINF_SUCCESS;
5746
5747 /*
5748 * Power off the VM if not already done that.
5749 * Leave the lock since EMT will call vmstateChangeCallback.
5750 *
5751 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5752 * VM-(guest-)initiated power off happened in parallel a ms before this
5753 * call. So far, we let this error pop up on the user's side.
5754 */
5755 if (!mVMPoweredOff)
5756 {
5757 LogFlowThisFunc(("Powering off the VM...\n"));
5758 alock.leave();
5759 vrc = VMR3PowerOff(mpVM);
5760 alock.enter();
5761 }
5762 else
5763 {
5764 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5765 * off. */
5766 /* reset the flag for future re-use */
5767 mVMPoweredOff = false;
5768 }
5769
5770 /* advance percent count */
5771 if (aProgress)
5772 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5773
5774#ifdef VBOX_WITH_HGCM
5775 /* Shutdown HGCM services before destroying the VM. */
5776 if (mVMMDev)
5777 {
5778 LogFlowThisFunc(("Shutdown HGCM...\n"));
5779
5780 /* Leave the lock since EMT will call us back as addVMCaller() */
5781 alock.leave();
5782
5783 mVMMDev->hgcmShutdown();
5784
5785 alock.enter();
5786 }
5787
5788 /* advance percent count */
5789 if (aProgress)
5790 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5791
5792#endif /* VBOX_WITH_HGCM */
5793
5794 LogFlowThisFunc(("Ready for VM destruction.\n"));
5795
5796 /* If we are called from Console::uninit(), then try to destroy the VM even
5797 * on failure (this will most likely fail too, but what to do?..) */
5798 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5799 {
5800 /* If the machine has an USB controller, release all USB devices
5801 * (symmetric to the code in captureUSBDevices()) */
5802 bool fHasUSBController = false;
5803 {
5804 PPDMIBASE pBase;
5805 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5806 if (RT_SUCCESS(vrc))
5807 {
5808 fHasUSBController = true;
5809 detachAllUSBDevices(false /* aDone */);
5810 }
5811 }
5812
5813 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5814 * this point). We leave the lock before calling VMR3Destroy() because
5815 * it will result into calling destructors of drivers associated with
5816 * Console children which may in turn try to lock Console (e.g. by
5817 * instantiating SafeVMPtr to access mpVM). It's safe here because
5818 * mVMDestroying is set which should prevent any activity. */
5819
5820 /* Set mpVM to NULL early just in case if some old code is not using
5821 * addVMCaller()/releaseVMCaller(). */
5822 PVM pVM = mpVM;
5823 mpVM = NULL;
5824
5825 LogFlowThisFunc(("Destroying the VM...\n"));
5826
5827 alock.leave();
5828
5829 vrc = VMR3Destroy(pVM);
5830
5831 /* take the lock again */
5832 alock.enter();
5833
5834 /* advance percent count */
5835 if (aProgress)
5836 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5837
5838 if (RT_SUCCESS(vrc))
5839 {
5840 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5841 mMachineState));
5842 /* Note: the Console-level machine state change happens on the
5843 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5844 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5845 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5846 * occurred yet. This is okay, because mMachineState is already
5847 * Stopping in this case, so any other attempt to call PowerDown()
5848 * will be rejected. */
5849 }
5850 else
5851 {
5852 /* bad bad bad, but what to do? */
5853 mpVM = pVM;
5854 rc = setError(VBOX_E_VM_ERROR,
5855 tr("Could not destroy the machine. (Error: %Rrc)"),
5856 vrc);
5857 }
5858
5859 /* Complete the detaching of the USB devices. */
5860 if (fHasUSBController)
5861 detachAllUSBDevices(true /* aDone */);
5862
5863 /* advance percent count */
5864 if (aProgress)
5865 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5866 }
5867 else
5868 {
5869 rc = setError(VBOX_E_VM_ERROR,
5870 tr("Could not power off the machine. (Error: %Rrc)"),
5871 vrc);
5872 }
5873
5874 /* Finished with destruction. Note that if something impossible happened and
5875 * we've failed to destroy the VM, mVMDestroying will remain true and
5876 * mMachineState will be something like Stopping, so most Console methods
5877 * will return an error to the caller. */
5878 if (mpVM == NULL)
5879 mVMDestroying = false;
5880
5881 if (SUCCEEDED(rc))
5882 mCallbackData.clear();
5883
5884 /* complete the progress */
5885 if (aProgress)
5886 aProgress->notifyComplete(rc);
5887
5888 LogFlowThisFuncLeave();
5889 return rc;
5890}
5891
5892/**
5893 * @note Locks this object for writing.
5894 */
5895HRESULT Console::setMachineState(MachineState_T aMachineState,
5896 bool aUpdateServer /* = true */)
5897{
5898 AutoCaller autoCaller(this);
5899 AssertComRCReturnRC(autoCaller.rc());
5900
5901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5902
5903 HRESULT rc = S_OK;
5904
5905 if (mMachineState != aMachineState)
5906 {
5907 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5908 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5909 mMachineState = aMachineState;
5910
5911 /// @todo (dmik)
5912 // possibly, we need to redo onStateChange() using the dedicated
5913 // Event thread, like it is done in VirtualBox. This will make it
5914 // much safer (no deadlocks possible if someone tries to use the
5915 // console from the callback), however, listeners will lose the
5916 // ability to synchronously react to state changes (is it really
5917 // necessary??)
5918 LogFlowThisFunc(("Doing onStateChange()...\n"));
5919 onStateChange(aMachineState);
5920 LogFlowThisFunc(("Done onStateChange()\n"));
5921
5922 if (aUpdateServer)
5923 {
5924 /* Server notification MUST be done from under the lock; otherwise
5925 * the machine state here and on the server might go out of sync
5926 * which can lead to various unexpected results (like the machine
5927 * state being >= MachineState_Running on the server, while the
5928 * session state is already SessionState_Closed at the same time
5929 * there).
5930 *
5931 * Cross-lock conditions should be carefully watched out: calling
5932 * UpdateState we will require Machine and SessionMachine locks
5933 * (remember that here we're holding the Console lock here, and also
5934 * all locks that have been entered by the thread before calling
5935 * this method).
5936 */
5937 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5938 rc = mControl->UpdateState(aMachineState);
5939 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5940 }
5941 }
5942
5943 return rc;
5944}
5945
5946/**
5947 * Searches for a shared folder with the given logical name
5948 * in the collection of shared folders.
5949 *
5950 * @param aName logical name of the shared folder
5951 * @param aSharedFolder where to return the found object
5952 * @param aSetError whether to set the error info if the folder is
5953 * not found
5954 * @return
5955 * S_OK when found or E_INVALIDARG when not found
5956 *
5957 * @note The caller must lock this object for writing.
5958 */
5959HRESULT Console::findSharedFolder(CBSTR aName,
5960 ComObjPtr<SharedFolder> &aSharedFolder,
5961 bool aSetError /* = false */)
5962{
5963 /* sanity check */
5964 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5965
5966 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5967 if (it != mSharedFolders.end())
5968 {
5969 aSharedFolder = it->second;
5970 return S_OK;
5971 }
5972
5973 if (aSetError)
5974 setError(VBOX_E_FILE_ERROR,
5975 tr("Could not find a shared folder named '%ls'."),
5976 aName);
5977
5978 return VBOX_E_FILE_ERROR;
5979}
5980
5981/**
5982 * Fetches the list of global or machine shared folders from the server.
5983 *
5984 * @param aGlobal true to fetch global folders.
5985 *
5986 * @note The caller must lock this object for writing.
5987 */
5988HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5989{
5990 /* sanity check */
5991 AssertReturn(AutoCaller(this).state() == InInit ||
5992 isWriteLockOnCurrentThread(), E_FAIL);
5993
5994 /* protect mpVM (if not NULL) */
5995 AutoVMCallerQuietWeak autoVMCaller(this);
5996
5997 HRESULT rc = S_OK;
5998
5999 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
6000
6001 if (aGlobal)
6002 {
6003 /// @todo grab & process global folders when they are done
6004 }
6005 else
6006 {
6007 SharedFolderDataMap oldFolders;
6008 if (online)
6009 oldFolders = mMachineSharedFolders;
6010
6011 mMachineSharedFolders.clear();
6012
6013 SafeIfaceArray<ISharedFolder> folders;
6014 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
6015 AssertComRCReturnRC(rc);
6016
6017 for (size_t i = 0; i < folders.size(); ++i)
6018 {
6019 ComPtr<ISharedFolder> folder = folders[i];
6020
6021 Bstr name;
6022 Bstr hostPath;
6023 BOOL writable;
6024
6025 rc = folder->COMGETTER(Name)(name.asOutParam());
6026 if (FAILED(rc)) break;
6027 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
6028 if (FAILED(rc)) break;
6029 rc = folder->COMGETTER(Writable)(&writable);
6030
6031 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable)));
6032
6033 /* send changes to HGCM if the VM is running */
6034 /// @todo report errors as runtime warnings through VMSetError
6035 if (online)
6036 {
6037 SharedFolderDataMap::iterator it = oldFolders.find(name);
6038 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
6039 {
6040 /* a new machine folder is added or
6041 * the existing machine folder is changed */
6042 if (mSharedFolders.find(name) != mSharedFolders.end())
6043 ; /* the console folder exists, nothing to do */
6044 else
6045 {
6046 /* remove the old machine folder (when changed)
6047 * or the global folder if any (when new) */
6048 if (it != oldFolders.end() ||
6049 mGlobalSharedFolders.find(name) !=
6050 mGlobalSharedFolders.end())
6051 rc = removeSharedFolder(name);
6052 /* create the new machine folder */
6053 rc = createSharedFolder(name, SharedFolderData(hostPath, writable));
6054 }
6055 }
6056 /* forget the processed (or identical) folder */
6057 if (it != oldFolders.end())
6058 oldFolders.erase(it);
6059
6060 rc = S_OK;
6061 }
6062 }
6063
6064 AssertComRCReturnRC(rc);
6065
6066 /* process outdated (removed) folders */
6067 /// @todo report errors as runtime warnings through VMSetError
6068 if (online)
6069 {
6070 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
6071 it != oldFolders.end(); ++ it)
6072 {
6073 if (mSharedFolders.find(it->first) != mSharedFolders.end())
6074 ; /* the console folder exists, nothing to do */
6075 else
6076 {
6077 /* remove the outdated machine folder */
6078 rc = removeSharedFolder(it->first);
6079 /* create the global folder if there is any */
6080 SharedFolderDataMap::const_iterator git =
6081 mGlobalSharedFolders.find(it->first);
6082 if (git != mGlobalSharedFolders.end())
6083 rc = createSharedFolder(git->first, git->second);
6084 }
6085 }
6086
6087 rc = S_OK;
6088 }
6089 }
6090
6091 return rc;
6092}
6093
6094/**
6095 * Searches for a shared folder with the given name in the list of machine
6096 * shared folders and then in the list of the global shared folders.
6097 *
6098 * @param aName Name of the folder to search for.
6099 * @param aIt Where to store the pointer to the found folder.
6100 * @return @c true if the folder was found and @c false otherwise.
6101 *
6102 * @note The caller must lock this object for reading.
6103 */
6104bool Console::findOtherSharedFolder(IN_BSTR aName,
6105 SharedFolderDataMap::const_iterator &aIt)
6106{
6107 /* sanity check */
6108 AssertReturn(isWriteLockOnCurrentThread(), false);
6109
6110 /* first, search machine folders */
6111 aIt = mMachineSharedFolders.find(aName);
6112 if (aIt != mMachineSharedFolders.end())
6113 return true;
6114
6115 /* second, search machine folders */
6116 aIt = mGlobalSharedFolders.find(aName);
6117 if (aIt != mGlobalSharedFolders.end())
6118 return true;
6119
6120 return false;
6121}
6122
6123/**
6124 * Calls the HGCM service to add a shared folder definition.
6125 *
6126 * @param aName Shared folder name.
6127 * @param aHostPath Shared folder path.
6128 *
6129 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6130 * @note Doesn't lock anything.
6131 */
6132HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
6133{
6134 ComAssertRet(aName && *aName, E_FAIL);
6135 ComAssertRet(aData.mHostPath, E_FAIL);
6136
6137 /* sanity checks */
6138 AssertReturn(mpVM, E_FAIL);
6139 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6140
6141 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
6142 SHFLSTRING *pFolderName, *pMapName;
6143 size_t cbString;
6144
6145 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
6146
6147 cbString = (RTUtf16Len(aData.mHostPath) + 1) * sizeof(RTUTF16);
6148 if (cbString >= UINT16_MAX)
6149 return setError(E_INVALIDARG, tr("The name is too long"));
6150 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6151 Assert(pFolderName);
6152 memcpy(pFolderName->String.ucs2, aData.mHostPath, cbString);
6153
6154 pFolderName->u16Size = (uint16_t)cbString;
6155 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6156
6157 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
6158 parms[0].u.pointer.addr = pFolderName;
6159 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6160
6161 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6162 if (cbString >= UINT16_MAX)
6163 {
6164 RTMemFree(pFolderName);
6165 return setError(E_INVALIDARG, tr("The host path is too long"));
6166 }
6167 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6168 Assert(pMapName);
6169 memcpy(pMapName->String.ucs2, aName, cbString);
6170
6171 pMapName->u16Size = (uint16_t)cbString;
6172 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6173
6174 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
6175 parms[1].u.pointer.addr = pMapName;
6176 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6177
6178 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
6179 parms[2].u.uint32 = aData.mWritable;
6180
6181 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6182 SHFL_FN_ADD_MAPPING,
6183 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
6184 RTMemFree(pFolderName);
6185 RTMemFree(pMapName);
6186
6187 if (RT_FAILURE(vrc))
6188 return setError(E_FAIL,
6189 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
6190 aName, aData.mHostPath.raw(), vrc);
6191
6192 return S_OK;
6193}
6194
6195/**
6196 * Calls the HGCM service to remove the shared folder definition.
6197 *
6198 * @param aName Shared folder name.
6199 *
6200 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6201 * @note Doesn't lock anything.
6202 */
6203HRESULT Console::removeSharedFolder(CBSTR aName)
6204{
6205 ComAssertRet(aName && *aName, E_FAIL);
6206
6207 /* sanity checks */
6208 AssertReturn(mpVM, E_FAIL);
6209 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6210
6211 VBOXHGCMSVCPARM parms;
6212 SHFLSTRING *pMapName;
6213 size_t cbString;
6214
6215 Log(("Removing shared folder '%ls'\n", aName));
6216
6217 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6218 if (cbString >= UINT16_MAX)
6219 return setError(E_INVALIDARG, tr("The name is too long"));
6220 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6221 Assert(pMapName);
6222 memcpy(pMapName->String.ucs2, aName, cbString);
6223
6224 pMapName->u16Size = (uint16_t)cbString;
6225 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6226
6227 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6228 parms.u.pointer.addr = pMapName;
6229 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6230
6231 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6232 SHFL_FN_REMOVE_MAPPING,
6233 1, &parms);
6234 RTMemFree(pMapName);
6235 if (RT_FAILURE(vrc))
6236 return setError(E_FAIL,
6237 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6238 aName, vrc);
6239
6240 return S_OK;
6241}
6242
6243/**
6244 * VM state callback function. Called by the VMM
6245 * using its state machine states.
6246 *
6247 * Primarily used to handle VM initiated power off, suspend and state saving,
6248 * but also for doing termination completed work (VMSTATE_TERMINATE).
6249 *
6250 * In general this function is called in the context of the EMT.
6251 *
6252 * @param aVM The VM handle.
6253 * @param aState The new state.
6254 * @param aOldState The old state.
6255 * @param aUser The user argument (pointer to the Console object).
6256 *
6257 * @note Locks the Console object for writing.
6258 */
6259DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6260 VMSTATE aState,
6261 VMSTATE aOldState,
6262 void *aUser)
6263{
6264 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6265 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6266
6267 Console *that = static_cast<Console *>(aUser);
6268 AssertReturnVoid(that);
6269
6270 AutoCaller autoCaller(that);
6271
6272 /* Note that we must let this method proceed even if Console::uninit() has
6273 * been already called. In such case this VMSTATE change is a result of:
6274 * 1) powerDown() called from uninit() itself, or
6275 * 2) VM-(guest-)initiated power off. */
6276 AssertReturnVoid( autoCaller.isOk()
6277 || autoCaller.state() == InUninit);
6278
6279 switch (aState)
6280 {
6281 /*
6282 * The VM has terminated
6283 */
6284 case VMSTATE_OFF:
6285 {
6286 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6287
6288 if (that->mVMStateChangeCallbackDisabled)
6289 break;
6290
6291 /* Do we still think that it is running? It may happen if this is a
6292 * VM-(guest-)initiated shutdown/poweroff.
6293 */
6294 if ( that->mMachineState != MachineState_Stopping
6295 && that->mMachineState != MachineState_Saving
6296 && that->mMachineState != MachineState_Restoring
6297 && that->mMachineState != MachineState_TeleportingIn
6298 && that->mMachineState != MachineState_TeleportingPausedVM
6299 && !that->mVMIsAlreadyPoweringOff
6300 )
6301 {
6302 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6303
6304 /* prevent powerDown() from calling VMR3PowerOff() again */
6305 Assert(that->mVMPoweredOff == false);
6306 that->mVMPoweredOff = true;
6307
6308 /* we are stopping now */
6309 that->setMachineState(MachineState_Stopping);
6310
6311 /* Setup task object and thread to carry out the operation
6312 * asynchronously (if we call powerDown() right here but there
6313 * is one or more mpVM callers (added with addVMCaller()) we'll
6314 * deadlock).
6315 */
6316 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6317 true /* aUsesVMPtr */));
6318
6319 /* If creating a task is falied, this can currently mean one of
6320 * two: either Console::uninit() has been called just a ms
6321 * before (so a powerDown() call is already on the way), or
6322 * powerDown() itself is being already executed. Just do
6323 * nothing.
6324 */
6325 if (!task->isOk())
6326 {
6327 LogFlowFunc(("Console is already being uninitialized.\n"));
6328 break;
6329 }
6330
6331 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6332 (void *) task.get(), 0,
6333 RTTHREADTYPE_MAIN_WORKER, 0,
6334 "VMPowerDown");
6335 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6336
6337 /* task is now owned by powerDownThread(), so release it */
6338 task.release();
6339 }
6340 break;
6341 }
6342
6343 /* The VM has been completely destroyed.
6344 *
6345 * Note: This state change can happen at two points:
6346 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6347 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6348 * called by EMT.
6349 */
6350 case VMSTATE_TERMINATED:
6351 {
6352 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6353
6354 if (that->mVMStateChangeCallbackDisabled)
6355 break;
6356
6357 /* Terminate host interface networking. If aVM is NULL, we've been
6358 * manually called from powerUpThread() either before calling
6359 * VMR3Create() or after VMR3Create() failed, so no need to touch
6360 * networking.
6361 */
6362 if (aVM)
6363 that->powerDownHostInterfaces();
6364
6365 /* From now on the machine is officially powered down or remains in
6366 * the Saved state.
6367 */
6368 switch (that->mMachineState)
6369 {
6370 default:
6371 AssertFailed();
6372 /* fall through */
6373 case MachineState_Stopping:
6374 /* successfully powered down */
6375 that->setMachineState(MachineState_PoweredOff);
6376 break;
6377 case MachineState_Saving:
6378 /* successfully saved (note that the machine is already in
6379 * the Saved state on the server due to EndSavingState()
6380 * called from saveStateThread(), so only change the local
6381 * state) */
6382 that->setMachineStateLocally(MachineState_Saved);
6383 break;
6384 case MachineState_Starting:
6385 /* failed to start, but be patient: set back to PoweredOff
6386 * (for similarity with the below) */
6387 that->setMachineState(MachineState_PoweredOff);
6388 break;
6389 case MachineState_Restoring:
6390 /* failed to load the saved state file, but be patient: set
6391 * back to Saved (to preserve the saved state file) */
6392 that->setMachineState(MachineState_Saved);
6393 break;
6394 case MachineState_TeleportingIn:
6395 /* Teleportation failed or was cancelled. Back to powered off. */
6396 that->setMachineState(MachineState_PoweredOff);
6397 break;
6398 case MachineState_TeleportingPausedVM:
6399 /* Successfully teleported the VM. */
6400 that->setMachineState(MachineState_Teleported);
6401 break;
6402 }
6403 break;
6404 }
6405
6406 case VMSTATE_SUSPENDED:
6407 {
6408 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6409
6410 if (that->mVMStateChangeCallbackDisabled)
6411 break;
6412
6413 switch (that->mMachineState)
6414 {
6415 case MachineState_Teleporting:
6416 that->setMachineState(MachineState_TeleportingPausedVM);
6417 break;
6418
6419 case MachineState_LiveSnapshotting:
6420 that->setMachineState(MachineState_Saving);
6421 break;
6422
6423 case MachineState_TeleportingPausedVM:
6424 case MachineState_Saving:
6425 case MachineState_Restoring:
6426 case MachineState_Stopping:
6427 case MachineState_TeleportingIn:
6428 /* The worker threads handles the transition. */
6429 break;
6430
6431 default:
6432 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6433 case MachineState_Running:
6434 that->setMachineState(MachineState_Paused);
6435 break;
6436 }
6437 break;
6438 }
6439
6440 case VMSTATE_SUSPENDED_LS:
6441 case VMSTATE_SUSPENDED_EXT_LS:
6442 {
6443 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6444 if (that->mVMStateChangeCallbackDisabled)
6445 break;
6446 switch (that->mMachineState)
6447 {
6448 case MachineState_Teleporting:
6449 that->setMachineState(MachineState_TeleportingPausedVM);
6450 break;
6451
6452 case MachineState_LiveSnapshotting:
6453 that->setMachineState(MachineState_Saving);
6454 break;
6455
6456 case MachineState_TeleportingPausedVM:
6457 case MachineState_Saving:
6458 /* ignore */
6459 break;
6460
6461 default:
6462 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6463 that->setMachineState(MachineState_Paused);
6464 break;
6465 }
6466 break;
6467 }
6468
6469 case VMSTATE_RUNNING:
6470 {
6471 if ( aOldState == VMSTATE_POWERING_ON
6472 || aOldState == VMSTATE_RESUMING)
6473 {
6474 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6475
6476 if (that->mVMStateChangeCallbackDisabled)
6477 break;
6478
6479 Assert( ( ( that->mMachineState == MachineState_Starting
6480 || that->mMachineState == MachineState_Paused)
6481 && aOldState == VMSTATE_POWERING_ON)
6482 || ( ( that->mMachineState == MachineState_Restoring
6483 || that->mMachineState == MachineState_TeleportingIn
6484 || that->mMachineState == MachineState_Paused
6485 || that->mMachineState == MachineState_Saving
6486 )
6487 && aOldState == VMSTATE_RESUMING));
6488
6489 that->setMachineState(MachineState_Running);
6490 }
6491
6492 break;
6493 }
6494
6495 case VMSTATE_RUNNING_LS:
6496 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6497 || that->mMachineState == MachineState_Teleporting,
6498 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6499 break;
6500
6501 case VMSTATE_FATAL_ERROR:
6502 {
6503 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6504
6505 if (that->mVMStateChangeCallbackDisabled)
6506 break;
6507
6508 /* Fatal errors are only for running VMs. */
6509 Assert(Global::IsOnline(that->mMachineState));
6510
6511 /* Note! 'Pause' is used here in want of something better. There
6512 * are currently only two places where fatal errors might be
6513 * raised, so it is not worth adding a new externally
6514 * visible state for this yet. */
6515 that->setMachineState(MachineState_Paused);
6516 break;
6517 }
6518
6519 case VMSTATE_GURU_MEDITATION:
6520 {
6521 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6522
6523 if (that->mVMStateChangeCallbackDisabled)
6524 break;
6525
6526 /* Guru are only for running VMs */
6527 Assert(Global::IsOnline(that->mMachineState));
6528
6529 that->setMachineState(MachineState_Stuck);
6530 break;
6531 }
6532
6533 default: /* shut up gcc */
6534 break;
6535 }
6536}
6537
6538#ifdef VBOX_WITH_USB
6539
6540/**
6541 * Sends a request to VMM to attach the given host device.
6542 * After this method succeeds, the attached device will appear in the
6543 * mUSBDevices collection.
6544 *
6545 * @param aHostDevice device to attach
6546 *
6547 * @note Synchronously calls EMT.
6548 * @note Must be called from under this object's lock.
6549 */
6550HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6551{
6552 AssertReturn(aHostDevice, E_FAIL);
6553 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6554
6555 /* still want a lock object because we need to leave it */
6556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 HRESULT hrc;
6559
6560 /*
6561 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6562 * method in EMT (using usbAttachCallback()).
6563 */
6564 Bstr BstrAddress;
6565 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6566 ComAssertComRCRetRC(hrc);
6567
6568 Utf8Str Address(BstrAddress);
6569
6570 Bstr id;
6571 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6572 ComAssertComRCRetRC(hrc);
6573 Guid uuid(id);
6574
6575 BOOL fRemote = FALSE;
6576 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6577 ComAssertComRCRetRC(hrc);
6578
6579 /* protect mpVM */
6580 AutoVMCaller autoVMCaller(this);
6581 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6582
6583 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6584 Address.raw(), uuid.ptr()));
6585
6586 /* leave the lock before a VMR3* call (EMT will call us back)! */
6587 alock.leave();
6588
6589/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6590 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6591 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
6592
6593 /* restore the lock */
6594 alock.enter();
6595
6596 /* hrc is S_OK here */
6597
6598 if (RT_FAILURE(vrc))
6599 {
6600 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6601 Address.raw(), uuid.ptr(), vrc));
6602
6603 switch (vrc)
6604 {
6605 case VERR_VUSB_NO_PORTS:
6606 hrc = setError(E_FAIL,
6607 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6608 break;
6609 case VERR_VUSB_USBFS_PERMISSION:
6610 hrc = setError(E_FAIL,
6611 tr("Not permitted to open the USB device, check usbfs options"));
6612 break;
6613 default:
6614 hrc = setError(E_FAIL,
6615 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6616 vrc);
6617 break;
6618 }
6619 }
6620
6621 return hrc;
6622}
6623
6624/**
6625 * USB device attach callback used by AttachUSBDevice().
6626 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6627 * so we don't use AutoCaller and don't care about reference counters of
6628 * interface pointers passed in.
6629 *
6630 * @thread EMT
6631 * @note Locks the console object for writing.
6632 */
6633//static
6634DECLCALLBACK(int)
6635Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6636{
6637 LogFlowFuncEnter();
6638 LogFlowFunc(("that={%p}\n", that));
6639
6640 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6641
6642 void *pvRemoteBackend = NULL;
6643 if (aRemote)
6644 {
6645 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6646 Guid guid(*aUuid);
6647
6648 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6649 if (!pvRemoteBackend)
6650 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6651 }
6652
6653 USHORT portVersion = 1;
6654 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6655 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6656 Assert(portVersion == 1 || portVersion == 2);
6657
6658 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6659 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6660 if (RT_SUCCESS(vrc))
6661 {
6662 /* Create a OUSBDevice and add it to the device list */
6663 ComObjPtr<OUSBDevice> device;
6664 device.createObject();
6665 hrc = device->init(aHostDevice);
6666 AssertComRC(hrc);
6667
6668 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6669 that->mUSBDevices.push_back(device);
6670 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6671
6672 /* notify callbacks */
6673 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6674 }
6675
6676 LogFlowFunc(("vrc=%Rrc\n", vrc));
6677 LogFlowFuncLeave();
6678 return vrc;
6679}
6680
6681/**
6682 * Sends a request to VMM to detach the given host device. After this method
6683 * succeeds, the detached device will disappear from the mUSBDevices
6684 * collection.
6685 *
6686 * @param aIt Iterator pointing to the device to detach.
6687 *
6688 * @note Synchronously calls EMT.
6689 * @note Must be called from under this object's lock.
6690 */
6691HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6692{
6693 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6694
6695 /* still want a lock object because we need to leave it */
6696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 /* protect mpVM */
6699 AutoVMCaller autoVMCaller(this);
6700 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6701
6702 /* if the device is attached, then there must at least one USB hub. */
6703 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6704
6705 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6706 (*aIt)->id().raw()));
6707
6708 /* leave the lock before a VMR3* call (EMT will call us back)! */
6709 alock.leave();
6710
6711/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6712 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6713 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6714 ComAssertRCRet(vrc, E_FAIL);
6715
6716 return S_OK;
6717}
6718
6719/**
6720 * USB device detach callback used by DetachUSBDevice().
6721 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6722 * so we don't use AutoCaller and don't care about reference counters of
6723 * interface pointers passed in.
6724 *
6725 * @thread EMT
6726 * @note Locks the console object for writing.
6727 */
6728//static
6729DECLCALLBACK(int)
6730Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6731{
6732 LogFlowFuncEnter();
6733 LogFlowFunc(("that={%p}\n", that));
6734
6735 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6736 ComObjPtr<OUSBDevice> device = **aIt;
6737
6738 /*
6739 * If that was a remote device, release the backend pointer.
6740 * The pointer was requested in usbAttachCallback.
6741 */
6742 BOOL fRemote = FALSE;
6743
6744 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6745 ComAssertComRC(hrc2);
6746
6747 if (fRemote)
6748 {
6749 Guid guid(*aUuid);
6750 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6751 }
6752
6753 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6754
6755 if (RT_SUCCESS(vrc))
6756 {
6757 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6758
6759 /* Remove the device from the collection */
6760 that->mUSBDevices.erase(*aIt);
6761 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6762
6763 /* notify callbacks */
6764 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6765 }
6766
6767 LogFlowFunc(("vrc=%Rrc\n", vrc));
6768 LogFlowFuncLeave();
6769 return vrc;
6770}
6771
6772#endif /* VBOX_WITH_USB */
6773#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6774
6775/**
6776 * Helper function to handle host interface device creation and attachment.
6777 *
6778 * @param networkAdapter the network adapter which attachment should be reset
6779 * @return COM status code
6780 *
6781 * @note The caller must lock this object for writing.
6782 *
6783 * @todo Move this back into the driver!
6784 */
6785HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6786{
6787 LogFlowThisFunc(("\n"));
6788 /* sanity check */
6789 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6790
6791# ifdef VBOX_STRICT
6792 /* paranoia */
6793 NetworkAttachmentType_T attachment;
6794 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6795 Assert(attachment == NetworkAttachmentType_Bridged);
6796# endif /* VBOX_STRICT */
6797
6798 HRESULT rc = S_OK;
6799
6800 ULONG slot = 0;
6801 rc = networkAdapter->COMGETTER(Slot)(&slot);
6802 AssertComRC(rc);
6803
6804# ifdef RT_OS_LINUX
6805 /*
6806 * Allocate a host interface device
6807 */
6808 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6809 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6810 if (RT_SUCCESS(rcVBox))
6811 {
6812 /*
6813 * Set/obtain the tap interface.
6814 */
6815 struct ifreq IfReq;
6816 memset(&IfReq, 0, sizeof(IfReq));
6817 /* The name of the TAP interface we are using */
6818 Bstr tapDeviceName;
6819 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6820 if (FAILED(rc))
6821 tapDeviceName.setNull(); /* Is this necessary? */
6822 if (tapDeviceName.isEmpty())
6823 {
6824 LogRel(("No TAP device name was supplied.\n"));
6825 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6826 }
6827
6828 if (SUCCEEDED(rc))
6829 {
6830 /* If we are using a static TAP device then try to open it. */
6831 Utf8Str str(tapDeviceName);
6832 if (str.length() <= sizeof(IfReq.ifr_name))
6833 strcpy(IfReq.ifr_name, str.raw());
6834 else
6835 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6836 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6837 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6838 if (rcVBox != 0)
6839 {
6840 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6841 rc = setError(E_FAIL,
6842 tr("Failed to open the host network interface %ls"),
6843 tapDeviceName.raw());
6844 }
6845 }
6846 if (SUCCEEDED(rc))
6847 {
6848 /*
6849 * Make it pollable.
6850 */
6851 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6852 {
6853 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6854 /*
6855 * Here is the right place to communicate the TAP file descriptor and
6856 * the host interface name to the server if/when it becomes really
6857 * necessary.
6858 */
6859 maTAPDeviceName[slot] = tapDeviceName;
6860 rcVBox = VINF_SUCCESS;
6861 }
6862 else
6863 {
6864 int iErr = errno;
6865
6866 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6867 rcVBox = VERR_HOSTIF_BLOCKING;
6868 rc = setError(E_FAIL,
6869 tr("could not set up the host networking device for non blocking access: %s"),
6870 strerror(errno));
6871 }
6872 }
6873 }
6874 else
6875 {
6876 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6877 switch (rcVBox)
6878 {
6879 case VERR_ACCESS_DENIED:
6880 /* will be handled by our caller */
6881 rc = rcVBox;
6882 break;
6883 default:
6884 rc = setError(E_FAIL,
6885 tr("Could not set up the host networking device: %Rrc"),
6886 rcVBox);
6887 break;
6888 }
6889 }
6890
6891# elif defined(RT_OS_FREEBSD)
6892 /*
6893 * Set/obtain the tap interface.
6894 */
6895 /* The name of the TAP interface we are using */
6896 Bstr tapDeviceName;
6897 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6898 if (FAILED(rc))
6899 tapDeviceName.setNull(); /* Is this necessary? */
6900 if (tapDeviceName.isEmpty())
6901 {
6902 LogRel(("No TAP device name was supplied.\n"));
6903 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6904 }
6905 char szTapdev[1024] = "/dev/";
6906 /* If we are using a static TAP device then try to open it. */
6907 Utf8Str str(tapDeviceName);
6908 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6909 strcat(szTapdev, str.raw());
6910 else
6911 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6912 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6913 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6914
6915 if (RT_SUCCESS(rcVBox))
6916 maTAPDeviceName[slot] = tapDeviceName;
6917 else
6918 {
6919 switch (rcVBox)
6920 {
6921 case VERR_ACCESS_DENIED:
6922 /* will be handled by our caller */
6923 rc = rcVBox;
6924 break;
6925 default:
6926 rc = setError(E_FAIL,
6927 tr("Failed to open the host network interface %ls"),
6928 tapDeviceName.raw());
6929 break;
6930 }
6931 }
6932# else
6933# error "huh?"
6934# endif
6935 /* in case of failure, cleanup. */
6936 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6937 {
6938 LogRel(("General failure attaching to host interface\n"));
6939 rc = setError(E_FAIL,
6940 tr("General failure attaching to host interface"));
6941 }
6942 LogFlowThisFunc(("rc=%d\n", rc));
6943 return rc;
6944}
6945
6946
6947/**
6948 * Helper function to handle detachment from a host interface
6949 *
6950 * @param networkAdapter the network adapter which attachment should be reset
6951 * @return COM status code
6952 *
6953 * @note The caller must lock this object for writing.
6954 *
6955 * @todo Move this back into the driver!
6956 */
6957HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6958{
6959 /* sanity check */
6960 LogFlowThisFunc(("\n"));
6961 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6962
6963 HRESULT rc = S_OK;
6964# ifdef VBOX_STRICT
6965 /* paranoia */
6966 NetworkAttachmentType_T attachment;
6967 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6968 Assert(attachment == NetworkAttachmentType_Bridged);
6969# endif /* VBOX_STRICT */
6970
6971 ULONG slot = 0;
6972 rc = networkAdapter->COMGETTER(Slot)(&slot);
6973 AssertComRC(rc);
6974
6975 /* is there an open TAP device? */
6976 if (maTapFD[slot] != NIL_RTFILE)
6977 {
6978 /*
6979 * Close the file handle.
6980 */
6981 Bstr tapDeviceName, tapTerminateApplication;
6982 bool isStatic = true;
6983 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6984 if (FAILED(rc) || tapDeviceName.isEmpty())
6985 {
6986 /* If the name is empty, this is a dynamic TAP device, so close it now,
6987 so that the termination script can remove the interface. Otherwise we still
6988 need the FD to pass to the termination script. */
6989 isStatic = false;
6990 int rcVBox = RTFileClose(maTapFD[slot]);
6991 AssertRC(rcVBox);
6992 maTapFD[slot] = NIL_RTFILE;
6993 }
6994 if (isStatic)
6995 {
6996 /* If we are using a static TAP device, we close it now, after having called the
6997 termination script. */
6998 int rcVBox = RTFileClose(maTapFD[slot]);
6999 AssertRC(rcVBox);
7000 }
7001 /* the TAP device name and handle are no longer valid */
7002 maTapFD[slot] = NIL_RTFILE;
7003 maTAPDeviceName[slot] = "";
7004 }
7005 LogFlowThisFunc(("returning %d\n", rc));
7006 return rc;
7007}
7008
7009#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
7010
7011/**
7012 * Called at power down to terminate host interface networking.
7013 *
7014 * @note The caller must lock this object for writing.
7015 */
7016HRESULT Console::powerDownHostInterfaces()
7017{
7018 LogFlowThisFunc(("\n"));
7019
7020 /* sanity check */
7021 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7022
7023 /*
7024 * host interface termination handling
7025 */
7026 HRESULT rc;
7027 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
7028 {
7029 ComPtr<INetworkAdapter> networkAdapter;
7030 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
7031 if (FAILED(rc)) break;
7032
7033 BOOL enabled = FALSE;
7034 networkAdapter->COMGETTER(Enabled)(&enabled);
7035 if (!enabled)
7036 continue;
7037
7038 NetworkAttachmentType_T attachment;
7039 networkAdapter->COMGETTER(AttachmentType)(&attachment);
7040 if (attachment == NetworkAttachmentType_Bridged)
7041 {
7042#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
7043 HRESULT rc2 = detachFromTapInterface(networkAdapter);
7044 if (FAILED(rc2) && SUCCEEDED(rc))
7045 rc = rc2;
7046#endif
7047 }
7048 }
7049
7050 return rc;
7051}
7052
7053
7054/**
7055 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
7056 * and VMR3Teleport.
7057 *
7058 * @param pVM The VM handle.
7059 * @param uPercent Completetion precentage (0-100).
7060 * @param pvUser Pointer to the VMProgressTask structure.
7061 * @return VINF_SUCCESS.
7062 */
7063/*static*/
7064DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
7065{
7066 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
7067 AssertReturn(task, VERR_INVALID_PARAMETER);
7068
7069 /* update the progress object */
7070 if (task->mProgress)
7071 task->mProgress->SetCurrentOperationProgress(uPercent);
7072
7073 return VINF_SUCCESS;
7074}
7075
7076/**
7077 * @copydoc FNVMATERROR
7078 *
7079 * @remarks Might be some tiny serialization concerns with access to the string
7080 * object here...
7081 */
7082/*static*/ DECLCALLBACK(void)
7083Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
7084 const char *pszErrorFmt, va_list va)
7085{
7086 Utf8Str *pErrorText = (Utf8Str *)pvUser;
7087 AssertPtr(pErrorText);
7088
7089 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
7090 va_list va2;
7091 va_copy(va2, va);
7092
7093 /* Append to any the existing error message. */
7094 if (pErrorText->length())
7095 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
7096 pszErrorFmt, &va2, rc, rc);
7097 else
7098 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
7099
7100 va_end(va2);
7101}
7102
7103/**
7104 * VM runtime error callback function.
7105 * See VMSetRuntimeError for the detailed description of parameters.
7106 *
7107 * @param pVM The VM handle.
7108 * @param pvUser The user argument.
7109 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
7110 * @param pszErrorId Error ID string.
7111 * @param pszFormat Error message format string.
7112 * @param va Error message arguments.
7113 * @thread EMT.
7114 */
7115/* static */ DECLCALLBACK(void)
7116Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
7117 const char *pszErrorId,
7118 const char *pszFormat, va_list va)
7119{
7120 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
7121 LogFlowFuncEnter();
7122
7123 Console *that = static_cast<Console *>(pvUser);
7124 AssertReturnVoid(that);
7125
7126 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
7127
7128 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
7129 fFatal, pszErrorId, message.raw()));
7130
7131 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
7132
7133 LogFlowFuncLeave();
7134}
7135
7136/**
7137 * Captures USB devices that match filters of the VM.
7138 * Called at VM startup.
7139 *
7140 * @param pVM The VM handle.
7141 *
7142 * @note The caller must lock this object for writing.
7143 */
7144HRESULT Console::captureUSBDevices(PVM pVM)
7145{
7146 LogFlowThisFunc(("\n"));
7147
7148 /* sanity check */
7149 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
7150
7151 /* If the machine has an USB controller, ask the USB proxy service to
7152 * capture devices */
7153 PPDMIBASE pBase;
7154 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7155 if (RT_SUCCESS(vrc))
7156 {
7157 /* leave the lock before calling Host in VBoxSVC since Host may call
7158 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7159 * produce an inter-process dead-lock otherwise. */
7160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7161 alock.leave();
7162
7163 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7164 ComAssertComRCRetRC(hrc);
7165 }
7166 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7167 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7168 vrc = VINF_SUCCESS;
7169 else
7170 AssertRC(vrc);
7171
7172 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7173}
7174
7175
7176/**
7177 * Detach all USB device which are attached to the VM for the
7178 * purpose of clean up and such like.
7179 *
7180 * @note The caller must lock this object for writing.
7181 */
7182void Console::detachAllUSBDevices(bool aDone)
7183{
7184 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7185
7186 /* sanity check */
7187 AssertReturnVoid(isWriteLockOnCurrentThread());
7188
7189 mUSBDevices.clear();
7190
7191 /* leave the lock before calling Host in VBoxSVC since Host may call
7192 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7193 * produce an inter-process dead-lock otherwise. */
7194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7195 alock.leave();
7196
7197 mControl->DetachAllUSBDevices(aDone);
7198}
7199
7200/**
7201 * @note Locks this object for writing.
7202 */
7203void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7204{
7205 LogFlowThisFuncEnter();
7206 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7207
7208 AutoCaller autoCaller(this);
7209 if (!autoCaller.isOk())
7210 {
7211 /* Console has been already uninitialized, deny request */
7212 AssertMsgFailed(("Console is already uninitialized\n"));
7213 LogFlowThisFunc(("Console is already uninitialized\n"));
7214 LogFlowThisFuncLeave();
7215 return;
7216 }
7217
7218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7219
7220 /*
7221 * Mark all existing remote USB devices as dirty.
7222 */
7223 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7224 it != mRemoteUSBDevices.end();
7225 ++it)
7226 {
7227 (*it)->dirty(true);
7228 }
7229
7230 /*
7231 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7232 */
7233 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7234 VRDPUSBDEVICEDESC *e = pDevList;
7235
7236 /* The cbDevList condition must be checked first, because the function can
7237 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7238 */
7239 while (cbDevList >= 2 && e->oNext)
7240 {
7241 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7242 e->idVendor, e->idProduct,
7243 e->oProduct? (char *)e + e->oProduct: ""));
7244
7245 bool fNewDevice = true;
7246
7247 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7248 it != mRemoteUSBDevices.end();
7249 ++it)
7250 {
7251 if ((*it)->devId() == e->id
7252 && (*it)->clientId() == u32ClientId)
7253 {
7254 /* The device is already in the list. */
7255 (*it)->dirty(false);
7256 fNewDevice = false;
7257 break;
7258 }
7259 }
7260
7261 if (fNewDevice)
7262 {
7263 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7264 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7265
7266 /* Create the device object and add the new device to list. */
7267 ComObjPtr<RemoteUSBDevice> device;
7268 device.createObject();
7269 device->init(u32ClientId, e);
7270
7271 mRemoteUSBDevices.push_back(device);
7272
7273 /* Check if the device is ok for current USB filters. */
7274 BOOL fMatched = FALSE;
7275 ULONG fMaskedIfs = 0;
7276
7277 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7278
7279 AssertComRC(hrc);
7280
7281 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7282
7283 if (fMatched)
7284 {
7285 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7286
7287 /// @todo (r=dmik) warning reporting subsystem
7288
7289 if (hrc == S_OK)
7290 {
7291 LogFlowThisFunc(("Device attached\n"));
7292 device->captured(true);
7293 }
7294 }
7295 }
7296
7297 if (cbDevList < e->oNext)
7298 {
7299 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7300 cbDevList, e->oNext));
7301 break;
7302 }
7303
7304 cbDevList -= e->oNext;
7305
7306 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7307 }
7308
7309 /*
7310 * Remove dirty devices, that is those which are not reported by the server anymore.
7311 */
7312 for (;;)
7313 {
7314 ComObjPtr<RemoteUSBDevice> device;
7315
7316 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7317 while (it != mRemoteUSBDevices.end())
7318 {
7319 if ((*it)->dirty())
7320 {
7321 device = *it;
7322 break;
7323 }
7324
7325 ++ it;
7326 }
7327
7328 if (!device)
7329 {
7330 break;
7331 }
7332
7333 USHORT vendorId = 0;
7334 device->COMGETTER(VendorId)(&vendorId);
7335
7336 USHORT productId = 0;
7337 device->COMGETTER(ProductId)(&productId);
7338
7339 Bstr product;
7340 device->COMGETTER(Product)(product.asOutParam());
7341
7342 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7343 vendorId, productId, product.raw()));
7344
7345 /* Detach the device from VM. */
7346 if (device->captured())
7347 {
7348 Bstr uuid;
7349 device->COMGETTER(Id)(uuid.asOutParam());
7350 onUSBDeviceDetach(uuid, NULL);
7351 }
7352
7353 /* And remove it from the list. */
7354 mRemoteUSBDevices.erase(it);
7355 }
7356
7357 LogFlowThisFuncLeave();
7358}
7359
7360/**
7361 * Thread function which starts the VM (also from saved state) and
7362 * track progress.
7363 *
7364 * @param Thread The thread id.
7365 * @param pvUser Pointer to a VMPowerUpTask structure.
7366 * @return VINF_SUCCESS (ignored).
7367 *
7368 * @note Locks the Console object for writing.
7369 */
7370/*static*/
7371DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7372{
7373 LogFlowFuncEnter();
7374
7375 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7376 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7377
7378 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7379 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7380
7381#if defined(RT_OS_WINDOWS)
7382 {
7383 /* initialize COM */
7384 HRESULT hrc = CoInitializeEx(NULL,
7385 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7386 COINIT_SPEED_OVER_MEMORY);
7387 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7388 }
7389#endif
7390
7391 HRESULT rc = S_OK;
7392 int vrc = VINF_SUCCESS;
7393
7394 /* Set up a build identifier so that it can be seen from core dumps what
7395 * exact build was used to produce the core. */
7396 static char saBuildID[40];
7397 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7398 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7399
7400 ComObjPtr<Console> console = task->mConsole;
7401
7402 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7403
7404 /* The lock is also used as a signal from the task initiator (which
7405 * releases it only after RTThreadCreate()) that we can start the job */
7406 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7407
7408 /* sanity */
7409 Assert(console->mpVM == NULL);
7410
7411 try
7412 {
7413 /* wait for auto reset ops to complete so that we can successfully lock
7414 * the attached hard disks by calling LockMedia() below */
7415 for (VMPowerUpTask::ProgressList::const_iterator
7416 it = task->hardDiskProgresses.begin();
7417 it != task->hardDiskProgresses.end(); ++ it)
7418 {
7419 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7420 AssertComRC(rc2);
7421 }
7422
7423 /*
7424 * Lock attached media. This method will also check their accessibility.
7425 * If we're a teleporter, we'll have to postpone this action so we can
7426 * migrate between local processes.
7427 *
7428 * Note! The media will be unlocked automatically by
7429 * SessionMachine::setMachineState() when the VM is powered down.
7430 */
7431 if (!task->mTeleporterEnabled)
7432 {
7433 rc = console->mControl->LockMedia();
7434 if (FAILED(rc)) throw rc;
7435 }
7436
7437#ifdef VBOX_WITH_VRDP
7438
7439 /* Create the VRDP server. In case of headless operation, this will
7440 * also create the framebuffer, required at VM creation.
7441 */
7442 ConsoleVRDPServer *server = console->consoleVRDPServer();
7443 Assert(server);
7444
7445 /* Does VRDP server call Console from the other thread?
7446 * Not sure (and can change), so leave the lock just in case.
7447 */
7448 alock.leave();
7449 vrc = server->Launch();
7450 alock.enter();
7451
7452 if (vrc == VERR_NET_ADDRESS_IN_USE)
7453 {
7454 Utf8Str errMsg;
7455 Bstr bstr;
7456 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7457 Utf8Str ports = bstr;
7458 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7459 ports.raw());
7460 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7461 vrc, errMsg.raw()));
7462 }
7463 else if (RT_FAILURE(vrc))
7464 {
7465 Utf8Str errMsg;
7466 switch (vrc)
7467 {
7468 case VERR_FILE_NOT_FOUND:
7469 {
7470 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7471 break;
7472 }
7473 default:
7474 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7475 vrc);
7476 }
7477 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7478 vrc, errMsg.raw()));
7479 throw setError(E_FAIL, errMsg.c_str());
7480 }
7481
7482#endif /* VBOX_WITH_VRDP */
7483
7484 ComPtr<IMachine> pMachine = console->machine();
7485 ULONG cCpus = 1;
7486 pMachine->COMGETTER(CPUCount)(&cCpus);
7487
7488 /*
7489 * Create the VM
7490 */
7491 PVM pVM;
7492 /*
7493 * leave the lock since EMT will call Console. It's safe because
7494 * mMachineState is either Starting or Restoring state here.
7495 */
7496 alock.leave();
7497
7498 vrc = VMR3Create(cCpus, Console::genericVMSetErrorCallback, &task->mErrorMsg,
7499 task->mConfigConstructor, static_cast<Console *>(console),
7500 &pVM);
7501
7502 alock.enter();
7503
7504#ifdef VBOX_WITH_VRDP
7505 /* Enable client connections to the server. */
7506 console->consoleVRDPServer()->EnableConnections();
7507#endif /* VBOX_WITH_VRDP */
7508
7509 if (RT_SUCCESS(vrc))
7510 {
7511 do
7512 {
7513 /*
7514 * Register our load/save state file handlers
7515 */
7516 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7517 NULL, NULL, NULL,
7518 NULL, saveStateFileExec, NULL,
7519 NULL, loadStateFileExec, NULL,
7520 static_cast<Console *>(console));
7521 AssertRCBreak(vrc);
7522
7523 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7524 AssertRC(vrc);
7525 if (RT_FAILURE(vrc))
7526 break;
7527
7528 /*
7529 * Synchronize debugger settings
7530 */
7531 MachineDebugger *machineDebugger = console->getMachineDebugger();
7532 if (machineDebugger)
7533 {
7534 machineDebugger->flushQueuedSettings();
7535 }
7536
7537 /*
7538 * Shared Folders
7539 */
7540 if (console->getVMMDev()->isShFlActive())
7541 {
7542 /* Does the code below call Console from the other thread?
7543 * Not sure, so leave the lock just in case. */
7544 alock.leave();
7545
7546 for (SharedFolderDataMap::const_iterator
7547 it = task->mSharedFolders.begin();
7548 it != task->mSharedFolders.end();
7549 ++ it)
7550 {
7551 rc = console->createSharedFolder((*it).first, (*it).second);
7552 if (FAILED(rc)) break;
7553 }
7554 if (FAILED(rc)) break;
7555
7556 /* enter the lock again */
7557 alock.enter();
7558 }
7559
7560 /*
7561 * Capture USB devices.
7562 */
7563 rc = console->captureUSBDevices(pVM);
7564 if (FAILED(rc)) break;
7565
7566 /* leave the lock before a lengthy operation */
7567 alock.leave();
7568
7569 /* Load saved state? */
7570 if (task->mSavedStateFile.length())
7571 {
7572 LogFlowFunc(("Restoring saved state from '%s'...\n",
7573 task->mSavedStateFile.raw()));
7574
7575 vrc = VMR3LoadFromFile(pVM,
7576 task->mSavedStateFile.c_str(),
7577 Console::stateProgressCallback,
7578 static_cast<VMProgressTask*>(task.get()));
7579
7580 if (RT_SUCCESS(vrc))
7581 {
7582 if (task->mStartPaused)
7583 /* done */
7584 console->setMachineState(MachineState_Paused);
7585 else
7586 {
7587 /* Start/Resume the VM execution */
7588 vrc = VMR3Resume(pVM);
7589 AssertRC(vrc);
7590 }
7591 }
7592
7593 /* Power off in case we failed loading or resuming the VM */
7594 if (RT_FAILURE(vrc))
7595 {
7596 int vrc2 = VMR3PowerOff(pVM);
7597 AssertRC(vrc2);
7598 }
7599 }
7600 else if (task->mTeleporterEnabled)
7601 {
7602 /* -> ConsoleImplTeleporter.cpp */
7603 bool fPowerOffOnFailure;
7604 rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7605 task->mProgress, &fPowerOffOnFailure);
7606 if (FAILED(rc) && fPowerOffOnFailure)
7607 {
7608 ErrorInfoKeeper eik;
7609 int vrc2 = VMR3PowerOff(pVM);
7610 AssertRC(vrc2);
7611 }
7612 }
7613 else if (task->mStartPaused)
7614 /* done */
7615 console->setMachineState(MachineState_Paused);
7616 else
7617 {
7618 /* Power on the VM (i.e. start executing) */
7619 vrc = VMR3PowerOn(pVM);
7620 AssertRC(vrc);
7621 }
7622
7623 /* enter the lock again */
7624 alock.enter();
7625 }
7626 while (0);
7627
7628 /* On failure, destroy the VM */
7629 if (FAILED(rc) || RT_FAILURE(vrc))
7630 {
7631 /* preserve existing error info */
7632 ErrorInfoKeeper eik;
7633
7634 /* powerDown() will call VMR3Destroy() and do all necessary
7635 * cleanup (VRDP, USB devices) */
7636 HRESULT rc2 = console->powerDown();
7637 AssertComRC(rc2);
7638 }
7639 else
7640 {
7641 /*
7642 * Deregister the VMSetError callback. This is necessary as the
7643 * pfnVMAtError() function passed to VMR3Create() is supposed to
7644 * be sticky but our error callback isn't.
7645 */
7646 alock.leave();
7647 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7648 /** @todo register another VMSetError callback? */
7649 alock.enter();
7650 }
7651 }
7652 else
7653 {
7654 /*
7655 * If VMR3Create() failed it has released the VM memory.
7656 */
7657 console->mpVM = NULL;
7658 }
7659
7660 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7661 {
7662 /* If VMR3Create() or one of the other calls in this function fail,
7663 * an appropriate error message has been set in task->mErrorMsg.
7664 * However since that happens via a callback, the rc status code in
7665 * this function is not updated.
7666 */
7667 if (!task->mErrorMsg.length())
7668 {
7669 /* If the error message is not set but we've got a failure,
7670 * convert the VBox status code into a meaningful error message.
7671 * This becomes unused once all the sources of errors set the
7672 * appropriate error message themselves.
7673 */
7674 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7675 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7676 vrc);
7677 }
7678
7679 /* Set the error message as the COM error.
7680 * Progress::notifyComplete() will pick it up later. */
7681 throw setError(E_FAIL, task->mErrorMsg.c_str());
7682 }
7683 }
7684 catch (HRESULT aRC) { rc = aRC; }
7685
7686 if ( console->mMachineState == MachineState_Starting
7687 || console->mMachineState == MachineState_Restoring
7688 || console->mMachineState == MachineState_TeleportingIn
7689 )
7690 {
7691 /* We are still in the Starting/Restoring state. This means one of:
7692 *
7693 * 1) we failed before VMR3Create() was called;
7694 * 2) VMR3Create() failed.
7695 *
7696 * In both cases, there is no need to call powerDown(), but we still
7697 * need to go back to the PoweredOff/Saved state. Reuse
7698 * vmstateChangeCallback() for that purpose.
7699 */
7700
7701 /* preserve existing error info */
7702 ErrorInfoKeeper eik;
7703
7704 Assert(console->mpVM == NULL);
7705 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7706 console);
7707 }
7708
7709 /*
7710 * Evaluate the final result. Note that the appropriate mMachineState value
7711 * is already set by vmstateChangeCallback() in all cases.
7712 */
7713
7714 /* leave the lock, don't need it any more */
7715 alock.leave();
7716
7717 if (SUCCEEDED(rc))
7718 {
7719 /* Notify the progress object of the success */
7720 task->mProgress->notifyComplete(S_OK);
7721 }
7722 else
7723 {
7724 /* The progress object will fetch the current error info */
7725 task->mProgress->notifyComplete(rc);
7726 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7727 }
7728
7729 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7730 console->mControl->EndPowerUp(rc);
7731
7732#if defined(RT_OS_WINDOWS)
7733 /* uninitialize COM */
7734 CoUninitialize();
7735#endif
7736
7737 LogFlowFuncLeave();
7738
7739 return VINF_SUCCESS;
7740}
7741
7742
7743/**
7744 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7745 *
7746 * @param pConsole Reference to the console object.
7747 * @param pVM The VM handle.
7748 * @param lInstance The instance of the controller.
7749 * @param pcszDevice The name of the controller type.
7750 * @param enmBus The storage bus type of the controller.
7751 * @param fSetupMerge Whether to set up a medium merge
7752 * @param uMergeSource Merge source image index
7753 * @param uMergeTarget Merge target image index
7754 * @param aMediumAtt The medium attachment.
7755 * @param aMachineState The current machine state.
7756 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7757 * @return VBox status code.
7758 */
7759/* static */
7760DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7761 PVM pVM,
7762 const char *pcszDevice,
7763 unsigned uInstance,
7764 StorageBus_T enmBus,
7765 bool fUseHostIOCache,
7766 bool fSetupMerge,
7767 unsigned uMergeSource,
7768 unsigned uMergeTarget,
7769 IMediumAttachment *aMediumAtt,
7770 MachineState_T aMachineState,
7771 HRESULT *phrc)
7772{
7773 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7774
7775 int rc;
7776 HRESULT hrc;
7777 Bstr bstr;
7778 *phrc = S_OK;
7779#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7780#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7781
7782 /* Ignore attachments other than hard disks, since at the moment they are
7783 * not subject to snapshotting in general. */
7784 DeviceType_T lType;
7785 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7786 if (lType != DeviceType_HardDisk)
7787 return VINF_SUCCESS;
7788
7789 /* Determine the base path for the device instance. */
7790 PCFGMNODE pCtlInst;
7791 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7792 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7793
7794 /* Update the device instance configuration. */
7795 rc = pConsole->configMediumAttachment(pCtlInst,
7796 pcszDevice,
7797 uInstance,
7798 enmBus,
7799 fUseHostIOCache,
7800 fSetupMerge,
7801 uMergeSource,
7802 uMergeTarget,
7803 aMediumAtt,
7804 aMachineState,
7805 phrc,
7806 true /* fAttachDetach */,
7807 false /* fForceUnmount */,
7808 pVM,
7809 NULL /* paLedDevType */);
7810 /** @todo this dumps everything attached to this device instance, which
7811 * is more than necessary. Dumping the changed LUN would be enough. */
7812 CFGMR3Dump(pCtlInst);
7813 RC_CHECK();
7814
7815#undef RC_CHECK
7816#undef H
7817
7818 LogFlowFunc(("Returns success\n"));
7819 return VINF_SUCCESS;
7820}
7821
7822/**
7823 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7824 */
7825static void takesnapshotProgressCancelCallback(void *pvUser)
7826{
7827 PVM pVM = (PVM)pvUser;
7828 SSMR3Cancel(pVM);
7829}
7830
7831/**
7832 * Worker thread created by Console::TakeSnapshot.
7833 * @param Thread The current thread (ignored).
7834 * @param pvUser The task.
7835 * @return VINF_SUCCESS (ignored).
7836 */
7837/*static*/
7838DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7839{
7840 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7841
7842 // taking a snapshot consists of the following:
7843
7844 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7845 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7846 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7847 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7848 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7849
7850 Console *that = pTask->mConsole;
7851 bool fBeganTakingSnapshot = false;
7852 bool fSuspenededBySave = false;
7853
7854 AutoCaller autoCaller(that);
7855 if (FAILED(autoCaller.rc()))
7856 {
7857 that->mptrCancelableProgress.setNull();
7858 return autoCaller.rc();
7859 }
7860
7861 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7862
7863 HRESULT rc = S_OK;
7864
7865 try
7866 {
7867 /* STEP 1 + 2:
7868 * request creating the diff images on the server and create the snapshot object
7869 * (this will set the machine state to Saving on the server to block
7870 * others from accessing this machine)
7871 */
7872 rc = that->mControl->BeginTakingSnapshot(that,
7873 pTask->bstrName,
7874 pTask->bstrDescription,
7875 pTask->mProgress,
7876 pTask->fTakingSnapshotOnline,
7877 pTask->bstrSavedStateFile.asOutParam());
7878 if (FAILED(rc))
7879 throw rc;
7880
7881 fBeganTakingSnapshot = true;
7882
7883 /*
7884 * state file is non-null only when the VM is paused
7885 * (i.e. creating a snapshot online)
7886 */
7887 ComAssertThrow( (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7888 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline),
7889 rc = E_FAIL);
7890
7891 /* sync the state with the server */
7892 if (pTask->lastMachineState == MachineState_Running)
7893 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7894 else
7895 that->setMachineStateLocally(MachineState_Saving);
7896
7897 // STEP 3: save the VM state (if online)
7898 if (pTask->fTakingSnapshotOnline)
7899 {
7900 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7901
7902 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7903 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7904 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7905
7906 alock.leave();
7907 LogFlowFunc(("VMR3Save...\n"));
7908 int vrc = VMR3Save(that->mpVM,
7909 strSavedStateFile.c_str(),
7910 true /*fContinueAfterwards*/,
7911 Console::stateProgressCallback,
7912 (void*)pTask,
7913 &fSuspenededBySave);
7914 alock.enter();
7915 if (RT_FAILURE(vrc))
7916 throw setError(E_FAIL,
7917 tr("Failed to save the machine state to '%s' (%Rrc)"),
7918 strSavedStateFile.c_str(), vrc);
7919
7920 pTask->mProgress->setCancelCallback(NULL, NULL);
7921 if (!pTask->mProgress->notifyPointOfNoReturn())
7922 throw setError(E_FAIL, tr("Cancelled"));
7923 that->mptrCancelableProgress.setNull();
7924
7925 // STEP 4: reattach hard disks
7926 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7927
7928 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7929 1); // operation weight, same as computed when setting up progress object
7930
7931 com::SafeIfaceArray<IMediumAttachment> atts;
7932 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7933 if (FAILED(rc))
7934 throw rc;
7935
7936 for (size_t i = 0;
7937 i < atts.size();
7938 ++i)
7939 {
7940 ComPtr<IStorageController> controller;
7941 Bstr controllerName;
7942 ULONG lInstance;
7943 StorageControllerType_T enmController;
7944 StorageBus_T enmBus;
7945 BOOL fUseHostIOCache;
7946
7947 /*
7948 * We can't pass a storage controller object directly
7949 * (g++ complains about not being able to pass non POD types through '...')
7950 * so we have to query needed values here and pass them.
7951 */
7952 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
7953 if (FAILED(rc))
7954 throw rc;
7955
7956 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7957 if (FAILED(rc))
7958 throw rc;
7959
7960 rc = controller->COMGETTER(ControllerType)(&enmController);
7961 if (FAILED(rc))
7962 throw rc;
7963 rc = controller->COMGETTER(Instance)(&lInstance);
7964 if (FAILED(rc))
7965 throw rc;
7966 rc = controller->COMGETTER(Bus)(&enmBus);
7967 if (FAILED(rc))
7968 throw rc;
7969 rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
7970 if (FAILED(rc))
7971 throw rc;
7972
7973 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7974
7975 /*
7976 * don't leave the lock since reconfigureMediumAttachment
7977 * isn't going to need the Console lock.
7978 */
7979 vrc = VMR3ReqCallWait(that->mpVM,
7980 VMCPUID_ANY,
7981 (PFNRT)reconfigureMediumAttachment,
7982 12,
7983 that,
7984 that->mpVM,
7985 pcszDevice,
7986 lInstance,
7987 enmBus,
7988 fUseHostIOCache,
7989 false /* fSetupMerge */,
7990 0 /* uMergeSource */,
7991 0 /* uMergeTarget */,
7992 atts[i],
7993 that->mMachineState,
7994 &rc);
7995 if (RT_FAILURE(vrc))
7996 throw setError(E_FAIL, Console::tr("%Rrc"), vrc);
7997 if (FAILED(rc))
7998 throw rc;
7999 }
8000 }
8001
8002 /*
8003 * finalize the requested snapshot object.
8004 * This will reset the machine state to the state it had right
8005 * before calling mControl->BeginTakingSnapshot().
8006 */
8007 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
8008 // do not throw rc here because we can't call EndTakingSnapshot() twice
8009 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8010 }
8011 catch (HRESULT rcThrown)
8012 {
8013 /* preserve existing error info */
8014 ErrorInfoKeeper eik;
8015
8016 if (fBeganTakingSnapshot)
8017 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
8018
8019 rc = rcThrown;
8020 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8021 }
8022 Assert(alock.isWriteLockOnCurrentThread());
8023
8024 if (FAILED(rc)) /* Must come before calling setMachineState. */
8025 pTask->mProgress->notifyComplete(rc);
8026
8027 /*
8028 * Fix up the machine state.
8029 *
8030 * For live snapshots we do all the work, for the two other variantions we
8031 * just update the local copy.
8032 */
8033 MachineState_T enmMachineState;
8034 that->mMachine->COMGETTER(State)(&enmMachineState);
8035 if ( that->mMachineState == MachineState_LiveSnapshotting
8036 || that->mMachineState == MachineState_Saving)
8037 {
8038
8039 if (!pTask->fTakingSnapshotOnline)
8040 that->setMachineStateLocally(pTask->lastMachineState);
8041 else if (SUCCEEDED(rc))
8042 {
8043 Assert( pTask->lastMachineState == MachineState_Running
8044 || pTask->lastMachineState == MachineState_Paused);
8045 Assert(that->mMachineState == MachineState_Saving);
8046 if (pTask->lastMachineState == MachineState_Running)
8047 {
8048 LogFlowFunc(("VMR3Resume...\n"));
8049 alock.leave();
8050 int vrc = VMR3Resume(that->mpVM);
8051 alock.enter();
8052 if (RT_FAILURE(vrc))
8053 {
8054 rc = setError(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
8055 pTask->mProgress->notifyComplete(rc);
8056 if (that->mMachineState == MachineState_Saving)
8057 that->setMachineStateLocally(MachineState_Paused);
8058 }
8059 }
8060 else
8061 that->setMachineStateLocally(MachineState_Paused);
8062 }
8063 else
8064 {
8065 /** @todo this could probably be made more generic and reused elsewhere. */
8066 /* paranoid cleanup on for a failed online snapshot. */
8067 VMSTATE enmVMState = VMR3GetState(that->mpVM);
8068 switch (enmVMState)
8069 {
8070 case VMSTATE_RUNNING:
8071 case VMSTATE_RUNNING_LS:
8072 case VMSTATE_DEBUGGING:
8073 case VMSTATE_DEBUGGING_LS:
8074 case VMSTATE_POWERING_OFF:
8075 case VMSTATE_POWERING_OFF_LS:
8076 case VMSTATE_RESETTING:
8077 case VMSTATE_RESETTING_LS:
8078 Assert(!fSuspenededBySave);
8079 that->setMachineState(MachineState_Running);
8080 break;
8081
8082 case VMSTATE_GURU_MEDITATION:
8083 case VMSTATE_GURU_MEDITATION_LS:
8084 that->setMachineState(MachineState_Stuck);
8085 break;
8086
8087 case VMSTATE_FATAL_ERROR:
8088 case VMSTATE_FATAL_ERROR_LS:
8089 if (pTask->lastMachineState == MachineState_Paused)
8090 that->setMachineStateLocally(pTask->lastMachineState);
8091 else
8092 that->setMachineState(MachineState_Paused);
8093 break;
8094
8095 default:
8096 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8097 case VMSTATE_SUSPENDED:
8098 case VMSTATE_SUSPENDED_LS:
8099 case VMSTATE_SUSPENDING:
8100 case VMSTATE_SUSPENDING_LS:
8101 case VMSTATE_SUSPENDING_EXT_LS:
8102 if (fSuspenededBySave)
8103 {
8104 Assert(pTask->lastMachineState == MachineState_Running);
8105 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8106 alock.leave();
8107 int vrc = VMR3Resume(that->mpVM);
8108 alock.enter();
8109 AssertLogRelRC(vrc);
8110 if (RT_FAILURE(vrc))
8111 that->setMachineState(MachineState_Paused);
8112 }
8113 else if (pTask->lastMachineState == MachineState_Paused)
8114 that->setMachineStateLocally(pTask->lastMachineState);
8115 else
8116 that->setMachineState(MachineState_Paused);
8117 break;
8118 }
8119
8120 }
8121 }
8122 /*else: somebody else has change the state... Leave it. */
8123
8124 /* check the remote state to see that we got it right. */
8125 that->mMachine->COMGETTER(State)(&enmMachineState);
8126 AssertLogRelMsg(that->mMachineState == enmMachineState,
8127 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8128 Global::stringifyMachineState(enmMachineState) ));
8129
8130
8131 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8132 pTask->mProgress->notifyComplete(rc);
8133
8134 delete pTask;
8135
8136 LogFlowFuncLeave();
8137 return VINF_SUCCESS;
8138}
8139
8140/**
8141 * Thread for executing the saved state operation.
8142 *
8143 * @param Thread The thread handle.
8144 * @param pvUser Pointer to a VMSaveTask structure.
8145 * @return VINF_SUCCESS (ignored).
8146 *
8147 * @note Locks the Console object for writing.
8148 */
8149/*static*/
8150DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8151{
8152 LogFlowFuncEnter();
8153
8154 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8155 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8156
8157 Assert(task->mSavedStateFile.length());
8158 Assert(!task->mProgress.isNull());
8159
8160 const ComObjPtr<Console> &that = task->mConsole;
8161 Utf8Str errMsg;
8162 HRESULT rc = S_OK;
8163
8164 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
8165
8166 bool fSuspenededBySave;
8167 int vrc = VMR3Save(that->mpVM,
8168 task->mSavedStateFile.c_str(),
8169 false, /*fContinueAfterwards*/
8170 Console::stateProgressCallback,
8171 static_cast<VMProgressTask*>(task.get()),
8172 &fSuspenededBySave);
8173 if (RT_FAILURE(vrc))
8174 {
8175 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8176 task->mSavedStateFile.raw(), vrc);
8177 rc = E_FAIL;
8178 }
8179 Assert(!fSuspenededBySave);
8180
8181 /* lock the console once we're going to access it */
8182 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8183
8184 /*
8185 * finalize the requested save state procedure.
8186 * In case of success, the server will set the machine state to Saved;
8187 * in case of failure it will reset the it to the state it had right
8188 * before calling mControl->BeginSavingState().
8189 */
8190 that->mControl->EndSavingState(SUCCEEDED(rc));
8191
8192 /* synchronize the state with the server */
8193 if (!FAILED(rc))
8194 {
8195 /*
8196 * The machine has been successfully saved, so power it down
8197 * (vmstateChangeCallback() will set state to Saved on success).
8198 * Note: we release the task's VM caller, otherwise it will
8199 * deadlock.
8200 */
8201 task->releaseVMCaller();
8202
8203 rc = that->powerDown();
8204 }
8205
8206 /* notify the progress object about operation completion */
8207 if (SUCCEEDED(rc))
8208 task->mProgress->notifyComplete(S_OK);
8209 else
8210 {
8211 if (errMsg.length())
8212 task->mProgress->notifyComplete(rc,
8213 COM_IIDOF(IConsole),
8214 (CBSTR)Console::getComponentName(),
8215 errMsg.c_str());
8216 else
8217 task->mProgress->notifyComplete(rc);
8218 }
8219
8220 LogFlowFuncLeave();
8221 return VINF_SUCCESS;
8222}
8223
8224/**
8225 * Thread for powering down the Console.
8226 *
8227 * @param Thread The thread handle.
8228 * @param pvUser Pointer to the VMTask structure.
8229 * @return VINF_SUCCESS (ignored).
8230 *
8231 * @note Locks the Console object for writing.
8232 */
8233/*static*/
8234DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8235{
8236 LogFlowFuncEnter();
8237
8238 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8239 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8240
8241 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8242
8243 const ComObjPtr<Console> &that = task->mConsole;
8244
8245 /* Note: no need to use addCaller() to protect Console because VMTask does
8246 * that */
8247
8248 /* wait until the method tat started us returns */
8249 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8250
8251 /* release VM caller to avoid the powerDown() deadlock */
8252 task->releaseVMCaller();
8253
8254 that->powerDown(task->mProgress);
8255
8256 LogFlowFuncLeave();
8257 return VINF_SUCCESS;
8258}
8259
8260/**
8261 * The Main status driver instance data.
8262 */
8263typedef struct DRVMAINSTATUS
8264{
8265 /** The LED connectors. */
8266 PDMILEDCONNECTORS ILedConnectors;
8267 /** Pointer to the LED ports interface above us. */
8268 PPDMILEDPORTS pLedPorts;
8269 /** Pointer to the array of LED pointers. */
8270 PPDMLED *papLeds;
8271 /** The unit number corresponding to the first entry in the LED array. */
8272 RTUINT iFirstLUN;
8273 /** The unit number corresponding to the last entry in the LED array.
8274 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8275 RTUINT iLastLUN;
8276} DRVMAINSTATUS, *PDRVMAINSTATUS;
8277
8278
8279/**
8280 * Notification about a unit which have been changed.
8281 *
8282 * The driver must discard any pointers to data owned by
8283 * the unit and requery it.
8284 *
8285 * @param pInterface Pointer to the interface structure containing the called function pointer.
8286 * @param iLUN The unit number.
8287 */
8288DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8289{
8290 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8291 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8292 {
8293 PPDMLED pLed;
8294 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8295 if (RT_FAILURE(rc))
8296 pLed = NULL;
8297 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8298 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8299 }
8300}
8301
8302
8303/**
8304 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8305 */
8306DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8307{
8308 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8309 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8310 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8311 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8312 return NULL;
8313}
8314
8315
8316/**
8317 * Destruct a status driver instance.
8318 *
8319 * @returns VBox status.
8320 * @param pDrvIns The driver instance data.
8321 */
8322DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8323{
8324 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8325 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8326 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8327
8328 if (pData->papLeds)
8329 {
8330 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8331 while (iLed-- > 0)
8332 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8333 }
8334}
8335
8336
8337/**
8338 * Construct a status driver instance.
8339 *
8340 * @copydoc FNPDMDRVCONSTRUCT
8341 */
8342DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8343{
8344 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8345 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8346 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8347
8348 /*
8349 * Validate configuration.
8350 */
8351 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8352 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8353 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8354 ("Configuration error: Not possible to attach anything to this driver!\n"),
8355 VERR_PDM_DRVINS_NO_ATTACH);
8356
8357 /*
8358 * Data.
8359 */
8360 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8361 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8362
8363 /*
8364 * Read config.
8365 */
8366 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8367 if (RT_FAILURE(rc))
8368 {
8369 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8370 return rc;
8371 }
8372
8373 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8374 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8375 pData->iFirstLUN = 0;
8376 else if (RT_FAILURE(rc))
8377 {
8378 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8379 return rc;
8380 }
8381
8382 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8383 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8384 pData->iLastLUN = 0;
8385 else if (RT_FAILURE(rc))
8386 {
8387 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8388 return rc;
8389 }
8390 if (pData->iFirstLUN > pData->iLastLUN)
8391 {
8392 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8393 return VERR_GENERAL_FAILURE;
8394 }
8395
8396 /*
8397 * Get the ILedPorts interface of the above driver/device and
8398 * query the LEDs we want.
8399 */
8400 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8401 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8402 VERR_PDM_MISSING_INTERFACE_ABOVE);
8403
8404 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8405 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8406
8407 return VINF_SUCCESS;
8408}
8409
8410
8411/**
8412 * Keyboard driver registration record.
8413 */
8414const PDMDRVREG Console::DrvStatusReg =
8415{
8416 /* u32Version */
8417 PDM_DRVREG_VERSION,
8418 /* szName */
8419 "MainStatus",
8420 /* szRCMod */
8421 "",
8422 /* szR0Mod */
8423 "",
8424 /* pszDescription */
8425 "Main status driver (Main as in the API).",
8426 /* fFlags */
8427 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8428 /* fClass. */
8429 PDM_DRVREG_CLASS_STATUS,
8430 /* cMaxInstances */
8431 ~0,
8432 /* cbInstance */
8433 sizeof(DRVMAINSTATUS),
8434 /* pfnConstruct */
8435 Console::drvStatus_Construct,
8436 /* pfnDestruct */
8437 Console::drvStatus_Destruct,
8438 /* pfnRelocate */
8439 NULL,
8440 /* pfnIOCtl */
8441 NULL,
8442 /* pfnPowerOn */
8443 NULL,
8444 /* pfnReset */
8445 NULL,
8446 /* pfnSuspend */
8447 NULL,
8448 /* pfnResume */
8449 NULL,
8450 /* pfnAttach */
8451 NULL,
8452 /* pfnDetach */
8453 NULL,
8454 /* pfnPowerOff */
8455 NULL,
8456 /* pfnSoftReset */
8457 NULL,
8458 /* u32EndVersion */
8459 PDM_DRVREG_VERSION
8460};
8461
8462/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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