VirtualBox

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

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

Main: moved listeners to location usable by fronends, rework

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

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