VirtualBox

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

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

PCI: more 4.0 interfaces

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

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