VirtualBox

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

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

release build fixes

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