VirtualBox

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

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

Main: Bandwidth groups for disks (and later network)

This introduces two new interfaces. The first one named IBandwidthGroup
represents one I/O limit and can be assigned to several mediums which
share this limit (which works only for harddisk images with the disabled
host cache).
The second one IBandwdithControl manages the groups and can create new ones
and destroy them if not required anymore.

VBoxManage: commands to access the bandwidth groups

Syntax:
VBoxManage storageattach <uuid|vmname>

...
--bandwidthgroup <name>

--bandwidthgroup assigns the specified device to the given group.

VBoxManage bandwidthctl <uuid|vmname>

--name <name>
--add disk|network
--limit <megabytes per second>
--delete

The --name parameter gives the name of the bandwidth group.
--add creates a new group of the given type (only disk is implemented so far)

with the given name.

--limit sets the limit to the given amount of MB/s

Note that limit can be changed while the VM is running. The VM
will immediately pick up the new limit for the given group name.

--delete deletes the group with the given name if it isn't used anymore.

Trying to delete a still used group will result in an error.

Example:

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 20
Creates a group named Test having a 20 MB/s limit.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup Limit
Adds a new disk to the SATA controller and assigns the bandwidth group Limit to it.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup none
Removes the bandwidth limit from the disk.

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 10
Changes the limit of bandwidth group Limit to 10 MB/s. If the VM is running the limit will be picked up
immediately.

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