VirtualBox

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

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

Main: irrelevant change sneaked in

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