VirtualBox

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

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

Main: add DMI product name and DMI product version to the release log (helpful debugging information)

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