VirtualBox

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

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

Added 'restart' parameter to OnVRDPServerChange notification.

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

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