VirtualBox

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

Last change on this file since 29480 was 29480, checked in by vboxsync, 15 years ago

Main: Remove IoBackendType in StorageController and have a boolean setting fUseAsyncHostIOMgr instead; change XML settings and frontends accordingly; change VBoxManage storagectl syntax to --hostiocache on|off as well

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