VirtualBox

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

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

Main/Console: fix regression introduced by making inaccessible removable medium attachments non-lethal

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

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