VirtualBox

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

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

Pass RDP credentials to the guest only from one client, when multiple clients connect concurrently (for VDI).

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

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