VirtualBox

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

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

ConsoleImpl: Implemented VBOX_E_DONT_CALL_AGAIN for IConsoleCallback.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 265.7 KB
Line 
1/* $Id: ConsoleImpl.cpp 28959 2010-05-02 19:36:11Z 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 case StorageControllerType_LsiLogicSas:
3109 return "lsilogicscsi";
3110 case StorageControllerType_BusLogic:
3111 return "buslogic";
3112 case StorageControllerType_IntelAhci:
3113 return "ahci";
3114 case StorageControllerType_PIIX3:
3115 case StorageControllerType_PIIX4:
3116 case StorageControllerType_ICH6:
3117 return "piix3ide";
3118 case StorageControllerType_I82078:
3119 return "i82078";
3120 default:
3121 return NULL;
3122 }
3123}
3124
3125HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3126{
3127 switch (enmBus)
3128 {
3129 case StorageBus_IDE:
3130 case StorageBus_Floppy:
3131 {
3132 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3133 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3134 uLun = 2 * port + device;
3135 return S_OK;
3136 }
3137 case StorageBus_SATA:
3138 case StorageBus_SCSI:
3139 case StorageBus_SAS:
3140 {
3141 uLun = port;
3142 return S_OK;
3143 }
3144 default:
3145 uLun = 0;
3146 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3147 }
3148}
3149
3150// private methods
3151/////////////////////////////////////////////////////////////////////////////
3152
3153/**
3154 * Process a medium change.
3155 *
3156 * @param aMediumAttachment The medium attachment with the new medium state.
3157 * @param fForce Force medium chance, if it is locked or not.
3158 *
3159 * @note Locks this object for writing.
3160 */
3161HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce)
3162{
3163 AutoCaller autoCaller(this);
3164 AssertComRCReturnRC(autoCaller.rc());
3165
3166 /* We will need to release the write lock before calling EMT */
3167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 HRESULT rc = S_OK;
3170 const char *pszDevice = NULL;
3171
3172 SafeIfaceArray<IStorageController> ctrls;
3173 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3174 AssertComRC(rc);
3175 IMedium *pMedium;
3176 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3177 AssertComRC(rc);
3178 Bstr mediumLocation;
3179 if (pMedium)
3180 {
3181 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3182 AssertComRC(rc);
3183 }
3184
3185 Bstr attCtrlName;
3186 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3187 AssertComRC(rc);
3188 ComPtr<IStorageController> ctrl;
3189 for (size_t i = 0; i < ctrls.size(); ++i)
3190 {
3191 Bstr ctrlName;
3192 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3193 AssertComRC(rc);
3194 if (attCtrlName == ctrlName)
3195 {
3196 ctrl = ctrls[i];
3197 break;
3198 }
3199 }
3200 if (ctrl.isNull())
3201 {
3202 return setError(E_FAIL,
3203 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3204 }
3205 StorageControllerType_T enmCtrlType;
3206 rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
3207 AssertComRC(rc);
3208 pszDevice = convertControllerTypeToDev(enmCtrlType);
3209
3210 StorageBus_T enmBus;
3211 rc = ctrl->COMGETTER(Bus)(&enmBus);
3212 AssertComRC(rc);
3213 ULONG uInstance;
3214 rc = ctrl->COMGETTER(Instance)(&uInstance);
3215 AssertComRC(rc);
3216 IoBackendType_T enmIoBackend;
3217 rc = ctrl->COMGETTER(IoBackend)(&enmIoBackend);
3218 AssertComRC(rc);
3219
3220 /* protect mpVM */
3221 AutoVMCaller autoVMCaller(this);
3222 AssertComRCReturnRC(autoVMCaller.rc());
3223
3224 /*
3225 * Call worker in EMT, that's faster and safer than doing everything
3226 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3227 * here to make requests from under the lock in order to serialize them.
3228 */
3229 PVMREQ pReq;
3230 int vrc = VMR3ReqCall(mpVM, VMCPUID_ANY, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3231 (PFNRT)Console::changeRemovableMedium, 7,
3232 this, pszDevice, uInstance, enmBus, enmIoBackend,
3233 aMediumAttachment, fForce);
3234
3235 /* leave the lock before waiting for a result (EMT will call us back!) */
3236 alock.leave();
3237
3238 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3239 {
3240 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3241 AssertRC(vrc);
3242 if (RT_SUCCESS(vrc))
3243 vrc = pReq->iStatus;
3244 }
3245 VMR3ReqFree(pReq);
3246
3247 if (RT_SUCCESS(vrc))
3248 {
3249 LogFlowThisFunc(("Returns S_OK\n"));
3250 return S_OK;
3251 }
3252
3253 if (!pMedium)
3254 return setError(E_FAIL,
3255 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3256 mediumLocation.raw(), vrc);
3257
3258 return setError(E_FAIL,
3259 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3260 vrc);
3261}
3262
3263/**
3264 * Performs the medium change in EMT.
3265 *
3266 * @returns VBox status code.
3267 *
3268 * @param pThis Pointer to the Console object.
3269 * @param pcszDevice The PDM device name.
3270 * @param uInstance The PDM device instance.
3271 * @param uLun The PDM LUN number of the drive.
3272 * @param fHostDrive True if this is a host drive attachment.
3273 * @param pszPath The path to the media / drive which is now being mounted / captured.
3274 * If NULL no media or drive is attached and the LUN will be configured with
3275 * the default block driver with no media. This will also be the state if
3276 * mounting / capturing the specified media / drive fails.
3277 * @param pszFormat Medium format string, usually "RAW".
3278 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3279 *
3280 * @thread EMT
3281 */
3282DECLCALLBACK(int) Console::changeRemovableMedium(Console *pThis,
3283 const char *pcszDevice,
3284 unsigned uInstance,
3285 StorageBus_T enmBus,
3286 IoBackendType_T enmIoBackend,
3287 IMediumAttachment *aMediumAtt,
3288 bool fForce)
3289{
3290 LogFlowFunc(("pThis=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3291 pThis, uInstance, pcszDevice, enmBus, fForce));
3292
3293 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3294
3295 AutoCaller autoCaller(pThis);
3296 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3297
3298 PVM pVM = pThis->mpVM;
3299
3300 /*
3301 * Suspend the VM first.
3302 *
3303 * The VM must not be running since it might have pending I/O to
3304 * the drive which is being changed.
3305 */
3306 bool fResume;
3307 VMSTATE enmVMState = VMR3GetState(pVM);
3308 switch (enmVMState)
3309 {
3310 case VMSTATE_RESETTING:
3311 case VMSTATE_RUNNING:
3312 {
3313 LogFlowFunc(("Suspending the VM...\n"));
3314 /* disable the callback to prevent Console-level state change */
3315 pThis->mVMStateChangeCallbackDisabled = true;
3316 int rc = VMR3Suspend(pVM);
3317 pThis->mVMStateChangeCallbackDisabled = false;
3318 AssertRCReturn(rc, rc);
3319 fResume = true;
3320 break;
3321 }
3322
3323 case VMSTATE_SUSPENDED:
3324 case VMSTATE_CREATED:
3325 case VMSTATE_OFF:
3326 fResume = false;
3327 break;
3328
3329 case VMSTATE_RUNNING_LS:
3330 return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot change drive during live migration"));
3331
3332 default:
3333 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3334 }
3335
3336 /* Determine the base path for the device instance. */
3337 PCFGMNODE pCtlInst;
3338 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice,
3339 uInstance);
3340 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3341
3342 int rc = VINF_SUCCESS;
3343 int rcRet = VINF_SUCCESS;
3344
3345 rcRet = Console::configMediumAttachment(pCtlInst, pcszDevice, uInstance,
3346 enmBus, enmIoBackend,
3347 false /* fSetupMerge */,
3348 0 /* uMergeSource */,
3349 0 /* uMergeTarget */,
3350 aMediumAtt, pThis->mMachineState,
3351 NULL /* phrc */,
3352 true /* fAttachDetach */,
3353 fForce /* fForceUnmount */,
3354 pVM, NULL /* paLedDevType */);
3355 /** @todo this dumps everything attached to this device instance, which
3356 * is more than necessary. Dumping the changed LUN would be enough. */
3357 CFGMR3Dump(pCtlInst);
3358
3359 /*
3360 * Resume the VM if necessary.
3361 */
3362 if (fResume)
3363 {
3364 LogFlowFunc(("Resuming the VM...\n"));
3365 /* disable the callback to prevent Console-level state change */
3366 pThis->mVMStateChangeCallbackDisabled = true;
3367 rc = VMR3Resume(pVM);
3368 pThis->mVMStateChangeCallbackDisabled = false;
3369 AssertRC(rc);
3370 if (RT_FAILURE(rc))
3371 {
3372 /* too bad, we failed. try to sync the console state with the VMM state */
3373 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3374 }
3375 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3376 // error (if any) will be hidden from the caller. For proper reporting
3377 // of such multiple errors to the caller we need to enhance the
3378 // IVirtualBoxError interface. For now, give the first error the higher
3379 // priority.
3380 if (RT_SUCCESS(rcRet))
3381 rcRet = rc;
3382 }
3383
3384 LogFlowFunc(("Returning %Rrc\n", rcRet));
3385 return rcRet;
3386}
3387
3388
3389/**
3390 * Called by IInternalSessionControl::OnNetworkAdapterChange().
3391 *
3392 * @note Locks this object for writing.
3393 */
3394HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
3395{
3396 LogFlowThisFunc(("\n"));
3397
3398 AutoCaller autoCaller(this);
3399 AssertComRCReturnRC(autoCaller.rc());
3400
3401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3402
3403 /* Don't do anything if the VM isn't running */
3404 if (!mpVM)
3405 return S_OK;
3406
3407 /* protect mpVM */
3408 AutoVMCaller autoVMCaller(this);
3409 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3410
3411 /* Get the properties we need from the adapter */
3412 BOOL fCableConnected, fTraceEnabled;
3413 HRESULT rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
3414 AssertComRC(rc);
3415 if (SUCCEEDED(rc))
3416 {
3417 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
3418 AssertComRC(rc);
3419 }
3420 if (SUCCEEDED(rc))
3421 {
3422 ULONG ulInstance;
3423 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
3424 AssertComRC(rc);
3425 if (SUCCEEDED(rc))
3426 {
3427 /*
3428 * Find the pcnet instance, get the config interface and update
3429 * the link state.
3430 */
3431 NetworkAdapterType_T adapterType;
3432 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3433 AssertComRC(rc);
3434 const char *pszAdapterName = NULL;
3435 switch (adapterType)
3436 {
3437 case NetworkAdapterType_Am79C970A:
3438 case NetworkAdapterType_Am79C973:
3439 pszAdapterName = "pcnet";
3440 break;
3441#ifdef VBOX_WITH_E1000
3442 case NetworkAdapterType_I82540EM:
3443 case NetworkAdapterType_I82543GC:
3444 case NetworkAdapterType_I82545EM:
3445 pszAdapterName = "e1000";
3446 break;
3447#endif
3448#ifdef VBOX_WITH_VIRTIO
3449 case NetworkAdapterType_Virtio:
3450 pszAdapterName = "virtio-net";
3451 break;
3452#endif
3453 default:
3454 AssertFailed();
3455 pszAdapterName = "unknown";
3456 break;
3457 }
3458
3459 PPDMIBASE pBase;
3460 int vrc = PDMR3QueryDeviceLun(mpVM, pszAdapterName, ulInstance, 0, &pBase);
3461 ComAssertRC(vrc);
3462 if (RT_SUCCESS(vrc))
3463 {
3464 Assert(pBase);
3465 PPDMINETWORKCONFIG pINetCfg;
3466 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
3467 if (pINetCfg)
3468 {
3469 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
3470 fCableConnected));
3471 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
3472 fCableConnected ? PDMNETWORKLINKSTATE_UP
3473 : PDMNETWORKLINKSTATE_DOWN);
3474 ComAssertRC(vrc);
3475 }
3476#ifdef VBOX_DYNAMIC_NET_ATTACH
3477 if (RT_SUCCESS(vrc) && changeAdapter)
3478 {
3479 VMSTATE enmVMState = VMR3GetState(mpVM);
3480 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbit or deal correctly with the _LS variants */
3481 || enmVMState == VMSTATE_SUSPENDED)
3482 {
3483 if (fTraceEnabled && fCableConnected && pINetCfg)
3484 {
3485 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
3486 ComAssertRC(vrc);
3487 }
3488
3489 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3490
3491 if (fTraceEnabled && fCableConnected && pINetCfg)
3492 {
3493 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
3494 ComAssertRC(vrc);
3495 }
3496 }
3497 }
3498#endif /* VBOX_DYNAMIC_NET_ATTACH */
3499 }
3500
3501 if (RT_FAILURE(vrc))
3502 rc = E_FAIL;
3503 }
3504 }
3505
3506 /* notify console callbacks on success */
3507 if (SUCCEEDED(rc))
3508 CONSOLE_DO_CALLBACKS(OnNetworkAdapterChange,(aNetworkAdapter));
3509
3510 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3511 return rc;
3512}
3513
3514
3515#ifdef VBOX_DYNAMIC_NET_ATTACH
3516/**
3517 * Process a network adaptor change.
3518 *
3519 * @returns COM status code.
3520 *
3521 * @param pszDevice The PDM device name.
3522 * @param uInstance The PDM device instance.
3523 * @param uLun The PDM LUN number of the drive.
3524 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3525 *
3526 * @note Locks this object for writing.
3527 */
3528HRESULT Console::doNetworkAdapterChange(const char *pszDevice,
3529 unsigned uInstance,
3530 unsigned uLun,
3531 INetworkAdapter *aNetworkAdapter)
3532{
3533 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3534 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3535
3536 AutoCaller autoCaller(this);
3537 AssertComRCReturnRC(autoCaller.rc());
3538
3539 /* We will need to release the write lock before calling EMT */
3540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3541
3542 /* protect mpVM */
3543 AutoVMCaller autoVMCaller(this);
3544 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3545
3546 /*
3547 * Call worker in EMT, that's faster and safer than doing everything
3548 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3549 * here to make requests from under the lock in order to serialize them.
3550 */
3551 PVMREQ pReq;
3552 int vrc = VMR3ReqCall(mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3553 (PFNRT) Console::changeNetworkAttachment, 5,
3554 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3555
3556 /* leave the lock before waiting for a result (EMT will call us back!) */
3557 alock.leave();
3558
3559 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3560 {
3561 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3562 AssertRC(vrc);
3563 if (RT_SUCCESS(vrc))
3564 vrc = pReq->iStatus;
3565 }
3566 VMR3ReqFree(pReq);
3567
3568 if (RT_SUCCESS(vrc))
3569 {
3570 LogFlowThisFunc(("Returns S_OK\n"));
3571 return S_OK;
3572 }
3573
3574 return setError(E_FAIL,
3575 tr("Could not change the network adaptor attachement type (%Rrc)"),
3576 vrc);
3577}
3578
3579
3580/**
3581 * Performs the Network Adaptor change in EMT.
3582 *
3583 * @returns VBox status code.
3584 *
3585 * @param pThis Pointer to the Console object.
3586 * @param pszDevice The PDM device name.
3587 * @param uInstance The PDM device instance.
3588 * @param uLun The PDM LUN number of the drive.
3589 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3590 *
3591 * @thread EMT
3592 * @note Locks the Console object for writing.
3593 */
3594DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
3595 const char *pszDevice,
3596 unsigned uInstance,
3597 unsigned uLun,
3598 INetworkAdapter *aNetworkAdapter)
3599{
3600 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3601 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3602
3603 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3604
3605 AssertMsg( ( !strcmp(pszDevice, "pcnet")
3606 || !strcmp(pszDevice, "e1000")
3607 || !strcmp(pszDevice, "virtio-net"))
3608 && (uLun == 0)
3609 && (uInstance < SchemaDefs::NetworkAdapterCount),
3610 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3611 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3612
3613 AutoCaller autoCaller(pThis);
3614 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3615
3616 /* protect mpVM */
3617 AutoVMCaller autoVMCaller(pThis);
3618 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3619
3620 PVM pVM = pThis->mpVM;
3621
3622 /*
3623 * Suspend the VM first.
3624 *
3625 * The VM must not be running since it might have pending I/O to
3626 * the drive which is being changed.
3627 */
3628 bool fResume;
3629 VMSTATE enmVMState = VMR3GetState(pVM);
3630 switch (enmVMState)
3631 {
3632 case VMSTATE_RESETTING:
3633 case VMSTATE_RUNNING:
3634 {
3635 LogFlowFunc(("Suspending the VM...\n"));
3636 /* disable the callback to prevent Console-level state change */
3637 pThis->mVMStateChangeCallbackDisabled = true;
3638 int rc = VMR3Suspend(pVM);
3639 pThis->mVMStateChangeCallbackDisabled = false;
3640 AssertRCReturn(rc, rc);
3641 fResume = true;
3642 break;
3643 }
3644
3645 case VMSTATE_SUSPENDED:
3646 case VMSTATE_CREATED:
3647 case VMSTATE_OFF:
3648 fResume = false;
3649 break;
3650
3651 default:
3652 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3653 }
3654
3655 int rc = VINF_SUCCESS;
3656 int rcRet = VINF_SUCCESS;
3657
3658 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3659 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3660 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
3661 AssertRelease(pInst);
3662
3663 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
3664
3665 /*
3666 * Resume the VM if necessary.
3667 */
3668 if (fResume)
3669 {
3670 LogFlowFunc(("Resuming the VM...\n"));
3671 /* disable the callback to prevent Console-level state change */
3672 pThis->mVMStateChangeCallbackDisabled = true;
3673 rc = VMR3Resume(pVM);
3674 pThis->mVMStateChangeCallbackDisabled = false;
3675 AssertRC(rc);
3676 if (RT_FAILURE(rc))
3677 {
3678 /* too bad, we failed. try to sync the console state with the VMM state */
3679 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3680 }
3681 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3682 // error (if any) will be hidden from the caller. For proper reporting
3683 // of such multiple errors to the caller we need to enhance the
3684 // IVirtualBoxError interface. For now, give the first error the higher
3685 // priority.
3686 if (RT_SUCCESS(rcRet))
3687 rcRet = rc;
3688 }
3689
3690 LogFlowFunc(("Returning %Rrc\n", rcRet));
3691 return rcRet;
3692}
3693#endif /* VBOX_DYNAMIC_NET_ATTACH */
3694
3695
3696/**
3697 * Called by IInternalSessionControl::OnSerialPortChange().
3698 *
3699 * @note Locks this object for writing.
3700 */
3701HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
3702{
3703 LogFlowThisFunc(("\n"));
3704
3705 AutoCaller autoCaller(this);
3706 AssertComRCReturnRC(autoCaller.rc());
3707
3708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3709
3710 /* Don't do anything if the VM isn't running */
3711 if (!mpVM)
3712 return S_OK;
3713
3714 HRESULT rc = S_OK;
3715
3716 /* protect mpVM */
3717 AutoVMCaller autoVMCaller(this);
3718 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3719
3720 /* nothing to do so far */
3721
3722 /* notify console callbacks on success */
3723 if (SUCCEEDED(rc))
3724 CONSOLE_DO_CALLBACKS(OnSerialPortChange,(aSerialPort));
3725
3726 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3727 return rc;
3728}
3729
3730/**
3731 * Called by IInternalSessionControl::OnParallelPortChange().
3732 *
3733 * @note Locks this object for writing.
3734 */
3735HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
3736{
3737 LogFlowThisFunc(("\n"));
3738
3739 AutoCaller autoCaller(this);
3740 AssertComRCReturnRC(autoCaller.rc());
3741
3742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3743
3744 /* Don't do anything if the VM isn't running */
3745 if (!mpVM)
3746 return S_OK;
3747
3748 HRESULT rc = S_OK;
3749
3750 /* protect mpVM */
3751 AutoVMCaller autoVMCaller(this);
3752 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3753
3754 /* nothing to do so far */
3755
3756 /* notify console callbacks on success */
3757 if (SUCCEEDED(rc))
3758 CONSOLE_DO_CALLBACKS(OnParallelPortChange,(aParallelPort));
3759
3760 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3761 return rc;
3762}
3763
3764/**
3765 * Called by IInternalSessionControl::OnStorageControllerChange().
3766 *
3767 * @note Locks this object for writing.
3768 */
3769HRESULT Console::onStorageControllerChange()
3770{
3771 LogFlowThisFunc(("\n"));
3772
3773 AutoCaller autoCaller(this);
3774 AssertComRCReturnRC(autoCaller.rc());
3775
3776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3777
3778 /* Don't do anything if the VM isn't running */
3779 if (!mpVM)
3780 return S_OK;
3781
3782 HRESULT rc = S_OK;
3783
3784 /* protect mpVM */
3785 AutoVMCaller autoVMCaller(this);
3786 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3787
3788 /* nothing to do so far */
3789
3790 /* notify console callbacks on success */
3791 if (SUCCEEDED(rc))
3792 CONSOLE_DO_CALLBACKS(OnStorageControllerChange,());
3793
3794 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3795 return rc;
3796}
3797
3798/**
3799 * Called by IInternalSessionControl::OnMediumChange().
3800 *
3801 * @note Locks this object for writing.
3802 */
3803HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
3804{
3805 LogFlowThisFunc(("\n"));
3806
3807 AutoCaller autoCaller(this);
3808 AssertComRCReturnRC(autoCaller.rc());
3809
3810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3811
3812 /* Don't do anything if the VM isn't running */
3813 if (!mpVM)
3814 return S_OK;
3815
3816 HRESULT rc = S_OK;
3817
3818 /* protect mpVM */
3819 AutoVMCaller autoVMCaller(this);
3820 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3821
3822 rc = doMediumChange(aMediumAttachment, !!aForce);
3823
3824 /* notify console callbacks on success */
3825 if (SUCCEEDED(rc))
3826 CONSOLE_DO_CALLBACKS(OnMediumChange,(aMediumAttachment));
3827
3828 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3829 return rc;
3830}
3831
3832/**
3833 * Called by IInternalSessionControl::OnCPUChange().
3834 *
3835 * @note Locks this object for writing.
3836 */
3837HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
3838{
3839 LogFlowThisFunc(("\n"));
3840
3841 AutoCaller autoCaller(this);
3842 AssertComRCReturnRC(autoCaller.rc());
3843
3844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3845
3846 /* Don't do anything if the VM isn't running */
3847 if (!mpVM)
3848 return S_OK;
3849
3850 HRESULT rc = S_OK;
3851
3852 /* protect mpVM */
3853 AutoVMCaller autoVMCaller(this);
3854 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3855
3856 if (aRemove)
3857 rc = doCPURemove(aCPU);
3858 else
3859 rc = doCPUAdd(aCPU);
3860
3861 /* notify console callbacks on success */
3862 if (SUCCEEDED(rc))
3863 CONSOLE_DO_CALLBACKS(OnCPUChange,(aCPU, aRemove));
3864
3865 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3866 return rc;
3867}
3868
3869/**
3870 * Called by IInternalSessionControl::OnVRDPServerChange().
3871 *
3872 * @note Locks this object for writing.
3873 */
3874HRESULT Console::onVRDPServerChange()
3875{
3876 AutoCaller autoCaller(this);
3877 AssertComRCReturnRC(autoCaller.rc());
3878
3879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3880
3881 HRESULT rc = S_OK;
3882
3883 if ( mVRDPServer
3884 && ( mMachineState == MachineState_Running
3885 || mMachineState == MachineState_Teleporting
3886 || mMachineState == MachineState_LiveSnapshotting
3887 )
3888 )
3889 {
3890 BOOL vrdpEnabled = FALSE;
3891
3892 rc = mVRDPServer->COMGETTER(Enabled)(&vrdpEnabled);
3893 ComAssertComRCRetRC(rc);
3894
3895 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
3896 alock.leave();
3897
3898 if (vrdpEnabled)
3899 {
3900 // If there was no VRDP server started the 'stop' will do nothing.
3901 // However if a server was started and this notification was called,
3902 // we have to restart the server.
3903 mConsoleVRDPServer->Stop();
3904
3905 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
3906 {
3907 rc = E_FAIL;
3908 }
3909 else
3910 {
3911 mConsoleVRDPServer->EnableConnections();
3912 }
3913 }
3914 else
3915 {
3916 mConsoleVRDPServer->Stop();
3917 }
3918
3919 alock.enter();
3920 }
3921
3922 /* notify console callbacks on success */
3923 if (SUCCEEDED(rc))
3924 CONSOLE_DO_CALLBACKS(OnVRDPServerChange,());
3925
3926 return rc;
3927}
3928
3929/**
3930 * @note Locks this object for reading.
3931 */
3932void Console::onRemoteDisplayInfoChange()
3933{
3934 AutoCaller autoCaller(this);
3935 AssertComRCReturnVoid(autoCaller.rc());
3936
3937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3938
3939 CONSOLE_DO_CALLBACKS(OnRemoteDisplayInfoChange,());
3940}
3941
3942
3943
3944/**
3945 * Called by IInternalSessionControl::OnUSBControllerChange().
3946 *
3947 * @note Locks this object for writing.
3948 */
3949HRESULT Console::onUSBControllerChange()
3950{
3951 LogFlowThisFunc(("\n"));
3952
3953 AutoCaller autoCaller(this);
3954 AssertComRCReturnRC(autoCaller.rc());
3955
3956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3957
3958 /* Ignore if no VM is running yet. */
3959 if (!mpVM)
3960 return S_OK;
3961
3962 HRESULT rc = S_OK;
3963
3964/// @todo (dmik)
3965// check for the Enabled state and disable virtual USB controller??
3966// Anyway, if we want to query the machine's USB Controller we need to cache
3967// it to mUSBController in #init() (as it is done with mDVDDrive).
3968//
3969// bird: While the VM supports hot-plugging, I doubt any guest can handle it at this time... :-)
3970//
3971// /* protect mpVM */
3972// AutoVMCaller autoVMCaller(this);
3973// if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3974
3975 /* notify console callbacks on success */
3976 if (SUCCEEDED(rc))
3977 CONSOLE_DO_CALLBACKS(OnUSBControllerChange,());
3978
3979 return rc;
3980}
3981
3982/**
3983 * Called by IInternalSessionControl::OnSharedFolderChange().
3984 *
3985 * @note Locks this object for writing.
3986 */
3987HRESULT Console::onSharedFolderChange(BOOL aGlobal)
3988{
3989 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
3990
3991 AutoCaller autoCaller(this);
3992 AssertComRCReturnRC(autoCaller.rc());
3993
3994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3995
3996 HRESULT rc = fetchSharedFolders(aGlobal);
3997
3998 /* notify console callbacks on success */
3999 if (SUCCEEDED(rc))
4000 {
4001 if (aGlobal)
4002 CONSOLE_DO_CALLBACKS(OnSharedFolderChange,(Scope_Global));
4003 else
4004 CONSOLE_DO_CALLBACKS(OnSharedFolderChange,(Scope_Machine));
4005 }
4006
4007 return rc;
4008}
4009
4010/**
4011 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4012 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4013 * returns TRUE for a given remote USB device.
4014 *
4015 * @return S_OK if the device was attached to the VM.
4016 * @return failure if not attached.
4017 *
4018 * @param aDevice
4019 * The device in question.
4020 * @param aMaskedIfs
4021 * The interfaces to hide from the guest.
4022 *
4023 * @note Locks this object for writing.
4024 */
4025HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4026{
4027#ifdef VBOX_WITH_USB
4028 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4029
4030 AutoCaller autoCaller(this);
4031 ComAssertComRCRetRC(autoCaller.rc());
4032
4033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4034
4035 /* protect mpVM (we don't need error info, since it's a callback) */
4036 AutoVMCallerQuiet autoVMCaller(this);
4037 if (FAILED(autoVMCaller.rc()))
4038 {
4039 /* The VM may be no more operational when this message arrives
4040 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4041 * autoVMCaller.rc() will return a failure in this case. */
4042 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4043 mMachineState));
4044 return autoVMCaller.rc();
4045 }
4046
4047 if (aError != NULL)
4048 {
4049 /* notify callbacks about the error */
4050 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4051 return S_OK;
4052 }
4053
4054 /* Don't proceed unless there's at least one USB hub. */
4055 if (!PDMR3USBHasHub(mpVM))
4056 {
4057 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4058 return E_FAIL;
4059 }
4060
4061 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4062 if (FAILED(rc))
4063 {
4064 /* take the current error info */
4065 com::ErrorInfoKeeper eik;
4066 /* the error must be a VirtualBoxErrorInfo instance */
4067 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
4068 Assert(!error.isNull());
4069 if (!error.isNull())
4070 {
4071 /* notify callbacks about the error */
4072 onUSBDeviceStateChange(aDevice, true /* aAttached */, error);
4073 }
4074 }
4075
4076 return rc;
4077
4078#else /* !VBOX_WITH_USB */
4079 return E_FAIL;
4080#endif /* !VBOX_WITH_USB */
4081}
4082
4083/**
4084 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
4085 * processRemoteUSBDevices().
4086 *
4087 * @note Locks this object for writing.
4088 */
4089HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
4090 IVirtualBoxErrorInfo *aError)
4091{
4092#ifdef VBOX_WITH_USB
4093 Guid Uuid(aId);
4094 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
4095
4096 AutoCaller autoCaller(this);
4097 AssertComRCReturnRC(autoCaller.rc());
4098
4099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4100
4101 /* Find the device. */
4102 ComObjPtr<OUSBDevice> device;
4103 USBDeviceList::iterator it = mUSBDevices.begin();
4104 while (it != mUSBDevices.end())
4105 {
4106 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
4107 if ((*it)->id() == Uuid)
4108 {
4109 device = *it;
4110 break;
4111 }
4112 ++ it;
4113 }
4114
4115
4116 if (device.isNull())
4117 {
4118 LogFlowThisFunc(("USB device not found.\n"));
4119
4120 /* The VM may be no more operational when this message arrives
4121 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
4122 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
4123 * failure in this case. */
4124
4125 AutoVMCallerQuiet autoVMCaller(this);
4126 if (FAILED(autoVMCaller.rc()))
4127 {
4128 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
4129 mMachineState));
4130 return autoVMCaller.rc();
4131 }
4132
4133 /* the device must be in the list otherwise */
4134 AssertFailedReturn(E_FAIL);
4135 }
4136
4137 if (aError != NULL)
4138 {
4139 /* notify callback about an error */
4140 onUSBDeviceStateChange(device, false /* aAttached */, aError);
4141 return S_OK;
4142 }
4143
4144 HRESULT rc = detachUSBDevice(it);
4145
4146 if (FAILED(rc))
4147 {
4148 /* take the current error info */
4149 com::ErrorInfoKeeper eik;
4150 /* the error must be a VirtualBoxErrorInfo instance */
4151 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
4152 Assert(!error.isNull());
4153 if (!error.isNull())
4154 {
4155 /* notify callbacks about the error */
4156 onUSBDeviceStateChange(device, false /* aAttached */, error);
4157 }
4158 }
4159
4160 return rc;
4161
4162#else /* !VBOX_WITH_USB */
4163 return E_FAIL;
4164#endif /* !VBOX_WITH_USB */
4165}
4166
4167/**
4168 * @note Temporarily locks this object for writing.
4169 */
4170HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
4171 ULONG64 *aTimestamp, BSTR *aFlags)
4172{
4173#ifndef VBOX_WITH_GUEST_PROPS
4174 ReturnComNotImplemented();
4175#else /* VBOX_WITH_GUEST_PROPS */
4176 if (!VALID_PTR(aName))
4177 return E_INVALIDARG;
4178 if (!VALID_PTR(aValue))
4179 return E_POINTER;
4180 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
4181 return E_POINTER;
4182 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4183 return E_POINTER;
4184
4185 AutoCaller autoCaller(this);
4186 AssertComRCReturnRC(autoCaller.rc());
4187
4188 /* protect mpVM (if not NULL) */
4189 AutoVMCallerWeak autoVMCaller(this);
4190 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4191
4192 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4193 * autoVMCaller, so there is no need to hold a lock of this */
4194
4195 HRESULT rc = E_UNEXPECTED;
4196 using namespace guestProp;
4197
4198 try
4199 {
4200 VBOXHGCMSVCPARM parm[4];
4201 Utf8Str Utf8Name = aName;
4202 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4203
4204 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4205 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4206 /* The + 1 is the null terminator */
4207 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4208 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4209 parm[1].u.pointer.addr = pszBuffer;
4210 parm[1].u.pointer.size = sizeof(pszBuffer);
4211 int vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
4212 4, &parm[0]);
4213 /* The returned string should never be able to be greater than our buffer */
4214 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
4215 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4216 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4217 {
4218 rc = S_OK;
4219 if (vrc != VERR_NOT_FOUND)
4220 {
4221 Utf8Str strBuffer(pszBuffer);
4222 strBuffer.cloneTo(aValue);
4223
4224 *aTimestamp = parm[2].u.uint64;
4225
4226 size_t iFlags = strBuffer.length() + 1;
4227 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4228 }
4229 else
4230 aValue = NULL;
4231 }
4232 else
4233 rc = setError(E_UNEXPECTED,
4234 tr("The service call failed with the error %Rrc"),
4235 vrc);
4236 }
4237 catch(std::bad_alloc & /*e*/)
4238 {
4239 rc = E_OUTOFMEMORY;
4240 }
4241 return rc;
4242#endif /* VBOX_WITH_GUEST_PROPS */
4243}
4244
4245/**
4246 * @note Temporarily locks this object for writing.
4247 */
4248HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4249{
4250#ifndef VBOX_WITH_GUEST_PROPS
4251 ReturnComNotImplemented();
4252#else /* VBOX_WITH_GUEST_PROPS */
4253 if (!VALID_PTR(aName))
4254 return E_INVALIDARG;
4255 if ((aValue != NULL) && !VALID_PTR(aValue))
4256 return E_INVALIDARG;
4257 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4258 return E_INVALIDARG;
4259
4260 AutoCaller autoCaller(this);
4261 AssertComRCReturnRC(autoCaller.rc());
4262
4263 /* protect mpVM (if not NULL) */
4264 AutoVMCallerWeak autoVMCaller(this);
4265 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4266
4267 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4268 * autoVMCaller, so there is no need to hold a lock of this */
4269
4270 HRESULT rc = E_UNEXPECTED;
4271 using namespace guestProp;
4272
4273 VBOXHGCMSVCPARM parm[3];
4274 Utf8Str Utf8Name = aName;
4275 int vrc = VINF_SUCCESS;
4276
4277 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4278 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4279 /* The + 1 is the null terminator */
4280 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4281 Utf8Str Utf8Value = aValue;
4282 if (aValue != NULL)
4283 {
4284 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4285 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4286 /* The + 1 is the null terminator */
4287 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4288 }
4289 Utf8Str Utf8Flags = aFlags;
4290 if (aFlags != NULL)
4291 {
4292 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4293 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4294 /* The + 1 is the null terminator */
4295 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4296 }
4297 if ((aValue != NULL) && (aFlags != NULL))
4298 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
4299 3, &parm[0]);
4300 else if (aValue != NULL)
4301 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4302 2, &parm[0]);
4303 else
4304 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
4305 1, &parm[0]);
4306 if (RT_SUCCESS(vrc))
4307 rc = S_OK;
4308 else
4309 rc = setError(E_UNEXPECTED,
4310 tr("The service call failed with the error %Rrc"),
4311 vrc);
4312 return rc;
4313#endif /* VBOX_WITH_GUEST_PROPS */
4314}
4315
4316
4317/**
4318 * @note Temporarily locks this object for writing.
4319 */
4320HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
4321 ComSafeArrayOut(BSTR, aNames),
4322 ComSafeArrayOut(BSTR, aValues),
4323 ComSafeArrayOut(ULONG64, aTimestamps),
4324 ComSafeArrayOut(BSTR, aFlags))
4325{
4326#ifndef VBOX_WITH_GUEST_PROPS
4327 ReturnComNotImplemented();
4328#else /* VBOX_WITH_GUEST_PROPS */
4329 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4330 return E_POINTER;
4331 if (ComSafeArrayOutIsNull(aNames))
4332 return E_POINTER;
4333 if (ComSafeArrayOutIsNull(aValues))
4334 return E_POINTER;
4335 if (ComSafeArrayOutIsNull(aTimestamps))
4336 return E_POINTER;
4337 if (ComSafeArrayOutIsNull(aFlags))
4338 return E_POINTER;
4339
4340 AutoCaller autoCaller(this);
4341 AssertComRCReturnRC(autoCaller.rc());
4342
4343 /* protect mpVM (if not NULL) */
4344 AutoVMCallerWeak autoVMCaller(this);
4345 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4346
4347 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4348 * autoVMCaller, so there is no need to hold a lock of this */
4349
4350 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
4351 ComSafeArrayOutArg(aValues),
4352 ComSafeArrayOutArg(aTimestamps),
4353 ComSafeArrayOutArg(aFlags));
4354#endif /* VBOX_WITH_GUEST_PROPS */
4355}
4356
4357
4358/*
4359 * Internal: helper function for connecting progress reporting
4360 */
4361static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
4362{
4363 HRESULT rc = S_OK;
4364 IProgress *pProgress = static_cast<IProgress *>(pvUser);
4365 if (pProgress)
4366 rc = pProgress->SetCurrentOperationProgress(uPercentage);
4367 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
4368}
4369
4370/**
4371 * @note Temporarily locks this object for writing.
4372 */
4373HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
4374 ULONG aSourceIdx, ULONG aTargetIdx,
4375 IMedium *aSource, IMedium *aTarget,
4376 BOOL aMergeForward,
4377 IMedium *aParentForTarget,
4378 ComSafeArrayIn(IMedium *, aChildrenToReparent),
4379 IProgress *aProgress)
4380{
4381 AutoCaller autoCaller(this);
4382 AssertComRCReturnRC(autoCaller.rc());
4383
4384 HRESULT rc = S_OK;
4385 int vrc = VINF_SUCCESS;
4386 PVM pVM = mpVM;
4387
4388 /* We will need to release the lock before doing the actual merge */
4389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4390
4391 /* paranoia - we don't want merges to happen while teleporting etc. */
4392 switch (mMachineState)
4393 {
4394 case MachineState_DeletingSnapshotOnline:
4395 case MachineState_DeletingSnapshotPaused:
4396 break;
4397
4398 default:
4399 return setError(VBOX_E_INVALID_VM_STATE,
4400 tr("Invalid machine state: %s"),
4401 Global::stringifyMachineState(mMachineState));
4402 }
4403
4404 SafeIfaceArray<IStorageController> ctrls;
4405 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4406 AssertComRC(rc);
4407 LONG lDev;
4408 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
4409 AssertComRC(rc);
4410 LONG lPort;
4411 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
4412 AssertComRC(rc);
4413 IMedium *pMedium;
4414 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4415 AssertComRC(rc);
4416 Bstr mediumLocation;
4417 if (pMedium)
4418 {
4419 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4420 AssertComRC(rc);
4421 }
4422
4423 Bstr attCtrlName;
4424 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4425 AssertComRC(rc);
4426 ComPtr<IStorageController> ctrl;
4427 for (size_t i = 0; i < ctrls.size(); ++i)
4428 {
4429 Bstr ctrlName;
4430 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4431 AssertComRC(rc);
4432 if (attCtrlName == ctrlName)
4433 {
4434 ctrl = ctrls[i];
4435 break;
4436 }
4437 }
4438 if (ctrl.isNull())
4439 {
4440 return setError(E_FAIL,
4441 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
4442 }
4443 StorageControllerType_T enmCtrlType;
4444 rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
4445 AssertComRC(rc);
4446 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
4447
4448 StorageBus_T enmBus;
4449 rc = ctrl->COMGETTER(Bus)(&enmBus);
4450 AssertComRC(rc);
4451 ULONG uInstance;
4452 rc = ctrl->COMGETTER(Instance)(&uInstance);
4453 AssertComRC(rc);
4454 IoBackendType_T enmIoBackend;
4455 rc = ctrl->COMGETTER(IoBackend)(&enmIoBackend);
4456 AssertComRC(rc);
4457
4458 unsigned uLUN;
4459 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
4460 AssertComRCReturnRC(rc);
4461
4462 alock.release();
4463
4464 /* Pause the VM, as it might have pending IO on this drive */
4465 VMSTATE enmVMState = VMR3GetState(pVM);
4466 if (mMachineState == MachineState_DeletingSnapshotOnline)
4467 {
4468 LogFlowFunc(("Suspending the VM...\n"));
4469 /* disable the callback to prevent Console-level state change */
4470 mVMStateChangeCallbackDisabled = true;
4471 int vrc2 = VMR3Suspend(pVM);
4472 mVMStateChangeCallbackDisabled = false;
4473 AssertRCReturn(vrc2, E_FAIL);
4474 }
4475
4476 vrc = VMR3ReqCallWait(pVM,
4477 VMCPUID_ANY,
4478 (PFNRT)reconfigureMediumAttachment,
4479 11,
4480 pVM,
4481 pcszDevice,
4482 uInstance,
4483 enmBus,
4484 enmIoBackend,
4485 true /* fSetupMerge */,
4486 aSourceIdx,
4487 aTargetIdx,
4488 aMediumAttachment,
4489 mMachineState,
4490 &rc);
4491 /* error handling is after resuming the VM */
4492
4493 if (mMachineState == MachineState_DeletingSnapshotOnline)
4494 {
4495 LogFlowFunc(("Resuming the VM...\n"));
4496 /* disable the callback to prevent Console-level state change */
4497 mVMStateChangeCallbackDisabled = true;
4498 int vrc2 = VMR3Resume(pVM);
4499 mVMStateChangeCallbackDisabled = false;
4500 AssertRC(vrc2);
4501 if (RT_FAILURE(vrc2))
4502 {
4503 /* too bad, we failed. try to sync the console state with the VMM state */
4504 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4505 }
4506 }
4507
4508 if (RT_FAILURE(vrc))
4509 return setError(E_FAIL, tr("%Rrc"), vrc);
4510 if (FAILED(rc))
4511 return rc;
4512
4513 PPDMIBASE pIBase = NULL;
4514 PPDMIMEDIA pIMedium = NULL;
4515 vrc = PDMR3QueryDriverOnLun(pVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
4516 if (RT_SUCCESS(vrc))
4517 {
4518 if (pIBase)
4519 {
4520 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
4521 if (!pIMedium)
4522 return setError(E_FAIL, tr("could not query medium interface of controller"));
4523 }
4524 else
4525 return setError(E_FAIL, tr("could not query base interface of controller"));
4526 }
4527
4528 /* Finally trigger the merge. */
4529 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
4530 if (RT_FAILURE(vrc))
4531 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
4532
4533 /* Pause the VM, as it might have pending IO on this drive */
4534 enmVMState = VMR3GetState(pVM);
4535 if (mMachineState == MachineState_DeletingSnapshotOnline)
4536 {
4537 LogFlowFunc(("Suspending the VM...\n"));
4538 /* disable the callback to prevent Console-level state change */
4539 mVMStateChangeCallbackDisabled = true;
4540 int vrc2 = VMR3Suspend(pVM);
4541 mVMStateChangeCallbackDisabled = false;
4542 AssertRCReturn(vrc2, E_FAIL);
4543 }
4544
4545 /* Update medium chain and state now, so that the VM can continue. */
4546 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
4547 aMergeForward, aParentForTarget,
4548 ComSafeArrayInArg(aChildrenToReparent));
4549
4550 vrc = VMR3ReqCallWait(pVM,
4551 VMCPUID_ANY,
4552 (PFNRT)reconfigureMediumAttachment,
4553 11,
4554 pVM,
4555 pcszDevice,
4556 uInstance,
4557 enmBus,
4558 enmIoBackend,
4559 false /* fSetupMerge */,
4560 0 /* uMergeSource */,
4561 0 /* uMergeTarget */,
4562 aMediumAttachment,
4563 mMachineState,
4564 &rc);
4565 /* error handling is after resuming the VM */
4566
4567 if (mMachineState == MachineState_DeletingSnapshotOnline)
4568 {
4569 LogFlowFunc(("Resuming the VM...\n"));
4570 /* disable the callback to prevent Console-level state change */
4571 mVMStateChangeCallbackDisabled = true;
4572 int vrc2 = VMR3Resume(pVM);
4573 mVMStateChangeCallbackDisabled = false;
4574 AssertRC(vrc2);
4575 if (RT_FAILURE(vrc2))
4576 {
4577 /* too bad, we failed. try to sync the console state with the VMM state */
4578 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4579 }
4580 }
4581
4582 if (RT_FAILURE(vrc))
4583 return setError(E_FAIL, tr("%Rrc"), vrc);
4584 if (FAILED(rc))
4585 return rc;
4586
4587 return rc;
4588}
4589
4590
4591/**
4592 * Gets called by Session::UpdateMachineState()
4593 * (IInternalSessionControl::updateMachineState()).
4594 *
4595 * Must be called only in certain cases (see the implementation).
4596 *
4597 * @note Locks this object for writing.
4598 */
4599HRESULT Console::updateMachineState(MachineState_T aMachineState)
4600{
4601 AutoCaller autoCaller(this);
4602 AssertComRCReturnRC(autoCaller.rc());
4603
4604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4605
4606 AssertReturn( mMachineState == MachineState_Saving
4607 || mMachineState == MachineState_LiveSnapshotting
4608 || mMachineState == MachineState_RestoringSnapshot
4609 || mMachineState == MachineState_DeletingSnapshot
4610 || mMachineState == MachineState_DeletingSnapshotOnline
4611 || mMachineState == MachineState_DeletingSnapshotPaused
4612 , E_FAIL);
4613
4614 return setMachineStateLocally(aMachineState);
4615}
4616
4617/**
4618 * @note Locks this object for writing.
4619 */
4620void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4621 uint32_t xHot, uint32_t yHot,
4622 uint32_t width, uint32_t height,
4623 void *pShape)
4624{
4625#if 0
4626 LogFlowThisFuncEnter();
4627 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
4628 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4629#endif
4630
4631 AutoCaller autoCaller(this);
4632 AssertComRCReturnVoid(autoCaller.rc());
4633
4634 /* We need a write lock because we alter the cached callback data */
4635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4636
4637 /* Save the callback arguments */
4638 mCallbackData.mpsc.visible = fVisible;
4639 mCallbackData.mpsc.alpha = fAlpha;
4640 mCallbackData.mpsc.xHot = xHot;
4641 mCallbackData.mpsc.yHot = yHot;
4642 mCallbackData.mpsc.width = width;
4643 mCallbackData.mpsc.height = height;
4644
4645 /* start with not valid */
4646 bool wasValid = mCallbackData.mpsc.valid;
4647 mCallbackData.mpsc.valid = false;
4648
4649 if (pShape != NULL)
4650 {
4651 size_t cb = (width + 7) / 8 * height; /* size of the AND mask */
4652 cb = ((cb + 3) & ~3) + width * 4 * height; /* + gap + size of the XOR mask */
4653 /* try to reuse the old shape buffer if the size is the same */
4654 if (!wasValid)
4655 mCallbackData.mpsc.shape = NULL;
4656 else
4657 if (mCallbackData.mpsc.shape != NULL && mCallbackData.mpsc.shapeSize != cb)
4658 {
4659 RTMemFree(mCallbackData.mpsc.shape);
4660 mCallbackData.mpsc.shape = NULL;
4661 }
4662 if (mCallbackData.mpsc.shape == NULL)
4663 {
4664 mCallbackData.mpsc.shape = (BYTE *) RTMemAllocZ(cb);
4665 AssertReturnVoid(mCallbackData.mpsc.shape);
4666 }
4667 mCallbackData.mpsc.shapeSize = cb;
4668 memcpy(mCallbackData.mpsc.shape, pShape, cb);
4669 }
4670 else
4671 {
4672 if (wasValid && mCallbackData.mpsc.shape != NULL)
4673 RTMemFree(mCallbackData.mpsc.shape);
4674 mCallbackData.mpsc.shape = NULL;
4675 mCallbackData.mpsc.shapeSize = 0;
4676 }
4677
4678 mCallbackData.mpsc.valid = true;
4679
4680 CONSOLE_DO_CALLBACKS(OnMousePointerShapeChange,(fVisible, fAlpha, xHot, yHot, width, height, (BYTE *) pShape));
4681
4682#if 0
4683 LogFlowThisFuncLeave();
4684#endif
4685}
4686
4687/**
4688 * @note Locks this object for writing.
4689 */
4690void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
4691{
4692 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
4693 supportsAbsolute, supportsRelative, needsHostCursor));
4694
4695 AutoCaller autoCaller(this);
4696 AssertComRCReturnVoid(autoCaller.rc());
4697
4698 /* We need a write lock because we alter the cached callback data */
4699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4700
4701 /* save the callback arguments */
4702 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
4703 mCallbackData.mcc.supportsRelative = supportsRelative;
4704 mCallbackData.mcc.needsHostCursor = needsHostCursor;
4705 mCallbackData.mcc.valid = true;
4706
4707 CONSOLE_DO_CALLBACKS(OnMouseCapabilityChange,(supportsAbsolute, supportsRelative, needsHostCursor));
4708}
4709
4710/**
4711 * @note Locks this object for reading.
4712 */
4713void Console::onStateChange(MachineState_T machineState)
4714{
4715 AutoCaller autoCaller(this);
4716 AssertComRCReturnVoid(autoCaller.rc());
4717
4718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4719 CONSOLE_DO_CALLBACKS(OnStateChange,(machineState));
4720}
4721
4722/**
4723 * @note Locks this object for reading.
4724 */
4725void Console::onAdditionsStateChange()
4726{
4727 AutoCaller autoCaller(this);
4728 AssertComRCReturnVoid(autoCaller.rc());
4729
4730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4731 CONSOLE_DO_CALLBACKS(OnAdditionsStateChange,());
4732}
4733
4734/**
4735 * @note Locks this object for reading.
4736 */
4737void Console::onAdditionsOutdated()
4738{
4739 AutoCaller autoCaller(this);
4740 AssertComRCReturnVoid(autoCaller.rc());
4741
4742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4743
4744 /** @todo Use the On-Screen Display feature to report the fact.
4745 * The user should be told to install additions that are
4746 * provided with the current VBox build:
4747 * VBOX_VERSION_MAJOR.VBOX_VERSION_MINOR.VBOX_VERSION_BUILD
4748 */
4749}
4750
4751/**
4752 * @note Locks this object for writing.
4753 */
4754void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
4755{
4756 AutoCaller autoCaller(this);
4757 AssertComRCReturnVoid(autoCaller.rc());
4758
4759 /* We need a write lock because we alter the cached callback data */
4760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4761
4762 /* save the callback arguments */
4763 mCallbackData.klc.numLock = fNumLock;
4764 mCallbackData.klc.capsLock = fCapsLock;
4765 mCallbackData.klc.scrollLock = fScrollLock;
4766 mCallbackData.klc.valid = true;
4767
4768 CONSOLE_DO_CALLBACKS(OnKeyboardLedsChange,(fNumLock, fCapsLock, fScrollLock));
4769}
4770
4771/**
4772 * @note Locks this object for reading.
4773 */
4774void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
4775 IVirtualBoxErrorInfo *aError)
4776{
4777 AutoCaller autoCaller(this);
4778 AssertComRCReturnVoid(autoCaller.rc());
4779
4780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4781 CONSOLE_DO_CALLBACKS(OnUSBDeviceStateChange,(aDevice, aAttached, aError));
4782}
4783
4784/**
4785 * @note Locks this object for reading.
4786 */
4787void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
4788{
4789 AutoCaller autoCaller(this);
4790 AssertComRCReturnVoid(autoCaller.rc());
4791
4792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4793 CONSOLE_DO_CALLBACKS(OnRuntimeError,(aFatal, aErrorID, aMessage));
4794}
4795
4796/**
4797 * @note Locks this object for reading.
4798 */
4799HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
4800{
4801 AssertReturn(aCanShow, E_POINTER);
4802 AssertReturn(aWinId, E_POINTER);
4803
4804 *aCanShow = FALSE;
4805 *aWinId = 0;
4806
4807 AutoCaller autoCaller(this);
4808 AssertComRCReturnRC(autoCaller.rc());
4809
4810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4811 CallbackList::iterator it = mCallbacks.begin();
4812 if (aCheck)
4813 {
4814 while (it != mCallbacks.end())
4815 {
4816 if (it->isWanted(ConsoleCallbackRegistration::kOnCanShowWindow))
4817 {
4818 BOOL canShow = FALSE;
4819 HRESULT hrc = it->ptrICb->OnCanShowWindow(&canShow);
4820 hrc = it->handleResult(ConsoleCallbackRegistration::kOnCanShowWindow, hrc);
4821 if (FAILED_DEAD_INTERFACE(hrc))
4822 {
4823 it = this->mCallbacks.erase(it);
4824 continue;
4825 }
4826 AssertComRC(hrc);
4827 if (FAILED(hrc) || !canShow)
4828 return hrc;
4829 }
4830 ++it;
4831 }
4832 *aCanShow = TRUE;
4833 }
4834 else
4835 {
4836 while (it != mCallbacks.end())
4837 {
4838 if (it->isWanted(ConsoleCallbackRegistration::kOnShowWindow))
4839 {
4840 ULONG64 winId = 0;
4841 HRESULT hrc = it->ptrICb->OnShowWindow(&winId);
4842 hrc = it->handleResult(ConsoleCallbackRegistration::kOnCanShowWindow, hrc);
4843 if (FAILED_DEAD_INTERFACE(hrc))
4844 {
4845 it = this->mCallbacks.erase(it);
4846 continue;
4847 }
4848 AssertComRC(hrc);
4849 if (FAILED(hrc))
4850 return hrc;
4851
4852 /* only one callback may return non-null winId */
4853 Assert(*aWinId == 0 || winId == 0);
4854 if (*aWinId == 0)
4855 *aWinId = winId;
4856 }
4857 }
4858 }
4859
4860 return S_OK;
4861}
4862
4863// private methods
4864////////////////////////////////////////////////////////////////////////////////
4865
4866/**
4867 * Increases the usage counter of the mpVM pointer. Guarantees that
4868 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
4869 * is called.
4870 *
4871 * If this method returns a failure, the caller is not allowed to use mpVM
4872 * and may return the failed result code to the upper level. This method sets
4873 * the extended error info on failure if \a aQuiet is false.
4874 *
4875 * Setting \a aQuiet to true is useful for methods that don't want to return
4876 * the failed result code to the caller when this method fails (e.g. need to
4877 * silently check for the mpVM availability).
4878 *
4879 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
4880 * returned instead of asserting. Having it false is intended as a sanity check
4881 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
4882 *
4883 * @param aQuiet true to suppress setting error info
4884 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
4885 * (otherwise this method will assert if mpVM is NULL)
4886 *
4887 * @note Locks this object for writing.
4888 */
4889HRESULT Console::addVMCaller(bool aQuiet /* = false */,
4890 bool aAllowNullVM /* = false */)
4891{
4892 AutoCaller autoCaller(this);
4893 AssertComRCReturnRC(autoCaller.rc());
4894
4895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4896
4897 if (mVMDestroying)
4898 {
4899 /* powerDown() is waiting for all callers to finish */
4900 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
4901 tr("Virtual machine is being powered down"));
4902 }
4903
4904 if (mpVM == NULL)
4905 {
4906 Assert(aAllowNullVM == true);
4907
4908 /* The machine is not powered up */
4909 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
4910 tr("Virtual machine is not powered up"));
4911 }
4912
4913 ++ mVMCallers;
4914
4915 return S_OK;
4916}
4917
4918/**
4919 * Decreases the usage counter of the mpVM pointer. Must always complete
4920 * the addVMCaller() call after the mpVM pointer is no more necessary.
4921 *
4922 * @note Locks this object for writing.
4923 */
4924void Console::releaseVMCaller()
4925{
4926 AutoCaller autoCaller(this);
4927 AssertComRCReturnVoid(autoCaller.rc());
4928
4929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4930
4931 AssertReturnVoid(mpVM != NULL);
4932
4933 Assert(mVMCallers > 0);
4934 --mVMCallers;
4935
4936 if (mVMCallers == 0 && mVMDestroying)
4937 {
4938 /* inform powerDown() there are no more callers */
4939 RTSemEventSignal(mVMZeroCallersSem);
4940 }
4941}
4942
4943/**
4944 * Initialize the release logging facility. In case something
4945 * goes wrong, there will be no release logging. Maybe in the future
4946 * we can add some logic to use different file names in this case.
4947 * Note that the logic must be in sync with Machine::DeleteSettings().
4948 */
4949HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
4950{
4951 HRESULT hrc = S_OK;
4952
4953 Bstr logFolder;
4954 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
4955 if (FAILED(hrc)) return hrc;
4956
4957 Utf8Str logDir = logFolder;
4958
4959 /* make sure the Logs folder exists */
4960 Assert(logDir.length());
4961 if (!RTDirExists(logDir.c_str()))
4962 RTDirCreateFullPath(logDir.c_str(), 0777);
4963
4964 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
4965 logDir.raw(), RTPATH_DELIMITER);
4966 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
4967 logDir.raw(), RTPATH_DELIMITER);
4968
4969 /*
4970 * Age the old log files
4971 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
4972 * Overwrite target files in case they exist.
4973 */
4974 ComPtr<IVirtualBox> virtualBox;
4975 aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
4976 ComPtr<ISystemProperties> systemProperties;
4977 virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4978 ULONG cHistoryFiles = 3;
4979 systemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
4980 if (cHistoryFiles)
4981 {
4982 for (int i = cHistoryFiles-1; i >= 0; i--)
4983 {
4984 Utf8Str *files[] = { &logFile, &pngFile };
4985 Utf8Str oldName, newName;
4986
4987 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
4988 {
4989 if (i > 0)
4990 oldName = Utf8StrFmt("%s.%d", files[j]->raw(), i);
4991 else
4992 oldName = *files[j];
4993 newName = Utf8StrFmt("%s.%d", files[j]->raw(), i + 1);
4994 /* If the old file doesn't exist, delete the new file (if it
4995 * exists) to provide correct rotation even if the sequence is
4996 * broken */
4997 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
4998 == VERR_FILE_NOT_FOUND)
4999 RTFileDelete(newName.c_str());
5000 }
5001 }
5002 }
5003
5004 PRTLOGGER loggerRelease;
5005 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
5006 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
5007#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5008 fFlags |= RTLOGFLAGS_USECRLF;
5009#endif
5010 char szError[RTPATH_MAX + 128] = "";
5011 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
5012 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
5013 RTLOGDEST_FILE, szError, sizeof(szError), logFile.raw());
5014 if (RT_SUCCESS(vrc))
5015 {
5016 /* some introductory information */
5017 RTTIMESPEC timeSpec;
5018 char szTmp[256];
5019 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
5020 RTLogRelLogger(loggerRelease, 0, ~0U,
5021 "VirtualBox %s r%u %s (%s %s) release log\n"
5022#ifdef VBOX_BLEEDING_EDGE
5023 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
5024#endif
5025 "Log opened %s\n",
5026 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
5027 __DATE__, __TIME__, szTmp);
5028
5029 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
5030 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5031 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
5032 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
5033 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5034 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
5035 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
5036 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5037 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
5038 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
5039 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5040 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
5041
5042 ComPtr<IHost> host;
5043 virtualBox->COMGETTER(Host)(host.asOutParam());
5044 ULONG cMbHostRam = 0;
5045 ULONG cMbHostRamAvail = 0;
5046 host->COMGETTER(MemorySize)(&cMbHostRam);
5047 host->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5048 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5049 cMbHostRam, cMbHostRamAvail);
5050
5051 /* the package type is interesting for Linux distributions */
5052 char szExecName[RTPATH_MAX];
5053 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
5054 RTLogRelLogger(loggerRelease, 0, ~0U,
5055 "Executable: %s\n"
5056 "Process ID: %u\n"
5057 "Package type: %s"
5058#ifdef VBOX_OSE
5059 " (OSE)"
5060#endif
5061 "\n",
5062 pszExecName ? pszExecName : "unknown",
5063 RTProcSelf(),
5064 VBOX_PACKAGE_STRING);
5065
5066 /* register this logger as the release logger */
5067 RTLogRelSetDefaultInstance(loggerRelease);
5068 hrc = S_OK;
5069
5070 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5071 RTLogFlush(loggerRelease);
5072 }
5073 else
5074 hrc = setError(E_FAIL,
5075 tr("Failed to open release log (%s, %Rrc)"),
5076 szError, vrc);
5077
5078 /* If we've made any directory changes, flush the directory to increase
5079 the likelyhood that the log file will be usable after a system panic.
5080
5081 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5082 is missing. Just don't have too high hopes for this to help. */
5083 if (SUCCEEDED(hrc) || cHistoryFiles)
5084 RTDirFlush(logDir.c_str());
5085
5086 return hrc;
5087}
5088
5089/**
5090 * Common worker for PowerUp and PowerUpPaused.
5091 *
5092 * @returns COM status code.
5093 *
5094 * @param aProgress Where to return the progress object.
5095 * @param aPaused true if PowerUpPaused called.
5096 *
5097 * @todo move down to powerDown();
5098 */
5099HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5100{
5101 if (aProgress == NULL)
5102 return E_POINTER;
5103
5104 LogFlowThisFuncEnter();
5105 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5106
5107 AutoCaller autoCaller(this);
5108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5109
5110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5111
5112 if (Global::IsOnlineOrTransient(mMachineState))
5113 return setError(VBOX_E_INVALID_VM_STATE,
5114 tr("Virtual machine is already running or busy (machine state: %s)"),
5115 Global::stringifyMachineState(mMachineState));
5116
5117 HRESULT rc = S_OK;
5118
5119 /* the network cards will undergo a quick consistency check */
5120 for (ULONG slot = 0;
5121 slot < SchemaDefs::NetworkAdapterCount;
5122 ++slot)
5123 {
5124 ComPtr<INetworkAdapter> adapter;
5125 mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
5126 BOOL enabled = FALSE;
5127 adapter->COMGETTER(Enabled)(&enabled);
5128 if (!enabled)
5129 continue;
5130
5131 NetworkAttachmentType_T netattach;
5132 adapter->COMGETTER(AttachmentType)(&netattach);
5133 switch (netattach)
5134 {
5135 case NetworkAttachmentType_Bridged:
5136 {
5137#ifdef RT_OS_WINDOWS
5138 /* a valid host interface must have been set */
5139 Bstr hostif;
5140 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
5141 if (!hostif)
5142 {
5143 return setError(VBOX_E_HOST_ERROR,
5144 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5145 }
5146 ComPtr<IVirtualBox> virtualBox;
5147 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5148 ComPtr<IHost> host;
5149 virtualBox->COMGETTER(Host)(host.asOutParam());
5150 ComPtr<IHostNetworkInterface> hostInterface;
5151 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
5152 {
5153 return setError(VBOX_E_HOST_ERROR,
5154 tr("VM cannot start because the host interface '%ls' does not exist"),
5155 hostif.raw());
5156 }
5157#endif /* RT_OS_WINDOWS */
5158 break;
5159 }
5160 default:
5161 break;
5162 }
5163 }
5164
5165 /* Read console data stored in the saved state file (if not yet done) */
5166 rc = loadDataFromSavedState();
5167 if (FAILED(rc)) return rc;
5168
5169 /* Check all types of shared folders and compose a single list */
5170 SharedFolderDataMap sharedFolders;
5171 {
5172 /* first, insert global folders */
5173 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5174 it != mGlobalSharedFolders.end(); ++ it)
5175 sharedFolders[it->first] = it->second;
5176
5177 /* second, insert machine folders */
5178 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5179 it != mMachineSharedFolders.end(); ++ it)
5180 sharedFolders[it->first] = it->second;
5181
5182 /* third, insert console folders */
5183 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5184 it != mSharedFolders.end(); ++ it)
5185 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(), it->second->isWritable());
5186 }
5187
5188 Bstr savedStateFile;
5189
5190 /*
5191 * Saved VMs will have to prove that their saved states seem kosher.
5192 */
5193 if (mMachineState == MachineState_Saved)
5194 {
5195 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5196 if (FAILED(rc)) return rc;
5197 ComAssertRet(!!savedStateFile, E_FAIL);
5198 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5199 if (RT_FAILURE(vrc))
5200 return setError(VBOX_E_FILE_ERROR,
5201 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5202 savedStateFile.raw(), vrc);
5203 }
5204
5205 /* test and clear the TeleporterEnabled property */
5206 BOOL fTeleporterEnabled;
5207 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5208 if (FAILED(rc)) return rc;
5209#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5210 if (fTeleporterEnabled)
5211 {
5212 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5213 if (FAILED(rc)) return rc;
5214 }
5215#endif
5216
5217 /* create a progress object to track progress of this operation */
5218 ComObjPtr<Progress> powerupProgress;
5219 powerupProgress.createObject();
5220 Bstr progressDesc;
5221 if (mMachineState == MachineState_Saved)
5222 progressDesc = tr("Restoring virtual machine");
5223 else if (fTeleporterEnabled)
5224 progressDesc = tr("Teleporting virtual machine");
5225 else
5226 progressDesc = tr("Starting virtual machine");
5227 rc = powerupProgress->init(static_cast<IConsole *>(this),
5228 progressDesc,
5229 fTeleporterEnabled /* aCancelable */);
5230 if (FAILED(rc)) return rc;
5231
5232 /* setup task object and thread to carry out the operation
5233 * asynchronously */
5234
5235 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
5236 ComAssertComRCRetRC(task->rc());
5237
5238 task->mSetVMErrorCallback = setVMErrorCallback;
5239 task->mConfigConstructor = configConstructor;
5240 task->mSharedFolders = sharedFolders;
5241 task->mStartPaused = aPaused;
5242 if (mMachineState == MachineState_Saved)
5243 task->mSavedStateFile = savedStateFile;
5244 task->mTeleporterEnabled = fTeleporterEnabled;
5245
5246 /* Reset differencing hard disks for which autoReset is true,
5247 * but only if the machine has no snapshots OR the current snapshot
5248 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5249 * image of an ONLINE snapshot which contains the disk state of the machine
5250 * while it was previously running, but without the corresponding machine
5251 * state, which is equivalent to powering off a running machine and not
5252 * good idea
5253 */
5254 ComPtr<ISnapshot> pCurrentSnapshot;
5255 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5256 if (FAILED(rc)) return rc;
5257
5258 BOOL fCurrentSnapshotIsOnline = false;
5259 if (pCurrentSnapshot)
5260 {
5261 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5262 if (FAILED(rc)) return rc;
5263 }
5264
5265 if (!fCurrentSnapshotIsOnline)
5266 {
5267 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5268
5269 com::SafeIfaceArray<IMediumAttachment> atts;
5270 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5271 if (FAILED(rc)) return rc;
5272
5273 for (size_t i = 0;
5274 i < atts.size();
5275 ++i)
5276 {
5277 DeviceType_T devType;
5278 rc = atts[i]->COMGETTER(Type)(&devType);
5279 /** @todo later applies to floppies as well */
5280 if (devType == DeviceType_HardDisk)
5281 {
5282 ComPtr<IMedium> medium;
5283 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
5284 if (FAILED(rc)) return rc;
5285
5286 /* needs autoreset? */
5287 BOOL autoReset = FALSE;
5288 rc = medium->COMGETTER(AutoReset)(&autoReset);
5289 if (FAILED(rc)) return rc;
5290
5291 if (autoReset)
5292 {
5293 ComPtr<IProgress> resetProgress;
5294 rc = medium->Reset(resetProgress.asOutParam());
5295 if (FAILED(rc)) return rc;
5296
5297 /* save for later use on the powerup thread */
5298 task->hardDiskProgresses.push_back(resetProgress);
5299 }
5300 }
5301 }
5302 }
5303 else
5304 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5305
5306 rc = consoleInitReleaseLog(mMachine);
5307 if (FAILED(rc)) return rc;
5308
5309 /* pass the progress object to the caller if requested */
5310 if (aProgress)
5311 {
5312 if (task->hardDiskProgresses.size() == 0)
5313 {
5314 /* there are no other operations to track, return the powerup
5315 * progress only */
5316 powerupProgress.queryInterfaceTo(aProgress);
5317 }
5318 else
5319 {
5320 /* create a combined progress object */
5321 ComObjPtr<CombinedProgress> progress;
5322 progress.createObject();
5323 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5324 progresses.push_back(ComPtr<IProgress> (powerupProgress));
5325 rc = progress->init(static_cast<IConsole *>(this),
5326 progressDesc, progresses.begin(),
5327 progresses.end());
5328 AssertComRCReturnRC(rc);
5329 progress.queryInterfaceTo(aProgress);
5330 }
5331 }
5332
5333 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5334 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5335
5336 ComAssertMsgRCRet(vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
5337 E_FAIL);
5338
5339 /* task is now owned by powerUpThread(), so release it */
5340 task.release();
5341
5342 /* finally, set the state: no right to fail in this method afterwards
5343 * since we've already started the thread and it is now responsible for
5344 * any error reporting and appropriate state change! */
5345
5346 if (mMachineState == MachineState_Saved)
5347 setMachineState(MachineState_Restoring);
5348 else if (fTeleporterEnabled)
5349 setMachineState(MachineState_TeleportingIn);
5350 else
5351 setMachineState(MachineState_Starting);
5352
5353 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5354 LogFlowThisFuncLeave();
5355 return S_OK;
5356}
5357
5358/**
5359 * Internal power off worker routine.
5360 *
5361 * This method may be called only at certain places with the following meaning
5362 * as shown below:
5363 *
5364 * - if the machine state is either Running or Paused, a normal
5365 * Console-initiated powerdown takes place (e.g. PowerDown());
5366 * - if the machine state is Saving, saveStateThread() has successfully done its
5367 * job;
5368 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5369 * to start/load the VM;
5370 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5371 * as a result of the powerDown() call).
5372 *
5373 * Calling it in situations other than the above will cause unexpected behavior.
5374 *
5375 * Note that this method should be the only one that destroys mpVM and sets it
5376 * to NULL.
5377 *
5378 * @param aProgress Progress object to run (may be NULL).
5379 *
5380 * @note Locks this object for writing.
5381 *
5382 * @note Never call this method from a thread that called addVMCaller() or
5383 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5384 * release(). Otherwise it will deadlock.
5385 */
5386HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5387{
5388 LogFlowThisFuncEnter();
5389
5390 AutoCaller autoCaller(this);
5391 AssertComRCReturnRC(autoCaller.rc());
5392
5393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5394
5395 /* Total # of steps for the progress object. Must correspond to the
5396 * number of "advance percent count" comments in this method! */
5397 enum { StepCount = 7 };
5398 /* current step */
5399 ULONG step = 0;
5400
5401 HRESULT rc = S_OK;
5402 int vrc = VINF_SUCCESS;
5403
5404 /* sanity */
5405 Assert(mVMDestroying == false);
5406
5407 Assert(mpVM != NULL);
5408
5409 AssertMsg( mMachineState == MachineState_Running
5410 || mMachineState == MachineState_Paused
5411 || mMachineState == MachineState_Stuck
5412 || mMachineState == MachineState_Starting
5413 || mMachineState == MachineState_Stopping
5414 || mMachineState == MachineState_Saving
5415 || mMachineState == MachineState_Restoring
5416 || mMachineState == MachineState_TeleportingPausedVM
5417 || mMachineState == MachineState_TeleportingIn
5418 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5419
5420 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5421 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5422
5423 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5424 * VM has already powered itself off in vmstateChangeCallback() and is just
5425 * notifying Console about that. In case of Starting or Restoring,
5426 * powerUpThread() is calling us on failure, so the VM is already off at
5427 * that point. */
5428 if ( !mVMPoweredOff
5429 && ( mMachineState == MachineState_Starting
5430 || mMachineState == MachineState_Restoring
5431 || mMachineState == MachineState_TeleportingIn)
5432 )
5433 mVMPoweredOff = true;
5434
5435 /*
5436 * Go to Stopping state if not already there.
5437 *
5438 * Note that we don't go from Saving/Restoring to Stopping because
5439 * vmstateChangeCallback() needs it to set the state to Saved on
5440 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5441 * while leaving the lock below, Saving or Restoring should be fine too.
5442 * Ditto for TeleportingPausedVM -> Teleported.
5443 */
5444 if ( mMachineState != MachineState_Saving
5445 && mMachineState != MachineState_Restoring
5446 && mMachineState != MachineState_Stopping
5447 && mMachineState != MachineState_TeleportingIn
5448 && mMachineState != MachineState_TeleportingPausedVM
5449 )
5450 setMachineState(MachineState_Stopping);
5451
5452 /* ----------------------------------------------------------------------
5453 * DONE with necessary state changes, perform the power down actions (it's
5454 * safe to leave the object lock now if needed)
5455 * ---------------------------------------------------------------------- */
5456
5457 /* Stop the VRDP server to prevent new clients connection while VM is being
5458 * powered off. */
5459 if (mConsoleVRDPServer)
5460 {
5461 LogFlowThisFunc(("Stopping VRDP server...\n"));
5462
5463 /* Leave the lock since EMT will call us back as addVMCaller()
5464 * in updateDisplayData(). */
5465 alock.leave();
5466
5467 mConsoleVRDPServer->Stop();
5468
5469 alock.enter();
5470 }
5471
5472 /* advance percent count */
5473 if (aProgress)
5474 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5475
5476
5477 /* ----------------------------------------------------------------------
5478 * Now, wait for all mpVM callers to finish their work if there are still
5479 * some on other threads. NO methods that need mpVM (or initiate other calls
5480 * that need it) may be called after this point
5481 * ---------------------------------------------------------------------- */
5482
5483 /* go to the destroying state to prevent from adding new callers */
5484 mVMDestroying = true;
5485
5486 if (mVMCallers > 0)
5487 {
5488 /* lazy creation */
5489 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5490 RTSemEventCreate(&mVMZeroCallersSem);
5491
5492 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5493 mVMCallers));
5494
5495 alock.leave();
5496
5497 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5498
5499 alock.enter();
5500 }
5501
5502 /* advance percent count */
5503 if (aProgress)
5504 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5505
5506 vrc = VINF_SUCCESS;
5507
5508 /*
5509 * Power off the VM if not already done that.
5510 * Leave the lock since EMT will call vmstateChangeCallback.
5511 *
5512 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5513 * VM-(guest-)initiated power off happened in parallel a ms before this
5514 * call. So far, we let this error pop up on the user's side.
5515 */
5516 if (!mVMPoweredOff)
5517 {
5518 LogFlowThisFunc(("Powering off the VM...\n"));
5519 alock.leave();
5520 vrc = VMR3PowerOff(mpVM);
5521 alock.enter();
5522 }
5523 else
5524 {
5525 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5526 * off. */
5527 /* reset the flag for future re-use */
5528 mVMPoweredOff = false;
5529 }
5530
5531 /* advance percent count */
5532 if (aProgress)
5533 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5534
5535#ifdef VBOX_WITH_HGCM
5536 /* Shutdown HGCM services before destroying the VM. */
5537 if (mVMMDev)
5538 {
5539 LogFlowThisFunc(("Shutdown HGCM...\n"));
5540
5541 /* Leave the lock since EMT will call us back as addVMCaller() */
5542 alock.leave();
5543
5544 mVMMDev->hgcmShutdown();
5545
5546 alock.enter();
5547 }
5548
5549 /* advance percent count */
5550 if (aProgress)
5551 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5552
5553#endif /* VBOX_WITH_HGCM */
5554
5555 LogFlowThisFunc(("Ready for VM destruction.\n"));
5556
5557 /* If we are called from Console::uninit(), then try to destroy the VM even
5558 * on failure (this will most likely fail too, but what to do?..) */
5559 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5560 {
5561 /* If the machine has an USB controller, release all USB devices
5562 * (symmetric to the code in captureUSBDevices()) */
5563 bool fHasUSBController = false;
5564 {
5565 PPDMIBASE pBase;
5566 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5567 if (RT_SUCCESS(vrc))
5568 {
5569 fHasUSBController = true;
5570 detachAllUSBDevices(false /* aDone */);
5571 }
5572 }
5573
5574 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5575 * this point). We leave the lock before calling VMR3Destroy() because
5576 * it will result into calling destructors of drivers associated with
5577 * Console children which may in turn try to lock Console (e.g. by
5578 * instantiating SafeVMPtr to access mpVM). It's safe here because
5579 * mVMDestroying is set which should prevent any activity. */
5580
5581 /* Set mpVM to NULL early just in case if some old code is not using
5582 * addVMCaller()/releaseVMCaller(). */
5583 PVM pVM = mpVM;
5584 mpVM = NULL;
5585
5586 LogFlowThisFunc(("Destroying the VM...\n"));
5587
5588 alock.leave();
5589
5590 vrc = VMR3Destroy(pVM);
5591
5592 /* take the lock again */
5593 alock.enter();
5594
5595 /* advance percent count */
5596 if (aProgress)
5597 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5598
5599 if (RT_SUCCESS(vrc))
5600 {
5601 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5602 mMachineState));
5603 /* Note: the Console-level machine state change happens on the
5604 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5605 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5606 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5607 * occurred yet. This is okay, because mMachineState is already
5608 * Stopping in this case, so any other attempt to call PowerDown()
5609 * will be rejected. */
5610 }
5611 else
5612 {
5613 /* bad bad bad, but what to do? */
5614 mpVM = pVM;
5615 rc = setError(VBOX_E_VM_ERROR,
5616 tr("Could not destroy the machine. (Error: %Rrc)"),
5617 vrc);
5618 }
5619
5620 /* Complete the detaching of the USB devices. */
5621 if (fHasUSBController)
5622 detachAllUSBDevices(true /* aDone */);
5623
5624 /* advance percent count */
5625 if (aProgress)
5626 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5627 }
5628 else
5629 {
5630 rc = setError(VBOX_E_VM_ERROR,
5631 tr("Could not power off the machine. (Error: %Rrc)"),
5632 vrc);
5633 }
5634
5635 /* Finished with destruction. Note that if something impossible happened and
5636 * we've failed to destroy the VM, mVMDestroying will remain true and
5637 * mMachineState will be something like Stopping, so most Console methods
5638 * will return an error to the caller. */
5639 if (mpVM == NULL)
5640 mVMDestroying = false;
5641
5642 if (SUCCEEDED(rc))
5643 {
5644 /* uninit dynamically allocated members of mCallbackData */
5645 if (mCallbackData.mpsc.valid)
5646 {
5647 if (mCallbackData.mpsc.shape != NULL)
5648 RTMemFree(mCallbackData.mpsc.shape);
5649 }
5650 memset(&mCallbackData, 0, sizeof(mCallbackData));
5651 }
5652
5653 /* complete the progress */
5654 if (aProgress)
5655 aProgress->notifyComplete(rc);
5656
5657 LogFlowThisFuncLeave();
5658 return rc;
5659}
5660
5661/**
5662 * @note Locks this object for writing.
5663 */
5664HRESULT Console::setMachineState(MachineState_T aMachineState,
5665 bool aUpdateServer /* = true */)
5666{
5667 AutoCaller autoCaller(this);
5668 AssertComRCReturnRC(autoCaller.rc());
5669
5670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5671
5672 HRESULT rc = S_OK;
5673
5674 if (mMachineState != aMachineState)
5675 {
5676 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5677 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5678 mMachineState = aMachineState;
5679
5680 /// @todo (dmik)
5681 // possibly, we need to redo onStateChange() using the dedicated
5682 // Event thread, like it is done in VirtualBox. This will make it
5683 // much safer (no deadlocks possible if someone tries to use the
5684 // console from the callback), however, listeners will lose the
5685 // ability to synchronously react to state changes (is it really
5686 // necessary??)
5687 LogFlowThisFunc(("Doing onStateChange()...\n"));
5688 onStateChange(aMachineState);
5689 LogFlowThisFunc(("Done onStateChange()\n"));
5690
5691 if (aUpdateServer)
5692 {
5693 /* Server notification MUST be done from under the lock; otherwise
5694 * the machine state here and on the server might go out of sync
5695 * which can lead to various unexpected results (like the machine
5696 * state being >= MachineState_Running on the server, while the
5697 * session state is already SessionState_Closed at the same time
5698 * there).
5699 *
5700 * Cross-lock conditions should be carefully watched out: calling
5701 * UpdateState we will require Machine and SessionMachine locks
5702 * (remember that here we're holding the Console lock here, and also
5703 * all locks that have been entered by the thread before calling
5704 * this method).
5705 */
5706 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5707 rc = mControl->UpdateState(aMachineState);
5708 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5709 }
5710 }
5711
5712 return rc;
5713}
5714
5715/**
5716 * Searches for a shared folder with the given logical name
5717 * in the collection of shared folders.
5718 *
5719 * @param aName logical name of the shared folder
5720 * @param aSharedFolder where to return the found object
5721 * @param aSetError whether to set the error info if the folder is
5722 * not found
5723 * @return
5724 * S_OK when found or E_INVALIDARG when not found
5725 *
5726 * @note The caller must lock this object for writing.
5727 */
5728HRESULT Console::findSharedFolder(CBSTR aName,
5729 ComObjPtr<SharedFolder> &aSharedFolder,
5730 bool aSetError /* = false */)
5731{
5732 /* sanity check */
5733 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5734
5735 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5736 if (it != mSharedFolders.end())
5737 {
5738 aSharedFolder = it->second;
5739 return S_OK;
5740 }
5741
5742 if (aSetError)
5743 setError(VBOX_E_FILE_ERROR,
5744 tr("Could not find a shared folder named '%ls'."),
5745 aName);
5746
5747 return VBOX_E_FILE_ERROR;
5748}
5749
5750/**
5751 * Fetches the list of global or machine shared folders from the server.
5752 *
5753 * @param aGlobal true to fetch global folders.
5754 *
5755 * @note The caller must lock this object for writing.
5756 */
5757HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5758{
5759 /* sanity check */
5760 AssertReturn(AutoCaller(this).state() == InInit ||
5761 isWriteLockOnCurrentThread(), E_FAIL);
5762
5763 /* protect mpVM (if not NULL) */
5764 AutoVMCallerQuietWeak autoVMCaller(this);
5765
5766 HRESULT rc = S_OK;
5767
5768 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5769
5770 if (aGlobal)
5771 {
5772 /// @todo grab & process global folders when they are done
5773 }
5774 else
5775 {
5776 SharedFolderDataMap oldFolders;
5777 if (online)
5778 oldFolders = mMachineSharedFolders;
5779
5780 mMachineSharedFolders.clear();
5781
5782 SafeIfaceArray<ISharedFolder> folders;
5783 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
5784 AssertComRCReturnRC(rc);
5785
5786 for (size_t i = 0; i < folders.size(); ++i)
5787 {
5788 ComPtr<ISharedFolder> folder = folders[i];
5789
5790 Bstr name;
5791 Bstr hostPath;
5792 BOOL writable;
5793
5794 rc = folder->COMGETTER(Name)(name.asOutParam());
5795 if (FAILED(rc)) break;
5796 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
5797 if (FAILED(rc)) break;
5798 rc = folder->COMGETTER(Writable)(&writable);
5799
5800 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable)));
5801
5802 /* send changes to HGCM if the VM is running */
5803 /// @todo report errors as runtime warnings through VMSetError
5804 if (online)
5805 {
5806 SharedFolderDataMap::iterator it = oldFolders.find(name);
5807 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5808 {
5809 /* a new machine folder is added or
5810 * the existing machine folder is changed */
5811 if (mSharedFolders.find(name) != mSharedFolders.end())
5812 ; /* the console folder exists, nothing to do */
5813 else
5814 {
5815 /* remove the old machine folder (when changed)
5816 * or the global folder if any (when new) */
5817 if (it != oldFolders.end() ||
5818 mGlobalSharedFolders.find(name) !=
5819 mGlobalSharedFolders.end())
5820 rc = removeSharedFolder(name);
5821 /* create the new machine folder */
5822 rc = createSharedFolder(name, SharedFolderData(hostPath, writable));
5823 }
5824 }
5825 /* forget the processed (or identical) folder */
5826 if (it != oldFolders.end())
5827 oldFolders.erase(it);
5828
5829 rc = S_OK;
5830 }
5831 }
5832
5833 AssertComRCReturnRC(rc);
5834
5835 /* process outdated (removed) folders */
5836 /// @todo report errors as runtime warnings through VMSetError
5837 if (online)
5838 {
5839 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5840 it != oldFolders.end(); ++ it)
5841 {
5842 if (mSharedFolders.find(it->first) != mSharedFolders.end())
5843 ; /* the console folder exists, nothing to do */
5844 else
5845 {
5846 /* remove the outdated machine folder */
5847 rc = removeSharedFolder(it->first);
5848 /* create the global folder if there is any */
5849 SharedFolderDataMap::const_iterator git =
5850 mGlobalSharedFolders.find(it->first);
5851 if (git != mGlobalSharedFolders.end())
5852 rc = createSharedFolder(git->first, git->second);
5853 }
5854 }
5855
5856 rc = S_OK;
5857 }
5858 }
5859
5860 return rc;
5861}
5862
5863/**
5864 * Searches for a shared folder with the given name in the list of machine
5865 * shared folders and then in the list of the global shared folders.
5866 *
5867 * @param aName Name of the folder to search for.
5868 * @param aIt Where to store the pointer to the found folder.
5869 * @return @c true if the folder was found and @c false otherwise.
5870 *
5871 * @note The caller must lock this object for reading.
5872 */
5873bool Console::findOtherSharedFolder(IN_BSTR aName,
5874 SharedFolderDataMap::const_iterator &aIt)
5875{
5876 /* sanity check */
5877 AssertReturn(isWriteLockOnCurrentThread(), false);
5878
5879 /* first, search machine folders */
5880 aIt = mMachineSharedFolders.find(aName);
5881 if (aIt != mMachineSharedFolders.end())
5882 return true;
5883
5884 /* second, search machine folders */
5885 aIt = mGlobalSharedFolders.find(aName);
5886 if (aIt != mGlobalSharedFolders.end())
5887 return true;
5888
5889 return false;
5890}
5891
5892/**
5893 * Calls the HGCM service to add a shared folder definition.
5894 *
5895 * @param aName Shared folder name.
5896 * @param aHostPath Shared folder path.
5897 *
5898 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5899 * @note Doesn't lock anything.
5900 */
5901HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
5902{
5903 ComAssertRet(aName && *aName, E_FAIL);
5904 ComAssertRet(aData.mHostPath, E_FAIL);
5905
5906 /* sanity checks */
5907 AssertReturn(mpVM, E_FAIL);
5908 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5909
5910 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
5911 SHFLSTRING *pFolderName, *pMapName;
5912 size_t cbString;
5913
5914 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
5915
5916 cbString = (RTUtf16Len(aData.mHostPath) + 1) * sizeof(RTUTF16);
5917 if (cbString >= UINT16_MAX)
5918 return setError(E_INVALIDARG, tr("The name is too long"));
5919 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5920 Assert(pFolderName);
5921 memcpy(pFolderName->String.ucs2, aData.mHostPath, cbString);
5922
5923 pFolderName->u16Size = (uint16_t)cbString;
5924 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5925
5926 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
5927 parms[0].u.pointer.addr = pFolderName;
5928 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5929
5930 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
5931 if (cbString >= UINT16_MAX)
5932 {
5933 RTMemFree(pFolderName);
5934 return setError(E_INVALIDARG, tr("The host path is too long"));
5935 }
5936 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5937 Assert(pMapName);
5938 memcpy(pMapName->String.ucs2, aName, cbString);
5939
5940 pMapName->u16Size = (uint16_t)cbString;
5941 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5942
5943 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
5944 parms[1].u.pointer.addr = pMapName;
5945 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5946
5947 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
5948 parms[2].u.uint32 = aData.mWritable;
5949
5950 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
5951 SHFL_FN_ADD_MAPPING,
5952 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
5953 RTMemFree(pFolderName);
5954 RTMemFree(pMapName);
5955
5956 if (RT_FAILURE(vrc))
5957 return setError(E_FAIL,
5958 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
5959 aName, aData.mHostPath.raw(), vrc);
5960
5961 return S_OK;
5962}
5963
5964/**
5965 * Calls the HGCM service to remove the shared folder definition.
5966 *
5967 * @param aName Shared folder name.
5968 *
5969 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5970 * @note Doesn't lock anything.
5971 */
5972HRESULT Console::removeSharedFolder(CBSTR aName)
5973{
5974 ComAssertRet(aName && *aName, E_FAIL);
5975
5976 /* sanity checks */
5977 AssertReturn(mpVM, E_FAIL);
5978 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5979
5980 VBOXHGCMSVCPARM parms;
5981 SHFLSTRING *pMapName;
5982 size_t cbString;
5983
5984 Log(("Removing shared folder '%ls'\n", aName));
5985
5986 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
5987 if (cbString >= UINT16_MAX)
5988 return setError(E_INVALIDARG, tr("The name is too long"));
5989 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5990 Assert(pMapName);
5991 memcpy(pMapName->String.ucs2, aName, cbString);
5992
5993 pMapName->u16Size = (uint16_t)cbString;
5994 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5995
5996 parms.type = VBOX_HGCM_SVC_PARM_PTR;
5997 parms.u.pointer.addr = pMapName;
5998 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5999
6000 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6001 SHFL_FN_REMOVE_MAPPING,
6002 1, &parms);
6003 RTMemFree(pMapName);
6004 if (RT_FAILURE(vrc))
6005 return setError(E_FAIL,
6006 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6007 aName, vrc);
6008
6009 return S_OK;
6010}
6011
6012/**
6013 * VM state callback function. Called by the VMM
6014 * using its state machine states.
6015 *
6016 * Primarily used to handle VM initiated power off, suspend and state saving,
6017 * but also for doing termination completed work (VMSTATE_TERMINATE).
6018 *
6019 * In general this function is called in the context of the EMT.
6020 *
6021 * @param aVM The VM handle.
6022 * @param aState The new state.
6023 * @param aOldState The old state.
6024 * @param aUser The user argument (pointer to the Console object).
6025 *
6026 * @note Locks the Console object for writing.
6027 */
6028DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6029 VMSTATE aState,
6030 VMSTATE aOldState,
6031 void *aUser)
6032{
6033 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6034 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6035
6036 Console *that = static_cast<Console *>(aUser);
6037 AssertReturnVoid(that);
6038
6039 AutoCaller autoCaller(that);
6040
6041 /* Note that we must let this method proceed even if Console::uninit() has
6042 * been already called. In such case this VMSTATE change is a result of:
6043 * 1) powerDown() called from uninit() itself, or
6044 * 2) VM-(guest-)initiated power off. */
6045 AssertReturnVoid( autoCaller.isOk()
6046 || autoCaller.state() == InUninit);
6047
6048 switch (aState)
6049 {
6050 /*
6051 * The VM has terminated
6052 */
6053 case VMSTATE_OFF:
6054 {
6055 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6056
6057 if (that->mVMStateChangeCallbackDisabled)
6058 break;
6059
6060 /* Do we still think that it is running? It may happen if this is a
6061 * VM-(guest-)initiated shutdown/poweroff.
6062 */
6063 if ( that->mMachineState != MachineState_Stopping
6064 && that->mMachineState != MachineState_Saving
6065 && that->mMachineState != MachineState_Restoring
6066 && that->mMachineState != MachineState_TeleportingIn
6067 && that->mMachineState != MachineState_TeleportingPausedVM
6068 && !that->mVMIsAlreadyPoweringOff
6069 )
6070 {
6071 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6072
6073 /* prevent powerDown() from calling VMR3PowerOff() again */
6074 Assert(that->mVMPoweredOff == false);
6075 that->mVMPoweredOff = true;
6076
6077 /* we are stopping now */
6078 that->setMachineState(MachineState_Stopping);
6079
6080 /* Setup task object and thread to carry out the operation
6081 * asynchronously (if we call powerDown() right here but there
6082 * is one or more mpVM callers (added with addVMCaller()) we'll
6083 * deadlock).
6084 */
6085 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6086 true /* aUsesVMPtr */));
6087
6088 /* If creating a task is falied, this can currently mean one of
6089 * two: either Console::uninit() has been called just a ms
6090 * before (so a powerDown() call is already on the way), or
6091 * powerDown() itself is being already executed. Just do
6092 * nothing.
6093 */
6094 if (!task->isOk())
6095 {
6096 LogFlowFunc(("Console is already being uninitialized.\n"));
6097 break;
6098 }
6099
6100 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6101 (void *) task.get(), 0,
6102 RTTHREADTYPE_MAIN_WORKER, 0,
6103 "VMPowerDown");
6104 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6105
6106 /* task is now owned by powerDownThread(), so release it */
6107 task.release();
6108 }
6109 break;
6110 }
6111
6112 /* The VM has been completely destroyed.
6113 *
6114 * Note: This state change can happen at two points:
6115 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6116 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6117 * called by EMT.
6118 */
6119 case VMSTATE_TERMINATED:
6120 {
6121 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6122
6123 if (that->mVMStateChangeCallbackDisabled)
6124 break;
6125
6126 /* Terminate host interface networking. If aVM is NULL, we've been
6127 * manually called from powerUpThread() either before calling
6128 * VMR3Create() or after VMR3Create() failed, so no need to touch
6129 * networking.
6130 */
6131 if (aVM)
6132 that->powerDownHostInterfaces();
6133
6134 /* From now on the machine is officially powered down or remains in
6135 * the Saved state.
6136 */
6137 switch (that->mMachineState)
6138 {
6139 default:
6140 AssertFailed();
6141 /* fall through */
6142 case MachineState_Stopping:
6143 /* successfully powered down */
6144 that->setMachineState(MachineState_PoweredOff);
6145 break;
6146 case MachineState_Saving:
6147 /* successfully saved (note that the machine is already in
6148 * the Saved state on the server due to EndSavingState()
6149 * called from saveStateThread(), so only change the local
6150 * state) */
6151 that->setMachineStateLocally(MachineState_Saved);
6152 break;
6153 case MachineState_Starting:
6154 /* failed to start, but be patient: set back to PoweredOff
6155 * (for similarity with the below) */
6156 that->setMachineState(MachineState_PoweredOff);
6157 break;
6158 case MachineState_Restoring:
6159 /* failed to load the saved state file, but be patient: set
6160 * back to Saved (to preserve the saved state file) */
6161 that->setMachineState(MachineState_Saved);
6162 break;
6163 case MachineState_TeleportingIn:
6164 /* Teleportation failed or was cancelled. Back to powered off. */
6165 that->setMachineState(MachineState_PoweredOff);
6166 break;
6167 case MachineState_TeleportingPausedVM:
6168 /* Successfully teleported the VM. */
6169 that->setMachineState(MachineState_Teleported);
6170 break;
6171 }
6172 break;
6173 }
6174
6175 case VMSTATE_SUSPENDED:
6176 {
6177 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6178
6179 if (that->mVMStateChangeCallbackDisabled)
6180 break;
6181
6182 switch (that->mMachineState)
6183 {
6184 case MachineState_Teleporting:
6185 that->setMachineState(MachineState_TeleportingPausedVM);
6186 break;
6187
6188 case MachineState_LiveSnapshotting:
6189 that->setMachineState(MachineState_Saving);
6190 break;
6191
6192 case MachineState_TeleportingPausedVM:
6193 case MachineState_Saving:
6194 case MachineState_Restoring:
6195 case MachineState_Stopping:
6196 case MachineState_TeleportingIn:
6197 /* The worker threads handles the transition. */
6198 break;
6199
6200 default:
6201 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6202 case MachineState_Running:
6203 that->setMachineState(MachineState_Paused);
6204 break;
6205 }
6206 break;
6207 }
6208
6209 case VMSTATE_SUSPENDED_LS:
6210 case VMSTATE_SUSPENDED_EXT_LS:
6211 {
6212 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6213 if (that->mVMStateChangeCallbackDisabled)
6214 break;
6215 switch (that->mMachineState)
6216 {
6217 case MachineState_Teleporting:
6218 that->setMachineState(MachineState_TeleportingPausedVM);
6219 break;
6220
6221 case MachineState_LiveSnapshotting:
6222 that->setMachineState(MachineState_Saving);
6223 break;
6224
6225 case MachineState_TeleportingPausedVM:
6226 case MachineState_Saving:
6227 /* ignore */
6228 break;
6229
6230 default:
6231 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6232 that->setMachineState(MachineState_Paused);
6233 break;
6234 }
6235 break;
6236 }
6237
6238 case VMSTATE_RUNNING:
6239 {
6240 if ( aOldState == VMSTATE_POWERING_ON
6241 || aOldState == VMSTATE_RESUMING)
6242 {
6243 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6244
6245 if (that->mVMStateChangeCallbackDisabled)
6246 break;
6247
6248 Assert( ( ( that->mMachineState == MachineState_Starting
6249 || that->mMachineState == MachineState_Paused)
6250 && aOldState == VMSTATE_POWERING_ON)
6251 || ( ( that->mMachineState == MachineState_Restoring
6252 || that->mMachineState == MachineState_TeleportingIn
6253 || that->mMachineState == MachineState_Paused
6254 || that->mMachineState == MachineState_Saving
6255 )
6256 && aOldState == VMSTATE_RESUMING));
6257
6258 that->setMachineState(MachineState_Running);
6259 }
6260
6261 break;
6262 }
6263
6264 case VMSTATE_RUNNING_LS:
6265 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6266 || that->mMachineState == MachineState_Teleporting,
6267 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6268 break;
6269
6270 case VMSTATE_FATAL_ERROR:
6271 {
6272 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6273
6274 if (that->mVMStateChangeCallbackDisabled)
6275 break;
6276
6277 /* Fatal errors are only for running VMs. */
6278 Assert(Global::IsOnline(that->mMachineState));
6279
6280 /* Note! 'Pause' is used here in want of something better. There
6281 * are currently only two places where fatal errors might be
6282 * raised, so it is not worth adding a new externally
6283 * visible state for this yet. */
6284 that->setMachineState(MachineState_Paused);
6285 break;
6286 }
6287
6288 case VMSTATE_GURU_MEDITATION:
6289 {
6290 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6291
6292 if (that->mVMStateChangeCallbackDisabled)
6293 break;
6294
6295 /* Guru are only for running VMs */
6296 Assert(Global::IsOnline(that->mMachineState));
6297
6298 that->setMachineState(MachineState_Stuck);
6299 break;
6300 }
6301
6302 default: /* shut up gcc */
6303 break;
6304 }
6305}
6306
6307#ifdef VBOX_WITH_USB
6308
6309/**
6310 * Sends a request to VMM to attach the given host device.
6311 * After this method succeeds, the attached device will appear in the
6312 * mUSBDevices collection.
6313 *
6314 * @param aHostDevice device to attach
6315 *
6316 * @note Synchronously calls EMT.
6317 * @note Must be called from under this object's lock.
6318 */
6319HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6320{
6321 AssertReturn(aHostDevice, E_FAIL);
6322 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6323
6324 /* still want a lock object because we need to leave it */
6325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6326
6327 HRESULT hrc;
6328
6329 /*
6330 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6331 * method in EMT (using usbAttachCallback()).
6332 */
6333 Bstr BstrAddress;
6334 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6335 ComAssertComRCRetRC(hrc);
6336
6337 Utf8Str Address(BstrAddress);
6338
6339 Bstr id;
6340 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6341 ComAssertComRCRetRC(hrc);
6342 Guid uuid(id);
6343
6344 BOOL fRemote = FALSE;
6345 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6346 ComAssertComRCRetRC(hrc);
6347
6348 /* protect mpVM */
6349 AutoVMCaller autoVMCaller(this);
6350 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6351
6352 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6353 Address.raw(), uuid.ptr()));
6354
6355 /* leave the lock before a VMR3* call (EMT will call us back)! */
6356 alock.leave();
6357
6358/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6359 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6360 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
6361
6362 /* restore the lock */
6363 alock.enter();
6364
6365 /* hrc is S_OK here */
6366
6367 if (RT_FAILURE(vrc))
6368 {
6369 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6370 Address.raw(), uuid.ptr(), vrc));
6371
6372 switch (vrc)
6373 {
6374 case VERR_VUSB_NO_PORTS:
6375 hrc = setError(E_FAIL,
6376 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6377 break;
6378 case VERR_VUSB_USBFS_PERMISSION:
6379 hrc = setError(E_FAIL,
6380 tr("Not permitted to open the USB device, check usbfs options"));
6381 break;
6382 default:
6383 hrc = setError(E_FAIL,
6384 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6385 vrc);
6386 break;
6387 }
6388 }
6389
6390 return hrc;
6391}
6392
6393/**
6394 * USB device attach callback used by AttachUSBDevice().
6395 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6396 * so we don't use AutoCaller and don't care about reference counters of
6397 * interface pointers passed in.
6398 *
6399 * @thread EMT
6400 * @note Locks the console object for writing.
6401 */
6402//static
6403DECLCALLBACK(int)
6404Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6405{
6406 LogFlowFuncEnter();
6407 LogFlowFunc(("that={%p}\n", that));
6408
6409 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6410
6411 void *pvRemoteBackend = NULL;
6412 if (aRemote)
6413 {
6414 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6415 Guid guid(*aUuid);
6416
6417 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6418 if (!pvRemoteBackend)
6419 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6420 }
6421
6422 USHORT portVersion = 1;
6423 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6424 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6425 Assert(portVersion == 1 || portVersion == 2);
6426
6427 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6428 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6429 if (RT_SUCCESS(vrc))
6430 {
6431 /* Create a OUSBDevice and add it to the device list */
6432 ComObjPtr<OUSBDevice> device;
6433 device.createObject();
6434 hrc = device->init(aHostDevice);
6435 AssertComRC(hrc);
6436
6437 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6438 that->mUSBDevices.push_back(device);
6439 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6440
6441 /* notify callbacks */
6442 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6443 }
6444
6445 LogFlowFunc(("vrc=%Rrc\n", vrc));
6446 LogFlowFuncLeave();
6447 return vrc;
6448}
6449
6450/**
6451 * Sends a request to VMM to detach the given host device. After this method
6452 * succeeds, the detached device will disappear from the mUSBDevices
6453 * collection.
6454 *
6455 * @param aIt Iterator pointing to the device to detach.
6456 *
6457 * @note Synchronously calls EMT.
6458 * @note Must be called from under this object's lock.
6459 */
6460HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6461{
6462 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6463
6464 /* still want a lock object because we need to leave it */
6465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6466
6467 /* protect mpVM */
6468 AutoVMCaller autoVMCaller(this);
6469 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6470
6471 /* if the device is attached, then there must at least one USB hub. */
6472 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6473
6474 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6475 (*aIt)->id().raw()));
6476
6477 /* leave the lock before a VMR3* call (EMT will call us back)! */
6478 alock.leave();
6479
6480/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6481 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6482 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6483 ComAssertRCRet(vrc, E_FAIL);
6484
6485 return S_OK;
6486}
6487
6488/**
6489 * USB device detach callback used by DetachUSBDevice().
6490 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6491 * so we don't use AutoCaller and don't care about reference counters of
6492 * interface pointers passed in.
6493 *
6494 * @thread EMT
6495 * @note Locks the console object for writing.
6496 */
6497//static
6498DECLCALLBACK(int)
6499Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6500{
6501 LogFlowFuncEnter();
6502 LogFlowFunc(("that={%p}\n", that));
6503
6504 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6505 ComObjPtr<OUSBDevice> device = **aIt;
6506
6507 /*
6508 * If that was a remote device, release the backend pointer.
6509 * The pointer was requested in usbAttachCallback.
6510 */
6511 BOOL fRemote = FALSE;
6512
6513 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6514 ComAssertComRC(hrc2);
6515
6516 if (fRemote)
6517 {
6518 Guid guid(*aUuid);
6519 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6520 }
6521
6522 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6523
6524 if (RT_SUCCESS(vrc))
6525 {
6526 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6527
6528 /* Remove the device from the collection */
6529 that->mUSBDevices.erase(*aIt);
6530 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6531
6532 /* notify callbacks */
6533 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6534 }
6535
6536 LogFlowFunc(("vrc=%Rrc\n", vrc));
6537 LogFlowFuncLeave();
6538 return vrc;
6539}
6540
6541#endif /* VBOX_WITH_USB */
6542#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6543
6544/**
6545 * Helper function to handle host interface device creation and attachment.
6546 *
6547 * @param networkAdapter the network adapter which attachment should be reset
6548 * @return COM status code
6549 *
6550 * @note The caller must lock this object for writing.
6551 *
6552 * @todo Move this back into the driver!
6553 */
6554HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6555{
6556 LogFlowThisFunc(("\n"));
6557 /* sanity check */
6558 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6559
6560# ifdef VBOX_STRICT
6561 /* paranoia */
6562 NetworkAttachmentType_T attachment;
6563 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6564 Assert(attachment == NetworkAttachmentType_Bridged);
6565# endif /* VBOX_STRICT */
6566
6567 HRESULT rc = S_OK;
6568
6569 ULONG slot = 0;
6570 rc = networkAdapter->COMGETTER(Slot)(&slot);
6571 AssertComRC(rc);
6572
6573# ifdef RT_OS_LINUX
6574 /*
6575 * Allocate a host interface device
6576 */
6577 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6578 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6579 if (RT_SUCCESS(rcVBox))
6580 {
6581 /*
6582 * Set/obtain the tap interface.
6583 */
6584 struct ifreq IfReq;
6585 memset(&IfReq, 0, sizeof(IfReq));
6586 /* The name of the TAP interface we are using */
6587 Bstr tapDeviceName;
6588 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6589 if (FAILED(rc))
6590 tapDeviceName.setNull(); /* Is this necessary? */
6591 if (tapDeviceName.isEmpty())
6592 {
6593 LogRel(("No TAP device name was supplied.\n"));
6594 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6595 }
6596
6597 if (SUCCEEDED(rc))
6598 {
6599 /* If we are using a static TAP device then try to open it. */
6600 Utf8Str str(tapDeviceName);
6601 if (str.length() <= sizeof(IfReq.ifr_name))
6602 strcpy(IfReq.ifr_name, str.raw());
6603 else
6604 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6605 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6606 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6607 if (rcVBox != 0)
6608 {
6609 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6610 rc = setError(E_FAIL,
6611 tr("Failed to open the host network interface %ls"),
6612 tapDeviceName.raw());
6613 }
6614 }
6615 if (SUCCEEDED(rc))
6616 {
6617 /*
6618 * Make it pollable.
6619 */
6620 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6621 {
6622 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6623 /*
6624 * Here is the right place to communicate the TAP file descriptor and
6625 * the host interface name to the server if/when it becomes really
6626 * necessary.
6627 */
6628 maTAPDeviceName[slot] = tapDeviceName;
6629 rcVBox = VINF_SUCCESS;
6630 }
6631 else
6632 {
6633 int iErr = errno;
6634
6635 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6636 rcVBox = VERR_HOSTIF_BLOCKING;
6637 rc = setError(E_FAIL,
6638 tr("could not set up the host networking device for non blocking access: %s"),
6639 strerror(errno));
6640 }
6641 }
6642 }
6643 else
6644 {
6645 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6646 switch (rcVBox)
6647 {
6648 case VERR_ACCESS_DENIED:
6649 /* will be handled by our caller */
6650 rc = rcVBox;
6651 break;
6652 default:
6653 rc = setError(E_FAIL,
6654 tr("Could not set up the host networking device: %Rrc"),
6655 rcVBox);
6656 break;
6657 }
6658 }
6659
6660# elif defined(RT_OS_FREEBSD)
6661 /*
6662 * Set/obtain the tap interface.
6663 */
6664 /* The name of the TAP interface we are using */
6665 Bstr tapDeviceName;
6666 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6667 if (FAILED(rc))
6668 tapDeviceName.setNull(); /* Is this necessary? */
6669 if (tapDeviceName.isEmpty())
6670 {
6671 LogRel(("No TAP device name was supplied.\n"));
6672 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6673 }
6674 char szTapdev[1024] = "/dev/";
6675 /* If we are using a static TAP device then try to open it. */
6676 Utf8Str str(tapDeviceName);
6677 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6678 strcat(szTapdev, str.raw());
6679 else
6680 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6681 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6682 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6683
6684 if (RT_SUCCESS(rcVBox))
6685 maTAPDeviceName[slot] = tapDeviceName;
6686 else
6687 {
6688 switch (rcVBox)
6689 {
6690 case VERR_ACCESS_DENIED:
6691 /* will be handled by our caller */
6692 rc = rcVBox;
6693 break;
6694 default:
6695 rc = setError(E_FAIL,
6696 tr("Failed to open the host network interface %ls"),
6697 tapDeviceName.raw());
6698 break;
6699 }
6700 }
6701# else
6702# error "huh?"
6703# endif
6704 /* in case of failure, cleanup. */
6705 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6706 {
6707 LogRel(("General failure attaching to host interface\n"));
6708 rc = setError(E_FAIL,
6709 tr("General failure attaching to host interface"));
6710 }
6711 LogFlowThisFunc(("rc=%d\n", rc));
6712 return rc;
6713}
6714
6715
6716/**
6717 * Helper function to handle detachment from a host interface
6718 *
6719 * @param networkAdapter the network adapter which attachment should be reset
6720 * @return COM status code
6721 *
6722 * @note The caller must lock this object for writing.
6723 *
6724 * @todo Move this back into the driver!
6725 */
6726HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6727{
6728 /* sanity check */
6729 LogFlowThisFunc(("\n"));
6730 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6731
6732 HRESULT rc = S_OK;
6733# ifdef VBOX_STRICT
6734 /* paranoia */
6735 NetworkAttachmentType_T attachment;
6736 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6737 Assert(attachment == NetworkAttachmentType_Bridged);
6738# endif /* VBOX_STRICT */
6739
6740 ULONG slot = 0;
6741 rc = networkAdapter->COMGETTER(Slot)(&slot);
6742 AssertComRC(rc);
6743
6744 /* is there an open TAP device? */
6745 if (maTapFD[slot] != NIL_RTFILE)
6746 {
6747 /*
6748 * Close the file handle.
6749 */
6750 Bstr tapDeviceName, tapTerminateApplication;
6751 bool isStatic = true;
6752 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6753 if (FAILED(rc) || tapDeviceName.isEmpty())
6754 {
6755 /* If the name is empty, this is a dynamic TAP device, so close it now,
6756 so that the termination script can remove the interface. Otherwise we still
6757 need the FD to pass to the termination script. */
6758 isStatic = false;
6759 int rcVBox = RTFileClose(maTapFD[slot]);
6760 AssertRC(rcVBox);
6761 maTapFD[slot] = NIL_RTFILE;
6762 }
6763 if (isStatic)
6764 {
6765 /* If we are using a static TAP device, we close it now, after having called the
6766 termination script. */
6767 int rcVBox = RTFileClose(maTapFD[slot]);
6768 AssertRC(rcVBox);
6769 }
6770 /* the TAP device name and handle are no longer valid */
6771 maTapFD[slot] = NIL_RTFILE;
6772 maTAPDeviceName[slot] = "";
6773 }
6774 LogFlowThisFunc(("returning %d\n", rc));
6775 return rc;
6776}
6777
6778#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6779
6780/**
6781 * Called at power down to terminate host interface networking.
6782 *
6783 * @note The caller must lock this object for writing.
6784 */
6785HRESULT Console::powerDownHostInterfaces()
6786{
6787 LogFlowThisFunc(("\n"));
6788
6789 /* sanity check */
6790 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6791
6792 /*
6793 * host interface termination handling
6794 */
6795 HRESULT rc;
6796 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6797 {
6798 ComPtr<INetworkAdapter> networkAdapter;
6799 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6800 if (FAILED(rc)) break;
6801
6802 BOOL enabled = FALSE;
6803 networkAdapter->COMGETTER(Enabled)(&enabled);
6804 if (!enabled)
6805 continue;
6806
6807 NetworkAttachmentType_T attachment;
6808 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6809 if (attachment == NetworkAttachmentType_Bridged)
6810 {
6811#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6812 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6813 if (FAILED(rc2) && SUCCEEDED(rc))
6814 rc = rc2;
6815#endif
6816 }
6817 }
6818
6819 return rc;
6820}
6821
6822
6823/**
6824 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
6825 * and VMR3Teleport.
6826 *
6827 * @param pVM The VM handle.
6828 * @param uPercent Completetion precentage (0-100).
6829 * @param pvUser Pointer to the VMProgressTask structure.
6830 * @return VINF_SUCCESS.
6831 */
6832/*static*/
6833DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6834{
6835 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6836 AssertReturn(task, VERR_INVALID_PARAMETER);
6837
6838 /* update the progress object */
6839 if (task->mProgress)
6840 task->mProgress->SetCurrentOperationProgress(uPercent);
6841
6842 return VINF_SUCCESS;
6843}
6844
6845/**
6846 * VM error callback function. Called by the various VM components.
6847 *
6848 * @param pVM VM handle. Can be NULL if an error occurred before
6849 * successfully creating a VM.
6850 * @param pvUser Pointer to the VMProgressTask structure.
6851 * @param rc VBox status code.
6852 * @param pszFormat Printf-like error message.
6853 * @param args Various number of arguments for the error message.
6854 *
6855 * @thread EMT, VMPowerUp...
6856 *
6857 * @note The VMProgressTask structure modified by this callback is not thread
6858 * safe.
6859 */
6860/* static */ DECLCALLBACK(void)
6861Console::setVMErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6862 const char *pszFormat, va_list args)
6863{
6864 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6865 AssertReturnVoid(task);
6866
6867 /* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */
6868 va_list va2;
6869 va_copy(va2, args); /* Have to make a copy here or GCC will break. */
6870
6871 /* append to the existing error message if any */
6872 if (task->mErrorMsg.length())
6873 task->mErrorMsg = Utf8StrFmt("%s.\n%N (%Rrc)", task->mErrorMsg.raw(),
6874 pszFormat, &va2, rc, rc);
6875 else
6876 task->mErrorMsg = Utf8StrFmt("%N (%Rrc)",
6877 pszFormat, &va2, rc, rc);
6878
6879 va_end (va2);
6880}
6881
6882/**
6883 * VM runtime error callback function.
6884 * See VMSetRuntimeError for the detailed description of parameters.
6885 *
6886 * @param pVM The VM handle.
6887 * @param pvUser The user argument.
6888 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6889 * @param pszErrorId Error ID string.
6890 * @param pszFormat Error message format string.
6891 * @param va Error message arguments.
6892 * @thread EMT.
6893 */
6894/* static */ DECLCALLBACK(void)
6895Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
6896 const char *pszErrorId,
6897 const char *pszFormat, va_list va)
6898{
6899 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6900 LogFlowFuncEnter();
6901
6902 Console *that = static_cast<Console *>(pvUser);
6903 AssertReturnVoid(that);
6904
6905 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
6906
6907 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
6908 fFatal, pszErrorId, message.raw()));
6909
6910 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
6911
6912 LogFlowFuncLeave();
6913}
6914
6915/**
6916 * Captures USB devices that match filters of the VM.
6917 * Called at VM startup.
6918 *
6919 * @param pVM The VM handle.
6920 *
6921 * @note The caller must lock this object for writing.
6922 */
6923HRESULT Console::captureUSBDevices(PVM pVM)
6924{
6925 LogFlowThisFunc(("\n"));
6926
6927 /* sanity check */
6928 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6929
6930 /* If the machine has an USB controller, ask the USB proxy service to
6931 * capture devices */
6932 PPDMIBASE pBase;
6933 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
6934 if (RT_SUCCESS(vrc))
6935 {
6936 /* leave the lock before calling Host in VBoxSVC since Host may call
6937 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6938 * produce an inter-process dead-lock otherwise. */
6939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6940 alock.leave();
6941
6942 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6943 ComAssertComRCRetRC(hrc);
6944 }
6945 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6946 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6947 vrc = VINF_SUCCESS;
6948 else
6949 AssertRC(vrc);
6950
6951 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
6952}
6953
6954
6955/**
6956 * Detach all USB device which are attached to the VM for the
6957 * purpose of clean up and such like.
6958 *
6959 * @note The caller must lock this object for writing.
6960 */
6961void Console::detachAllUSBDevices(bool aDone)
6962{
6963 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6964
6965 /* sanity check */
6966 AssertReturnVoid(isWriteLockOnCurrentThread());
6967
6968 mUSBDevices.clear();
6969
6970 /* leave the lock before calling Host in VBoxSVC since Host may call
6971 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6972 * produce an inter-process dead-lock otherwise. */
6973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6974 alock.leave();
6975
6976 mControl->DetachAllUSBDevices(aDone);
6977}
6978
6979/**
6980 * @note Locks this object for writing.
6981 */
6982void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6983{
6984 LogFlowThisFuncEnter();
6985 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
6986
6987 AutoCaller autoCaller(this);
6988 if (!autoCaller.isOk())
6989 {
6990 /* Console has been already uninitialized, deny request */
6991 AssertMsgFailed(("Console is already uninitialized\n"));
6992 LogFlowThisFunc(("Console is already uninitialized\n"));
6993 LogFlowThisFuncLeave();
6994 return;
6995 }
6996
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998
6999 /*
7000 * Mark all existing remote USB devices as dirty.
7001 */
7002 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7003 it != mRemoteUSBDevices.end();
7004 ++it)
7005 {
7006 (*it)->dirty(true);
7007 }
7008
7009 /*
7010 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7011 */
7012 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7013 VRDPUSBDEVICEDESC *e = pDevList;
7014
7015 /* The cbDevList condition must be checked first, because the function can
7016 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7017 */
7018 while (cbDevList >= 2 && e->oNext)
7019 {
7020 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7021 e->idVendor, e->idProduct,
7022 e->oProduct? (char *)e + e->oProduct: ""));
7023
7024 bool fNewDevice = true;
7025
7026 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7027 it != mRemoteUSBDevices.end();
7028 ++it)
7029 {
7030 if ((*it)->devId() == e->id
7031 && (*it)->clientId() == u32ClientId)
7032 {
7033 /* The device is already in the list. */
7034 (*it)->dirty(false);
7035 fNewDevice = false;
7036 break;
7037 }
7038 }
7039
7040 if (fNewDevice)
7041 {
7042 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7043 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7044
7045 /* Create the device object and add the new device to list. */
7046 ComObjPtr<RemoteUSBDevice> device;
7047 device.createObject();
7048 device->init(u32ClientId, e);
7049
7050 mRemoteUSBDevices.push_back(device);
7051
7052 /* Check if the device is ok for current USB filters. */
7053 BOOL fMatched = FALSE;
7054 ULONG fMaskedIfs = 0;
7055
7056 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7057
7058 AssertComRC(hrc);
7059
7060 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7061
7062 if (fMatched)
7063 {
7064 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7065
7066 /// @todo (r=dmik) warning reporting subsystem
7067
7068 if (hrc == S_OK)
7069 {
7070 LogFlowThisFunc(("Device attached\n"));
7071 device->captured(true);
7072 }
7073 }
7074 }
7075
7076 if (cbDevList < e->oNext)
7077 {
7078 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7079 cbDevList, e->oNext));
7080 break;
7081 }
7082
7083 cbDevList -= e->oNext;
7084
7085 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7086 }
7087
7088 /*
7089 * Remove dirty devices, that is those which are not reported by the server anymore.
7090 */
7091 for (;;)
7092 {
7093 ComObjPtr<RemoteUSBDevice> device;
7094
7095 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7096 while (it != mRemoteUSBDevices.end())
7097 {
7098 if ((*it)->dirty())
7099 {
7100 device = *it;
7101 break;
7102 }
7103
7104 ++ it;
7105 }
7106
7107 if (!device)
7108 {
7109 break;
7110 }
7111
7112 USHORT vendorId = 0;
7113 device->COMGETTER(VendorId)(&vendorId);
7114
7115 USHORT productId = 0;
7116 device->COMGETTER(ProductId)(&productId);
7117
7118 Bstr product;
7119 device->COMGETTER(Product)(product.asOutParam());
7120
7121 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7122 vendorId, productId, product.raw()));
7123
7124 /* Detach the device from VM. */
7125 if (device->captured())
7126 {
7127 Bstr uuid;
7128 device->COMGETTER(Id)(uuid.asOutParam());
7129 onUSBDeviceDetach(uuid, NULL);
7130 }
7131
7132 /* And remove it from the list. */
7133 mRemoteUSBDevices.erase(it);
7134 }
7135
7136 LogFlowThisFuncLeave();
7137}
7138
7139/**
7140 * Thread function which starts the VM (also from saved state) and
7141 * track progress.
7142 *
7143 * @param Thread The thread id.
7144 * @param pvUser Pointer to a VMPowerUpTask structure.
7145 * @return VINF_SUCCESS (ignored).
7146 *
7147 * @note Locks the Console object for writing.
7148 */
7149/*static*/
7150DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7151{
7152 LogFlowFuncEnter();
7153
7154 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7155 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7156
7157 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7158 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7159
7160#if defined(RT_OS_WINDOWS)
7161 {
7162 /* initialize COM */
7163 HRESULT hrc = CoInitializeEx(NULL,
7164 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7165 COINIT_SPEED_OVER_MEMORY);
7166 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7167 }
7168#endif
7169
7170 HRESULT rc = S_OK;
7171 int vrc = VINF_SUCCESS;
7172
7173 /* Set up a build identifier so that it can be seen from core dumps what
7174 * exact build was used to produce the core. */
7175 static char saBuildID[40];
7176 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7177 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7178
7179 ComObjPtr<Console> console = task->mConsole;
7180
7181 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7182
7183 /* The lock is also used as a signal from the task initiator (which
7184 * releases it only after RTThreadCreate()) that we can start the job */
7185 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7186
7187 /* sanity */
7188 Assert(console->mpVM == NULL);
7189
7190 try
7191 {
7192 /* wait for auto reset ops to complete so that we can successfully lock
7193 * the attached hard disks by calling LockMedia() below */
7194 for (VMPowerUpTask::ProgressList::const_iterator
7195 it = task->hardDiskProgresses.begin();
7196 it != task->hardDiskProgresses.end(); ++ it)
7197 {
7198 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7199 AssertComRC(rc2);
7200 }
7201
7202 /*
7203 * Lock attached media. This method will also check their accessibility.
7204 * If we're a teleporter, we'll have to postpone this action so we can
7205 * migrate between local processes.
7206 *
7207 * Note! The media will be unlocked automatically by
7208 * SessionMachine::setMachineState() when the VM is powered down.
7209 */
7210 if (!task->mTeleporterEnabled)
7211 {
7212 rc = console->mControl->LockMedia();
7213 if (FAILED(rc)) throw rc;
7214 }
7215
7216#ifdef VBOX_WITH_VRDP
7217
7218 /* Create the VRDP server. In case of headless operation, this will
7219 * also create the framebuffer, required at VM creation.
7220 */
7221 ConsoleVRDPServer *server = console->consoleVRDPServer();
7222 Assert(server);
7223
7224 /* Does VRDP server call Console from the other thread?
7225 * Not sure (and can change), so leave the lock just in case.
7226 */
7227 alock.leave();
7228 vrc = server->Launch();
7229 alock.enter();
7230
7231 if (vrc == VERR_NET_ADDRESS_IN_USE)
7232 {
7233 Utf8Str errMsg;
7234 Bstr bstr;
7235 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7236 Utf8Str ports = bstr;
7237 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7238 ports.raw());
7239 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7240 vrc, errMsg.raw()));
7241 }
7242 else if (RT_FAILURE(vrc))
7243 {
7244 Utf8Str errMsg;
7245 switch (vrc)
7246 {
7247 case VERR_FILE_NOT_FOUND:
7248 {
7249 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7250 break;
7251 }
7252 default:
7253 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7254 vrc);
7255 }
7256 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7257 vrc, errMsg.raw()));
7258 throw setError(E_FAIL, errMsg.c_str());
7259 }
7260
7261#endif /* VBOX_WITH_VRDP */
7262
7263 ComPtr<IMachine> pMachine = console->machine();
7264 ULONG cCpus = 1;
7265 pMachine->COMGETTER(CPUCount)(&cCpus);
7266
7267 /*
7268 * Create the VM
7269 */
7270 PVM pVM;
7271 /*
7272 * leave the lock since EMT will call Console. It's safe because
7273 * mMachineState is either Starting or Restoring state here.
7274 */
7275 alock.leave();
7276
7277 vrc = VMR3Create(cCpus, task->mSetVMErrorCallback, task.get(),
7278 task->mConfigConstructor, static_cast<Console *>(console),
7279 &pVM);
7280
7281 alock.enter();
7282
7283#ifdef VBOX_WITH_VRDP
7284 /* Enable client connections to the server. */
7285 console->consoleVRDPServer()->EnableConnections();
7286#endif /* VBOX_WITH_VRDP */
7287
7288 if (RT_SUCCESS(vrc))
7289 {
7290 do
7291 {
7292 /*
7293 * Register our load/save state file handlers
7294 */
7295 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7296 NULL, NULL, NULL,
7297 NULL, saveStateFileExec, NULL,
7298 NULL, loadStateFileExec, NULL,
7299 static_cast<Console *>(console));
7300 AssertRCBreak(vrc);
7301
7302 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7303 AssertRC(vrc);
7304 if (RT_FAILURE(vrc))
7305 break;
7306
7307 /*
7308 * Synchronize debugger settings
7309 */
7310 MachineDebugger *machineDebugger = console->getMachineDebugger();
7311 if (machineDebugger)
7312 {
7313 machineDebugger->flushQueuedSettings();
7314 }
7315
7316 /*
7317 * Shared Folders
7318 */
7319 if (console->getVMMDev()->isShFlActive())
7320 {
7321 /* Does the code below call Console from the other thread?
7322 * Not sure, so leave the lock just in case. */
7323 alock.leave();
7324
7325 for (SharedFolderDataMap::const_iterator
7326 it = task->mSharedFolders.begin();
7327 it != task->mSharedFolders.end();
7328 ++ it)
7329 {
7330 rc = console->createSharedFolder((*it).first, (*it).second);
7331 if (FAILED(rc)) break;
7332 }
7333 if (FAILED(rc)) break;
7334
7335 /* enter the lock again */
7336 alock.enter();
7337 }
7338
7339 /*
7340 * Capture USB devices.
7341 */
7342 rc = console->captureUSBDevices(pVM);
7343 if (FAILED(rc)) break;
7344
7345 /* leave the lock before a lengthy operation */
7346 alock.leave();
7347
7348 /* Load saved state? */
7349 if (task->mSavedStateFile.length())
7350 {
7351 LogFlowFunc(("Restoring saved state from '%s'...\n",
7352 task->mSavedStateFile.raw()));
7353
7354 vrc = VMR3LoadFromFile(pVM,
7355 task->mSavedStateFile.c_str(),
7356 Console::stateProgressCallback,
7357 static_cast<VMProgressTask*>(task.get()));
7358
7359 if (RT_SUCCESS(vrc))
7360 {
7361 if (task->mStartPaused)
7362 /* done */
7363 console->setMachineState(MachineState_Paused);
7364 else
7365 {
7366 /* Start/Resume the VM execution */
7367 vrc = VMR3Resume(pVM);
7368 AssertRC(vrc);
7369 }
7370 }
7371
7372 /* Power off in case we failed loading or resuming the VM */
7373 if (RT_FAILURE(vrc))
7374 {
7375 int vrc2 = VMR3PowerOff(pVM);
7376 AssertRC(vrc2);
7377 }
7378 }
7379 else if (task->mTeleporterEnabled)
7380 {
7381 /* -> ConsoleImplTeleporter.cpp */
7382 vrc = console->teleporterTrg(pVM, pMachine, task->mStartPaused, task->mProgress);
7383 if (RT_FAILURE(vrc) && !task->mErrorMsg.length())
7384 rc = E_FAIL; /* Avoid the "Missing error message..." assertion. */
7385 }
7386 else if (task->mStartPaused)
7387 /* done */
7388 console->setMachineState(MachineState_Paused);
7389 else
7390 {
7391 /* Power on the VM (i.e. start executing) */
7392 vrc = VMR3PowerOn(pVM);
7393 AssertRC(vrc);
7394 }
7395
7396 /* enter the lock again */
7397 alock.enter();
7398 }
7399 while (0);
7400
7401 /* On failure, destroy the VM */
7402 if (FAILED(rc) || RT_FAILURE(vrc))
7403 {
7404 /* preserve existing error info */
7405 ErrorInfoKeeper eik;
7406
7407 /* powerDown() will call VMR3Destroy() and do all necessary
7408 * cleanup (VRDP, USB devices) */
7409 HRESULT rc2 = console->powerDown();
7410 AssertComRC(rc2);
7411 }
7412 else
7413 {
7414 /*
7415 * Deregister the VMSetError callback. This is necessary as the
7416 * pfnVMAtError() function passed to VMR3Create() is supposed to
7417 * be sticky but our error callback isn't.
7418 */
7419 alock.leave();
7420 VMR3AtErrorDeregister(pVM, task->mSetVMErrorCallback, task.get());
7421 /** @todo register another VMSetError callback? */
7422 alock.enter();
7423 }
7424 }
7425 else
7426 {
7427 /*
7428 * If VMR3Create() failed it has released the VM memory.
7429 */
7430 console->mpVM = NULL;
7431 }
7432
7433 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7434 {
7435 /* If VMR3Create() or one of the other calls in this function fail,
7436 * an appropriate error message has been set in task->mErrorMsg.
7437 * However since that happens via a callback, the rc status code in
7438 * this function is not updated.
7439 */
7440 if (!task->mErrorMsg.length())
7441 {
7442 /* If the error message is not set but we've got a failure,
7443 * convert the VBox status code into a meaningful error message.
7444 * This becomes unused once all the sources of errors set the
7445 * appropriate error message themselves.
7446 */
7447 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7448 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7449 vrc);
7450 }
7451
7452 /* Set the error message as the COM error.
7453 * Progress::notifyComplete() will pick it up later. */
7454 throw setError(E_FAIL, task->mErrorMsg.c_str());
7455 }
7456 }
7457 catch (HRESULT aRC) { rc = aRC; }
7458
7459 if ( console->mMachineState == MachineState_Starting
7460 || console->mMachineState == MachineState_Restoring
7461 || console->mMachineState == MachineState_TeleportingIn
7462 )
7463 {
7464 /* We are still in the Starting/Restoring state. This means one of:
7465 *
7466 * 1) we failed before VMR3Create() was called;
7467 * 2) VMR3Create() failed.
7468 *
7469 * In both cases, there is no need to call powerDown(), but we still
7470 * need to go back to the PoweredOff/Saved state. Reuse
7471 * vmstateChangeCallback() for that purpose.
7472 */
7473
7474 /* preserve existing error info */
7475 ErrorInfoKeeper eik;
7476
7477 Assert(console->mpVM == NULL);
7478 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7479 console);
7480 }
7481
7482 /*
7483 * Evaluate the final result. Note that the appropriate mMachineState value
7484 * is already set by vmstateChangeCallback() in all cases.
7485 */
7486
7487 /* leave the lock, don't need it any more */
7488 alock.leave();
7489
7490 if (SUCCEEDED(rc))
7491 {
7492 /* Notify the progress object of the success */
7493 task->mProgress->notifyComplete(S_OK);
7494 console->mControl->SetPowerUpInfo(NULL);
7495 }
7496 else
7497 {
7498 /* The progress object will fetch the current error info */
7499 task->mProgress->notifyComplete(rc);
7500 ProgressErrorInfo info(task->mProgress);
7501 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
7502 rc = errorInfo.createObject();
7503 if (SUCCEEDED(rc))
7504 {
7505 errorInfo->init(info.getResultCode(),
7506 info.getInterfaceID(),
7507 info.getComponent(),
7508 info.getText());
7509 console->mControl->SetPowerUpInfo(errorInfo);
7510 }
7511 else
7512 {
7513 /* If it's not possible to create an IVirtualBoxErrorInfo object
7514 * signal success, as not signalling anything will cause a stuck
7515 * progress object in VBoxSVC. */
7516 console->mControl->SetPowerUpInfo(NULL);
7517 }
7518
7519 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7520 }
7521
7522#if defined(RT_OS_WINDOWS)
7523 /* uninitialize COM */
7524 CoUninitialize();
7525#endif
7526
7527 LogFlowFuncLeave();
7528
7529 return VINF_SUCCESS;
7530}
7531
7532
7533/**
7534 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7535 *
7536 * @param pVM The VM handle.
7537 * @param lInstance The instance of the controller.
7538 * @param pcszDevice The name of the controller type.
7539 * @param enmBus The storage bus type of the controller.
7540 * @param fSetupMerge Whether to set up a medium merge
7541 * @param uMergeSource Merge source image index
7542 * @param uMergeTarget Merge target image index
7543 * @param aMediumAtt The medium attachment.
7544 * @param aMachineState The current machine state.
7545 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7546 * @return VBox status code.
7547 */
7548/* static */
7549DECLCALLBACK(int) Console::reconfigureMediumAttachment(PVM pVM,
7550 const char *pcszDevice,
7551 unsigned uInstance,
7552 StorageBus_T enmBus,
7553 IoBackendType_T enmIoBackend,
7554 bool fSetupMerge,
7555 unsigned uMergeSource,
7556 unsigned uMergeTarget,
7557 IMediumAttachment *aMediumAtt,
7558 MachineState_T aMachineState,
7559 HRESULT *phrc)
7560{
7561 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7562
7563 int rc;
7564 HRESULT hrc;
7565 Bstr bstr;
7566 *phrc = S_OK;
7567#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7568#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7569
7570 /* Ignore attachments other than hard disks, since at the moment they are
7571 * not subject to snapshotting in general. */
7572 DeviceType_T lType;
7573 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7574 if (lType != DeviceType_HardDisk)
7575 return VINF_SUCCESS;
7576
7577 /* Determine the base path for the device instance. */
7578 PCFGMNODE pCtlInst;
7579 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7580 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7581
7582 /* Update the device instance configuration. */
7583 rc = Console::configMediumAttachment(pCtlInst, pcszDevice, uInstance,
7584 enmBus, enmIoBackend,
7585 fSetupMerge, uMergeSource,
7586 uMergeTarget, aMediumAtt,
7587 aMachineState, phrc,
7588 true /* fAttachDetach */,
7589 false /* fForceUnmount */, pVM,
7590 NULL /* paLedDevType */);
7591 /** @todo this dumps everything attached to this device instance, which
7592 * is more than necessary. Dumping the changed LUN would be enough. */
7593 CFGMR3Dump(pCtlInst);
7594 RC_CHECK();
7595
7596#undef RC_CHECK
7597#undef H
7598
7599 LogFlowFunc(("Returns success\n"));
7600 return VINF_SUCCESS;
7601}
7602
7603/**
7604 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7605 */
7606static void takesnapshotProgressCancelCallback(void *pvUser)
7607{
7608 PVM pVM = (PVM)pvUser;
7609 SSMR3Cancel(pVM);
7610}
7611
7612/**
7613 * Worker thread created by Console::TakeSnapshot.
7614 * @param Thread The current thread (ignored).
7615 * @param pvUser The task.
7616 * @return VINF_SUCCESS (ignored).
7617 */
7618/*static*/
7619DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7620{
7621 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7622
7623 // taking a snapshot consists of the following:
7624
7625 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7626 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7627 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7628 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7629 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7630
7631 Console *that = pTask->mConsole;
7632 bool fBeganTakingSnapshot = false;
7633 bool fSuspenededBySave = false;
7634
7635 AutoCaller autoCaller(that);
7636 if (FAILED(autoCaller.rc()))
7637 {
7638 that->mptrCancelableProgress.setNull();
7639 return autoCaller.rc();
7640 }
7641
7642 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7643
7644 HRESULT rc = S_OK;
7645
7646 try
7647 {
7648 /* STEP 1 + 2:
7649 * request creating the diff images on the server and create the snapshot object
7650 * (this will set the machine state to Saving on the server to block
7651 * others from accessing this machine)
7652 */
7653 rc = that->mControl->BeginTakingSnapshot(that,
7654 pTask->bstrName,
7655 pTask->bstrDescription,
7656 pTask->mProgress,
7657 pTask->fTakingSnapshotOnline,
7658 pTask->bstrSavedStateFile.asOutParam());
7659 if (FAILED(rc))
7660 throw rc;
7661
7662 fBeganTakingSnapshot = true;
7663
7664 /*
7665 * state file is non-null only when the VM is paused
7666 * (i.e. creating a snapshot online)
7667 */
7668 ComAssertThrow( (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7669 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline),
7670 rc = E_FAIL);
7671
7672 /* sync the state with the server */
7673 if (pTask->lastMachineState == MachineState_Running)
7674 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7675 else
7676 that->setMachineStateLocally(MachineState_Saving);
7677
7678 // STEP 3: save the VM state (if online)
7679 if (pTask->fTakingSnapshotOnline)
7680 {
7681 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7682
7683 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7684 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7685 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7686
7687 alock.leave();
7688 LogFlowFunc(("VMR3Save...\n"));
7689 int vrc = VMR3Save(that->mpVM,
7690 strSavedStateFile.c_str(),
7691 true /*fContinueAfterwards*/,
7692 Console::stateProgressCallback,
7693 (void*)pTask,
7694 &fSuspenededBySave);
7695 alock.enter();
7696 if (RT_FAILURE(vrc))
7697 throw setError(E_FAIL,
7698 tr("Failed to save the machine state to '%s' (%Rrc)"),
7699 strSavedStateFile.c_str(), vrc);
7700
7701 pTask->mProgress->setCancelCallback(NULL, NULL);
7702 if (!pTask->mProgress->notifyPointOfNoReturn())
7703 throw setError(E_FAIL, tr("Cancelled"));
7704 that->mptrCancelableProgress.setNull();
7705
7706 // STEP 4: reattach hard disks
7707 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7708
7709 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7710 1); // operation weight, same as computed when setting up progress object
7711
7712 com::SafeIfaceArray<IMediumAttachment> atts;
7713 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7714 if (FAILED(rc))
7715 throw rc;
7716
7717 for (size_t i = 0;
7718 i < atts.size();
7719 ++i)
7720 {
7721 ComPtr<IStorageController> controller;
7722 BSTR controllerName;
7723 ULONG lInstance;
7724 StorageControllerType_T enmController;
7725 StorageBus_T enmBus;
7726 IoBackendType_T enmIoBackend;
7727
7728 /*
7729 * We can't pass a storage controller object directly
7730 * (g++ complains about not being able to pass non POD types through '...')
7731 * so we have to query needed values here and pass them.
7732 */
7733 rc = atts[i]->COMGETTER(Controller)(&controllerName);
7734 if (FAILED(rc))
7735 throw rc;
7736
7737 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7738 if (FAILED(rc))
7739 throw rc;
7740
7741 rc = controller->COMGETTER(ControllerType)(&enmController);
7742 if (FAILED(rc))
7743 throw rc;
7744 rc = controller->COMGETTER(Instance)(&lInstance);
7745 if (FAILED(rc))
7746 throw rc;
7747 rc = controller->COMGETTER(Bus)(&enmBus);
7748 if (FAILED(rc))
7749 throw rc;
7750 rc = controller->COMGETTER(IoBackend)(&enmIoBackend);
7751 if (FAILED(rc))
7752 throw rc;
7753
7754 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7755
7756 /*
7757 * don't leave the lock since reconfigureMediumAttachment
7758 * isn't going to need the Console lock.
7759 */
7760 vrc = VMR3ReqCallWait(that->mpVM,
7761 VMCPUID_ANY,
7762 (PFNRT)reconfigureMediumAttachment,
7763 11,
7764 that->mpVM,
7765 pcszDevice,
7766 lInstance,
7767 enmBus,
7768 enmIoBackend,
7769 false /* fSetupMerge */,
7770 0 /* uMergeSource */,
7771 0 /* uMergeTarget */,
7772 atts[i],
7773 that->mMachineState,
7774 &rc);
7775 if (RT_FAILURE(vrc))
7776 throw setError(E_FAIL, Console::tr("%Rrc"), vrc);
7777 if (FAILED(rc))
7778 throw rc;
7779 }
7780 }
7781
7782 /*
7783 * finalize the requested snapshot object.
7784 * This will reset the machine state to the state it had right
7785 * before calling mControl->BeginTakingSnapshot().
7786 */
7787 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
7788 // do not throw rc here because we can't call EndTakingSnapshot() twice
7789 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7790 }
7791 catch (HRESULT rcThrown)
7792 {
7793 /* preserve existing error info */
7794 ErrorInfoKeeper eik;
7795
7796 if (fBeganTakingSnapshot)
7797 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
7798
7799 rc = rcThrown;
7800 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7801 }
7802 Assert(alock.isWriteLockOnCurrentThread());
7803
7804 if (FAILED(rc)) /* Must come before calling setMachineState. */
7805 pTask->mProgress->notifyComplete(rc);
7806
7807 /*
7808 * Fix up the machine state.
7809 *
7810 * For live snapshots we do all the work, for the two other variantions we
7811 * just update the local copy.
7812 */
7813 MachineState_T enmMachineState;
7814 that->mMachine->COMGETTER(State)(&enmMachineState);
7815 if ( that->mMachineState == MachineState_LiveSnapshotting
7816 || that->mMachineState == MachineState_Saving)
7817 {
7818
7819 if (!pTask->fTakingSnapshotOnline)
7820 that->setMachineStateLocally(pTask->lastMachineState);
7821 else if (SUCCEEDED(rc))
7822 {
7823 Assert( pTask->lastMachineState == MachineState_Running
7824 || pTask->lastMachineState == MachineState_Paused);
7825 Assert(that->mMachineState == MachineState_Saving);
7826 if (pTask->lastMachineState == MachineState_Running)
7827 {
7828 LogFlowFunc(("VMR3Resume...\n"));
7829 alock.leave();
7830 int vrc = VMR3Resume(that->mpVM);
7831 alock.enter();
7832 if (RT_FAILURE(vrc))
7833 {
7834 rc = setError(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
7835 pTask->mProgress->notifyComplete(rc);
7836 if (that->mMachineState == MachineState_Saving)
7837 that->setMachineStateLocally(MachineState_Paused);
7838 }
7839 }
7840 else
7841 that->setMachineStateLocally(MachineState_Paused);
7842 }
7843 else
7844 {
7845 /** @todo this could probably be made more generic and reused elsewhere. */
7846 /* paranoid cleanup on for a failed online snapshot. */
7847 VMSTATE enmVMState = VMR3GetState(that->mpVM);
7848 switch (enmVMState)
7849 {
7850 case VMSTATE_RUNNING:
7851 case VMSTATE_RUNNING_LS:
7852 case VMSTATE_DEBUGGING:
7853 case VMSTATE_DEBUGGING_LS:
7854 case VMSTATE_POWERING_OFF:
7855 case VMSTATE_POWERING_OFF_LS:
7856 case VMSTATE_RESETTING:
7857 case VMSTATE_RESETTING_LS:
7858 Assert(!fSuspenededBySave);
7859 that->setMachineState(MachineState_Running);
7860 break;
7861
7862 case VMSTATE_GURU_MEDITATION:
7863 case VMSTATE_GURU_MEDITATION_LS:
7864 that->setMachineState(MachineState_Stuck);
7865 break;
7866
7867 case VMSTATE_FATAL_ERROR:
7868 case VMSTATE_FATAL_ERROR_LS:
7869 if (pTask->lastMachineState == MachineState_Paused)
7870 that->setMachineStateLocally(pTask->lastMachineState);
7871 else
7872 that->setMachineState(MachineState_Paused);
7873 break;
7874
7875 default:
7876 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
7877 case VMSTATE_SUSPENDED:
7878 case VMSTATE_SUSPENDED_LS:
7879 case VMSTATE_SUSPENDING:
7880 case VMSTATE_SUSPENDING_LS:
7881 case VMSTATE_SUSPENDING_EXT_LS:
7882 if (fSuspenededBySave)
7883 {
7884 Assert(pTask->lastMachineState == MachineState_Running);
7885 LogFlowFunc(("VMR3Resume (on failure)...\n"));
7886 alock.leave();
7887 int vrc = VMR3Resume(that->mpVM);
7888 alock.enter();
7889 AssertLogRelRC(vrc);
7890 if (RT_FAILURE(vrc))
7891 that->setMachineState(MachineState_Paused);
7892 }
7893 else if (pTask->lastMachineState == MachineState_Paused)
7894 that->setMachineStateLocally(pTask->lastMachineState);
7895 else
7896 that->setMachineState(MachineState_Paused);
7897 break;
7898 }
7899
7900 }
7901 }
7902 /*else: somebody else has change the state... Leave it. */
7903
7904 /* check the remote state to see that we got it right. */
7905 that->mMachine->COMGETTER(State)(&enmMachineState);
7906 AssertLogRelMsg(that->mMachineState == enmMachineState,
7907 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
7908 Global::stringifyMachineState(enmMachineState) ));
7909
7910
7911 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
7912 pTask->mProgress->notifyComplete(rc);
7913
7914 delete pTask;
7915
7916 LogFlowFuncLeave();
7917 return VINF_SUCCESS;
7918}
7919
7920/**
7921 * Thread for executing the saved state operation.
7922 *
7923 * @param Thread The thread handle.
7924 * @param pvUser Pointer to a VMSaveTask structure.
7925 * @return VINF_SUCCESS (ignored).
7926 *
7927 * @note Locks the Console object for writing.
7928 */
7929/*static*/
7930DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
7931{
7932 LogFlowFuncEnter();
7933
7934 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
7935 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7936
7937 Assert(task->mSavedStateFile.length());
7938 Assert(!task->mProgress.isNull());
7939
7940 const ComObjPtr<Console> &that = task->mConsole;
7941 Utf8Str errMsg;
7942 HRESULT rc = S_OK;
7943
7944 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7945
7946 bool fSuspenededBySave;
7947 int vrc = VMR3Save(that->mpVM,
7948 task->mSavedStateFile.c_str(),
7949 false, /*fContinueAfterwards*/
7950 Console::stateProgressCallback,
7951 static_cast<VMProgressTask*>(task.get()),
7952 &fSuspenededBySave);
7953 if (RT_FAILURE(vrc))
7954 {
7955 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
7956 task->mSavedStateFile.raw(), vrc);
7957 rc = E_FAIL;
7958 }
7959 Assert(!fSuspenededBySave);
7960
7961 /* lock the console once we're going to access it */
7962 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
7963
7964 /*
7965 * finalize the requested save state procedure.
7966 * In case of success, the server will set the machine state to Saved;
7967 * in case of failure it will reset the it to the state it had right
7968 * before calling mControl->BeginSavingState().
7969 */
7970 that->mControl->EndSavingState(SUCCEEDED(rc));
7971
7972 /* synchronize the state with the server */
7973 if (!FAILED(rc))
7974 {
7975 /*
7976 * The machine has been successfully saved, so power it down
7977 * (vmstateChangeCallback() will set state to Saved on success).
7978 * Note: we release the task's VM caller, otherwise it will
7979 * deadlock.
7980 */
7981 task->releaseVMCaller();
7982
7983 rc = that->powerDown();
7984 }
7985
7986 /* notify the progress object about operation completion */
7987 if (SUCCEEDED(rc))
7988 task->mProgress->notifyComplete(S_OK);
7989 else
7990 {
7991 if (errMsg.length())
7992 task->mProgress->notifyComplete(rc,
7993 COM_IIDOF(IConsole),
7994 (CBSTR)Console::getComponentName(),
7995 errMsg.c_str());
7996 else
7997 task->mProgress->notifyComplete(rc);
7998 }
7999
8000 LogFlowFuncLeave();
8001 return VINF_SUCCESS;
8002}
8003
8004/**
8005 * Thread for powering down the Console.
8006 *
8007 * @param Thread The thread handle.
8008 * @param pvUser Pointer to the VMTask structure.
8009 * @return VINF_SUCCESS (ignored).
8010 *
8011 * @note Locks the Console object for writing.
8012 */
8013/*static*/
8014DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8015{
8016 LogFlowFuncEnter();
8017
8018 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8019 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8020
8021 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8022
8023 const ComObjPtr<Console> &that = task->mConsole;
8024
8025 /* Note: no need to use addCaller() to protect Console because VMTask does
8026 * that */
8027
8028 /* wait until the method tat started us returns */
8029 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8030
8031 /* release VM caller to avoid the powerDown() deadlock */
8032 task->releaseVMCaller();
8033
8034 that->powerDown(task->mProgress);
8035
8036 LogFlowFuncLeave();
8037 return VINF_SUCCESS;
8038}
8039
8040/**
8041 * The Main status driver instance data.
8042 */
8043typedef struct DRVMAINSTATUS
8044{
8045 /** The LED connectors. */
8046 PDMILEDCONNECTORS ILedConnectors;
8047 /** Pointer to the LED ports interface above us. */
8048 PPDMILEDPORTS pLedPorts;
8049 /** Pointer to the array of LED pointers. */
8050 PPDMLED *papLeds;
8051 /** The unit number corresponding to the first entry in the LED array. */
8052 RTUINT iFirstLUN;
8053 /** The unit number corresponding to the last entry in the LED array.
8054 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8055 RTUINT iLastLUN;
8056} DRVMAINSTATUS, *PDRVMAINSTATUS;
8057
8058
8059/**
8060 * Notification about a unit which have been changed.
8061 *
8062 * The driver must discard any pointers to data owned by
8063 * the unit and requery it.
8064 *
8065 * @param pInterface Pointer to the interface structure containing the called function pointer.
8066 * @param iLUN The unit number.
8067 */
8068DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8069{
8070 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8071 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8072 {
8073 PPDMLED pLed;
8074 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8075 if (RT_FAILURE(rc))
8076 pLed = NULL;
8077 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8078 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8079 }
8080}
8081
8082
8083/**
8084 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8085 */
8086DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8087{
8088 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8089 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8090 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8091 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8092 return NULL;
8093}
8094
8095
8096/**
8097 * Destruct a status driver instance.
8098 *
8099 * @returns VBox status.
8100 * @param pDrvIns The driver instance data.
8101 */
8102DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8103{
8104 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8105 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8106 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8107
8108 if (pData->papLeds)
8109 {
8110 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8111 while (iLed-- > 0)
8112 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL);
8113 }
8114}
8115
8116
8117/**
8118 * Construct a status driver instance.
8119 *
8120 * @copydoc FNPDMDRVCONSTRUCT
8121 */
8122DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8123{
8124 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8125 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8126 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8127
8128 /*
8129 * Validate configuration.
8130 */
8131 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8132 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8133 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8134 ("Configuration error: Not possible to attach anything to this driver!\n"),
8135 VERR_PDM_DRVINS_NO_ATTACH);
8136
8137 /*
8138 * Data.
8139 */
8140 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8141 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8142
8143 /*
8144 * Read config.
8145 */
8146 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8147 if (RT_FAILURE(rc))
8148 {
8149 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8150 return rc;
8151 }
8152
8153 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8154 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8155 pData->iFirstLUN = 0;
8156 else if (RT_FAILURE(rc))
8157 {
8158 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8159 return rc;
8160 }
8161
8162 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8163 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8164 pData->iLastLUN = 0;
8165 else if (RT_FAILURE(rc))
8166 {
8167 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8168 return rc;
8169 }
8170 if (pData->iFirstLUN > pData->iLastLUN)
8171 {
8172 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8173 return VERR_GENERAL_FAILURE;
8174 }
8175
8176 /*
8177 * Get the ILedPorts interface of the above driver/device and
8178 * query the LEDs we want.
8179 */
8180 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8181 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8182 VERR_PDM_MISSING_INTERFACE_ABOVE);
8183
8184 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8185 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8186
8187 return VINF_SUCCESS;
8188}
8189
8190
8191/**
8192 * Keyboard driver registration record.
8193 */
8194const PDMDRVREG Console::DrvStatusReg =
8195{
8196 /* u32Version */
8197 PDM_DRVREG_VERSION,
8198 /* szName */
8199 "MainStatus",
8200 /* szRCMod */
8201 "",
8202 /* szR0Mod */
8203 "",
8204 /* pszDescription */
8205 "Main status driver (Main as in the API).",
8206 /* fFlags */
8207 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8208 /* fClass. */
8209 PDM_DRVREG_CLASS_STATUS,
8210 /* cMaxInstances */
8211 ~0,
8212 /* cbInstance */
8213 sizeof(DRVMAINSTATUS),
8214 /* pfnConstruct */
8215 Console::drvStatus_Construct,
8216 /* pfnDestruct */
8217 Console::drvStatus_Destruct,
8218 /* pfnRelocate */
8219 NULL,
8220 /* pfnIOCtl */
8221 NULL,
8222 /* pfnPowerOn */
8223 NULL,
8224 /* pfnReset */
8225 NULL,
8226 /* pfnSuspend */
8227 NULL,
8228 /* pfnResume */
8229 NULL,
8230 /* pfnAttach */
8231 NULL,
8232 /* pfnDetach */
8233 NULL,
8234 /* pfnPowerOff */
8235 NULL,
8236 /* pfnSoftReset */
8237 NULL,
8238 /* u32EndVersion */
8239 PDM_DRVREG_VERSION
8240};
8241
8242/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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