VirtualBox

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

Last change on this file since 29960 was 29957, checked in by vboxsync, 14 years ago

Console*: Fixed power off on failure issue again (should be correct now). Fixed missing socket retaining on the source side that would occasionally cause teleporterTcpOpIsOk to fail. Attempt at fixing the error propagation on the target side.

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

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