VirtualBox

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

Last change on this file since 30787 was 30777, checked in by vboxsync, 14 years ago

Main: Cancelled -> Canceled.

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

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