VirtualBox

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

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

Main/GuestProperties: two missing NULL checks

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

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