VirtualBox

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

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

Main: handle enumerate guest properties correctly at runtime if the patterns parameter is empty

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