VirtualBox

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

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

Main: cosmetical change in error messages

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 266.5 KB
Line 
1/* $Id: ConsoleImpl.cpp 29971 2010-06-02 08:51:13Z 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
5045 ComPtr<IHost> host;
5046 virtualBox->COMGETTER(Host)(host.asOutParam());
5047 ULONG cMbHostRam = 0;
5048 ULONG cMbHostRamAvail = 0;
5049 host->COMGETTER(MemorySize)(&cMbHostRam);
5050 host->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5051 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5052 cMbHostRam, cMbHostRamAvail);
5053
5054 /* the package type is interesting for Linux distributions */
5055 char szExecName[RTPATH_MAX];
5056 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
5057 RTLogRelLogger(loggerRelease, 0, ~0U,
5058 "Executable: %s\n"
5059 "Process ID: %u\n"
5060 "Package type: %s"
5061#ifdef VBOX_OSE
5062 " (OSE)"
5063#endif
5064 "\n",
5065 pszExecName ? pszExecName : "unknown",
5066 RTProcSelf(),
5067 VBOX_PACKAGE_STRING);
5068
5069 /* register this logger as the release logger */
5070 RTLogRelSetDefaultInstance(loggerRelease);
5071 hrc = S_OK;
5072
5073 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5074 RTLogFlush(loggerRelease);
5075 }
5076 else
5077 hrc = setError(E_FAIL,
5078 tr("Failed to open release log (%s, %Rrc)"),
5079 szError, vrc);
5080
5081 /* If we've made any directory changes, flush the directory to increase
5082 the likelyhood that the log file will be usable after a system panic.
5083
5084 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5085 is missing. Just don't have too high hopes for this to help. */
5086 if (SUCCEEDED(hrc) || cHistoryFiles)
5087 RTDirFlush(logDir.c_str());
5088
5089 return hrc;
5090}
5091
5092/**
5093 * Common worker for PowerUp and PowerUpPaused.
5094 *
5095 * @returns COM status code.
5096 *
5097 * @param aProgress Where to return the progress object.
5098 * @param aPaused true if PowerUpPaused called.
5099 *
5100 * @todo move down to powerDown();
5101 */
5102HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5103{
5104 if (aProgress == NULL)
5105 return E_POINTER;
5106
5107 LogFlowThisFuncEnter();
5108 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5109
5110 AutoCaller autoCaller(this);
5111 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5112
5113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5114
5115 if (Global::IsOnlineOrTransient(mMachineState))
5116 return setError(VBOX_E_INVALID_VM_STATE,
5117 tr("The virtual machine is already running or busy (machine state: %s)"),
5118 Global::stringifyMachineState(mMachineState));
5119
5120 HRESULT rc = S_OK;
5121
5122 /* the network cards will undergo a quick consistency check */
5123 for (ULONG slot = 0;
5124 slot < SchemaDefs::NetworkAdapterCount;
5125 ++slot)
5126 {
5127 ComPtr<INetworkAdapter> adapter;
5128 mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
5129 BOOL enabled = FALSE;
5130 adapter->COMGETTER(Enabled)(&enabled);
5131 if (!enabled)
5132 continue;
5133
5134 NetworkAttachmentType_T netattach;
5135 adapter->COMGETTER(AttachmentType)(&netattach);
5136 switch (netattach)
5137 {
5138 case NetworkAttachmentType_Bridged:
5139 {
5140#ifdef RT_OS_WINDOWS
5141 /* a valid host interface must have been set */
5142 Bstr hostif;
5143 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
5144 if (!hostif)
5145 {
5146 return setError(VBOX_E_HOST_ERROR,
5147 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5148 }
5149 ComPtr<IVirtualBox> virtualBox;
5150 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5151 ComPtr<IHost> host;
5152 virtualBox->COMGETTER(Host)(host.asOutParam());
5153 ComPtr<IHostNetworkInterface> hostInterface;
5154 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
5155 {
5156 return setError(VBOX_E_HOST_ERROR,
5157 tr("VM cannot start because the host interface '%ls' does not exist"),
5158 hostif.raw());
5159 }
5160#endif /* RT_OS_WINDOWS */
5161 break;
5162 }
5163 default:
5164 break;
5165 }
5166 }
5167
5168 /* Read console data stored in the saved state file (if not yet done) */
5169 rc = loadDataFromSavedState();
5170 if (FAILED(rc)) return rc;
5171
5172 /* Check all types of shared folders and compose a single list */
5173 SharedFolderDataMap sharedFolders;
5174 {
5175 /* first, insert global folders */
5176 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5177 it != mGlobalSharedFolders.end(); ++ it)
5178 sharedFolders[it->first] = it->second;
5179
5180 /* second, insert machine folders */
5181 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5182 it != mMachineSharedFolders.end(); ++ it)
5183 sharedFolders[it->first] = it->second;
5184
5185 /* third, insert console folders */
5186 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5187 it != mSharedFolders.end(); ++ it)
5188 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(), it->second->isWritable());
5189 }
5190
5191 Bstr savedStateFile;
5192
5193 /*
5194 * Saved VMs will have to prove that their saved states seem kosher.
5195 */
5196 if (mMachineState == MachineState_Saved)
5197 {
5198 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5199 if (FAILED(rc)) return rc;
5200 ComAssertRet(!!savedStateFile, E_FAIL);
5201 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5202 if (RT_FAILURE(vrc))
5203 return setError(VBOX_E_FILE_ERROR,
5204 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5205 savedStateFile.raw(), vrc);
5206 }
5207
5208 /* test and clear the TeleporterEnabled property */
5209 BOOL fTeleporterEnabled;
5210 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5211 if (FAILED(rc)) return rc;
5212#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5213 if (fTeleporterEnabled)
5214 {
5215 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5216 if (FAILED(rc)) return rc;
5217 }
5218#endif
5219
5220 /* create a progress object to track progress of this operation */
5221 ComObjPtr<Progress> powerupProgress;
5222 powerupProgress.createObject();
5223 Bstr progressDesc;
5224 if (mMachineState == MachineState_Saved)
5225 progressDesc = tr("Restoring virtual machine");
5226 else if (fTeleporterEnabled)
5227 progressDesc = tr("Teleporting virtual machine");
5228 else
5229 progressDesc = tr("Starting virtual machine");
5230 if (mMachineState == MachineState_Saved || !fTeleporterEnabled)
5231 rc = powerupProgress->init(static_cast<IConsole *>(this),
5232 progressDesc,
5233 FALSE /* aCancelable */);
5234 else
5235 rc = powerupProgress->init(static_cast<IConsole *>(this),
5236 progressDesc,
5237 TRUE /* aCancelable */,
5238 3 /* cOperations */,
5239 10 /* ulTotalOperationsWeight */,
5240 Bstr(tr("Teleporting virtual machine")),
5241 1 /* ulFirstOperationWeight */,
5242 NULL);
5243 if (FAILED(rc))
5244 return rc;
5245
5246 /* Tell VBoxSVC and Machine about the progress object so they can combine
5247 proxy it to any openRemoteSession caller. */
5248 rc = mControl->BeginPowerUp(powerupProgress);
5249 if (FAILED(rc))
5250 {
5251 LogFlowThisFunc(("BeginPowerUp failed\n"));
5252 return rc;
5253 }
5254
5255 BOOL fCanceled;
5256 rc = powerupProgress->COMGETTER(Canceled)(&fCanceled);
5257 if (FAILED(rc))
5258 return rc;
5259 if (fCanceled)
5260 {
5261 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5262 return setError(E_FAIL, tr("Powerup was canceled"));
5263 }
5264
5265 /* setup task object and thread to carry out the operation
5266 * asynchronously */
5267
5268 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
5269 ComAssertComRCRetRC(task->rc());
5270
5271 task->mConfigConstructor = configConstructor;
5272 task->mSharedFolders = sharedFolders;
5273 task->mStartPaused = aPaused;
5274 if (mMachineState == MachineState_Saved)
5275 task->mSavedStateFile = savedStateFile;
5276 task->mTeleporterEnabled = fTeleporterEnabled;
5277
5278 /* Reset differencing hard disks for which autoReset is true,
5279 * but only if the machine has no snapshots OR the current snapshot
5280 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5281 * image of an ONLINE snapshot which contains the disk state of the machine
5282 * while it was previously running, but without the corresponding machine
5283 * state, which is equivalent to powering off a running machine and not
5284 * good idea
5285 */
5286 ComPtr<ISnapshot> pCurrentSnapshot;
5287 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5288 if (FAILED(rc)) return rc;
5289
5290 BOOL fCurrentSnapshotIsOnline = false;
5291 if (pCurrentSnapshot)
5292 {
5293 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5294 if (FAILED(rc)) return rc;
5295 }
5296
5297 if (!fCurrentSnapshotIsOnline)
5298 {
5299 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5300
5301 com::SafeIfaceArray<IMediumAttachment> atts;
5302 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5303 if (FAILED(rc)) return rc;
5304
5305 for (size_t i = 0;
5306 i < atts.size();
5307 ++i)
5308 {
5309 DeviceType_T devType;
5310 rc = atts[i]->COMGETTER(Type)(&devType);
5311 /** @todo later applies to floppies as well */
5312 if (devType == DeviceType_HardDisk)
5313 {
5314 ComPtr<IMedium> medium;
5315 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
5316 if (FAILED(rc)) return rc;
5317
5318 /* needs autoreset? */
5319 BOOL autoReset = FALSE;
5320 rc = medium->COMGETTER(AutoReset)(&autoReset);
5321 if (FAILED(rc)) return rc;
5322
5323 if (autoReset)
5324 {
5325 ComPtr<IProgress> resetProgress;
5326 rc = medium->Reset(resetProgress.asOutParam());
5327 if (FAILED(rc)) return rc;
5328
5329 /* save for later use on the powerup thread */
5330 task->hardDiskProgresses.push_back(resetProgress);
5331 }
5332 }
5333 }
5334 }
5335 else
5336 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5337
5338 rc = consoleInitReleaseLog(mMachine);
5339 if (FAILED(rc)) return rc;
5340
5341 /* pass the progress object to the caller if requested */
5342 if (aProgress)
5343 {
5344 if (task->hardDiskProgresses.size() == 0)
5345 {
5346 /* there are no other operations to track, return the powerup
5347 * progress only */
5348 powerupProgress.queryInterfaceTo(aProgress);
5349 }
5350 else
5351 {
5352 /* create a combined progress object */
5353 ComObjPtr<CombinedProgress> progress;
5354 progress.createObject();
5355 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5356 progresses.push_back(ComPtr<IProgress> (powerupProgress));
5357 rc = progress->init(static_cast<IConsole *>(this),
5358 progressDesc, progresses.begin(),
5359 progresses.end());
5360 AssertComRCReturnRC(rc);
5361 progress.queryInterfaceTo(aProgress);
5362 }
5363 }
5364
5365 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5366 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5367
5368 ComAssertMsgRCRet(vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
5369 E_FAIL);
5370
5371 /* task is now owned by powerUpThread(), so release it */
5372 task.release();
5373
5374 /* finally, set the state: no right to fail in this method afterwards
5375 * since we've already started the thread and it is now responsible for
5376 * any error reporting and appropriate state change! */
5377
5378 if (mMachineState == MachineState_Saved)
5379 setMachineState(MachineState_Restoring);
5380 else if (fTeleporterEnabled)
5381 setMachineState(MachineState_TeleportingIn);
5382 else
5383 setMachineState(MachineState_Starting);
5384
5385 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5386 LogFlowThisFuncLeave();
5387 return S_OK;
5388}
5389
5390/**
5391 * Internal power off worker routine.
5392 *
5393 * This method may be called only at certain places with the following meaning
5394 * as shown below:
5395 *
5396 * - if the machine state is either Running or Paused, a normal
5397 * Console-initiated powerdown takes place (e.g. PowerDown());
5398 * - if the machine state is Saving, saveStateThread() has successfully done its
5399 * job;
5400 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5401 * to start/load the VM;
5402 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5403 * as a result of the powerDown() call).
5404 *
5405 * Calling it in situations other than the above will cause unexpected behavior.
5406 *
5407 * Note that this method should be the only one that destroys mpVM and sets it
5408 * to NULL.
5409 *
5410 * @param aProgress Progress object to run (may be NULL).
5411 *
5412 * @note Locks this object for writing.
5413 *
5414 * @note Never call this method from a thread that called addVMCaller() or
5415 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5416 * release(). Otherwise it will deadlock.
5417 */
5418HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5419{
5420 LogFlowThisFuncEnter();
5421
5422 AutoCaller autoCaller(this);
5423 AssertComRCReturnRC(autoCaller.rc());
5424
5425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5426
5427 /* Total # of steps for the progress object. Must correspond to the
5428 * number of "advance percent count" comments in this method! */
5429 enum { StepCount = 7 };
5430 /* current step */
5431 ULONG step = 0;
5432
5433 HRESULT rc = S_OK;
5434 int vrc = VINF_SUCCESS;
5435
5436 /* sanity */
5437 Assert(mVMDestroying == false);
5438
5439 Assert(mpVM != NULL);
5440
5441 AssertMsg( mMachineState == MachineState_Running
5442 || mMachineState == MachineState_Paused
5443 || mMachineState == MachineState_Stuck
5444 || mMachineState == MachineState_Starting
5445 || mMachineState == MachineState_Stopping
5446 || mMachineState == MachineState_Saving
5447 || mMachineState == MachineState_Restoring
5448 || mMachineState == MachineState_TeleportingPausedVM
5449 || mMachineState == MachineState_TeleportingIn
5450 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5451
5452 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5453 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5454
5455 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5456 * VM has already powered itself off in vmstateChangeCallback() and is just
5457 * notifying Console about that. In case of Starting or Restoring,
5458 * powerUpThread() is calling us on failure, so the VM is already off at
5459 * that point. */
5460 if ( !mVMPoweredOff
5461 && ( mMachineState == MachineState_Starting
5462 || mMachineState == MachineState_Restoring
5463 || mMachineState == MachineState_TeleportingIn)
5464 )
5465 mVMPoweredOff = true;
5466
5467 /*
5468 * Go to Stopping state if not already there.
5469 *
5470 * Note that we don't go from Saving/Restoring to Stopping because
5471 * vmstateChangeCallback() needs it to set the state to Saved on
5472 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5473 * while leaving the lock below, Saving or Restoring should be fine too.
5474 * Ditto for TeleportingPausedVM -> Teleported.
5475 */
5476 if ( mMachineState != MachineState_Saving
5477 && mMachineState != MachineState_Restoring
5478 && mMachineState != MachineState_Stopping
5479 && mMachineState != MachineState_TeleportingIn
5480 && mMachineState != MachineState_TeleportingPausedVM
5481 )
5482 setMachineState(MachineState_Stopping);
5483
5484 /* ----------------------------------------------------------------------
5485 * DONE with necessary state changes, perform the power down actions (it's
5486 * safe to leave the object lock now if needed)
5487 * ---------------------------------------------------------------------- */
5488
5489 /* Stop the VRDP server to prevent new clients connection while VM is being
5490 * powered off. */
5491 if (mConsoleVRDPServer)
5492 {
5493 LogFlowThisFunc(("Stopping VRDP server...\n"));
5494
5495 /* Leave the lock since EMT will call us back as addVMCaller()
5496 * in updateDisplayData(). */
5497 alock.leave();
5498
5499 mConsoleVRDPServer->Stop();
5500
5501 alock.enter();
5502 }
5503
5504 /* advance percent count */
5505 if (aProgress)
5506 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5507
5508
5509 /* ----------------------------------------------------------------------
5510 * Now, wait for all mpVM callers to finish their work if there are still
5511 * some on other threads. NO methods that need mpVM (or initiate other calls
5512 * that need it) may be called after this point
5513 * ---------------------------------------------------------------------- */
5514
5515 /* go to the destroying state to prevent from adding new callers */
5516 mVMDestroying = true;
5517
5518 if (mVMCallers > 0)
5519 {
5520 /* lazy creation */
5521 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5522 RTSemEventCreate(&mVMZeroCallersSem);
5523
5524 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5525 mVMCallers));
5526
5527 alock.leave();
5528
5529 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5530
5531 alock.enter();
5532 }
5533
5534 /* advance percent count */
5535 if (aProgress)
5536 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5537
5538 vrc = VINF_SUCCESS;
5539
5540 /*
5541 * Power off the VM if not already done that.
5542 * Leave the lock since EMT will call vmstateChangeCallback.
5543 *
5544 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5545 * VM-(guest-)initiated power off happened in parallel a ms before this
5546 * call. So far, we let this error pop up on the user's side.
5547 */
5548 if (!mVMPoweredOff)
5549 {
5550 LogFlowThisFunc(("Powering off the VM...\n"));
5551 alock.leave();
5552 vrc = VMR3PowerOff(mpVM);
5553 alock.enter();
5554 }
5555 else
5556 {
5557 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5558 * off. */
5559 /* reset the flag for future re-use */
5560 mVMPoweredOff = false;
5561 }
5562
5563 /* advance percent count */
5564 if (aProgress)
5565 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5566
5567#ifdef VBOX_WITH_HGCM
5568 /* Shutdown HGCM services before destroying the VM. */
5569 if (mVMMDev)
5570 {
5571 LogFlowThisFunc(("Shutdown HGCM...\n"));
5572
5573 /* Leave the lock since EMT will call us back as addVMCaller() */
5574 alock.leave();
5575
5576 mVMMDev->hgcmShutdown();
5577
5578 alock.enter();
5579 }
5580
5581 /* advance percent count */
5582 if (aProgress)
5583 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5584
5585#endif /* VBOX_WITH_HGCM */
5586
5587 LogFlowThisFunc(("Ready for VM destruction.\n"));
5588
5589 /* If we are called from Console::uninit(), then try to destroy the VM even
5590 * on failure (this will most likely fail too, but what to do?..) */
5591 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5592 {
5593 /* If the machine has an USB controller, release all USB devices
5594 * (symmetric to the code in captureUSBDevices()) */
5595 bool fHasUSBController = false;
5596 {
5597 PPDMIBASE pBase;
5598 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5599 if (RT_SUCCESS(vrc))
5600 {
5601 fHasUSBController = true;
5602 detachAllUSBDevices(false /* aDone */);
5603 }
5604 }
5605
5606 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5607 * this point). We leave the lock before calling VMR3Destroy() because
5608 * it will result into calling destructors of drivers associated with
5609 * Console children which may in turn try to lock Console (e.g. by
5610 * instantiating SafeVMPtr to access mpVM). It's safe here because
5611 * mVMDestroying is set which should prevent any activity. */
5612
5613 /* Set mpVM to NULL early just in case if some old code is not using
5614 * addVMCaller()/releaseVMCaller(). */
5615 PVM pVM = mpVM;
5616 mpVM = NULL;
5617
5618 LogFlowThisFunc(("Destroying the VM...\n"));
5619
5620 alock.leave();
5621
5622 vrc = VMR3Destroy(pVM);
5623
5624 /* take the lock again */
5625 alock.enter();
5626
5627 /* advance percent count */
5628 if (aProgress)
5629 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5630
5631 if (RT_SUCCESS(vrc))
5632 {
5633 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5634 mMachineState));
5635 /* Note: the Console-level machine state change happens on the
5636 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5637 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5638 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5639 * occurred yet. This is okay, because mMachineState is already
5640 * Stopping in this case, so any other attempt to call PowerDown()
5641 * will be rejected. */
5642 }
5643 else
5644 {
5645 /* bad bad bad, but what to do? */
5646 mpVM = pVM;
5647 rc = setError(VBOX_E_VM_ERROR,
5648 tr("Could not destroy the machine. (Error: %Rrc)"),
5649 vrc);
5650 }
5651
5652 /* Complete the detaching of the USB devices. */
5653 if (fHasUSBController)
5654 detachAllUSBDevices(true /* aDone */);
5655
5656 /* advance percent count */
5657 if (aProgress)
5658 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5659 }
5660 else
5661 {
5662 rc = setError(VBOX_E_VM_ERROR,
5663 tr("Could not power off the machine. (Error: %Rrc)"),
5664 vrc);
5665 }
5666
5667 /* Finished with destruction. Note that if something impossible happened and
5668 * we've failed to destroy the VM, mVMDestroying will remain true and
5669 * mMachineState will be something like Stopping, so most Console methods
5670 * will return an error to the caller. */
5671 if (mpVM == NULL)
5672 mVMDestroying = false;
5673
5674 if (SUCCEEDED(rc))
5675 mCallbackData.clear();
5676
5677 /* complete the progress */
5678 if (aProgress)
5679 aProgress->notifyComplete(rc);
5680
5681 LogFlowThisFuncLeave();
5682 return rc;
5683}
5684
5685/**
5686 * @note Locks this object for writing.
5687 */
5688HRESULT Console::setMachineState(MachineState_T aMachineState,
5689 bool aUpdateServer /* = true */)
5690{
5691 AutoCaller autoCaller(this);
5692 AssertComRCReturnRC(autoCaller.rc());
5693
5694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5695
5696 HRESULT rc = S_OK;
5697
5698 if (mMachineState != aMachineState)
5699 {
5700 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5701 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5702 mMachineState = aMachineState;
5703
5704 /// @todo (dmik)
5705 // possibly, we need to redo onStateChange() using the dedicated
5706 // Event thread, like it is done in VirtualBox. This will make it
5707 // much safer (no deadlocks possible if someone tries to use the
5708 // console from the callback), however, listeners will lose the
5709 // ability to synchronously react to state changes (is it really
5710 // necessary??)
5711 LogFlowThisFunc(("Doing onStateChange()...\n"));
5712 onStateChange(aMachineState);
5713 LogFlowThisFunc(("Done onStateChange()\n"));
5714
5715 if (aUpdateServer)
5716 {
5717 /* Server notification MUST be done from under the lock; otherwise
5718 * the machine state here and on the server might go out of sync
5719 * which can lead to various unexpected results (like the machine
5720 * state being >= MachineState_Running on the server, while the
5721 * session state is already SessionState_Closed at the same time
5722 * there).
5723 *
5724 * Cross-lock conditions should be carefully watched out: calling
5725 * UpdateState we will require Machine and SessionMachine locks
5726 * (remember that here we're holding the Console lock here, and also
5727 * all locks that have been entered by the thread before calling
5728 * this method).
5729 */
5730 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5731 rc = mControl->UpdateState(aMachineState);
5732 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5733 }
5734 }
5735
5736 return rc;
5737}
5738
5739/**
5740 * Searches for a shared folder with the given logical name
5741 * in the collection of shared folders.
5742 *
5743 * @param aName logical name of the shared folder
5744 * @param aSharedFolder where to return the found object
5745 * @param aSetError whether to set the error info if the folder is
5746 * not found
5747 * @return
5748 * S_OK when found or E_INVALIDARG when not found
5749 *
5750 * @note The caller must lock this object for writing.
5751 */
5752HRESULT Console::findSharedFolder(CBSTR aName,
5753 ComObjPtr<SharedFolder> &aSharedFolder,
5754 bool aSetError /* = false */)
5755{
5756 /* sanity check */
5757 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5758
5759 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5760 if (it != mSharedFolders.end())
5761 {
5762 aSharedFolder = it->second;
5763 return S_OK;
5764 }
5765
5766 if (aSetError)
5767 setError(VBOX_E_FILE_ERROR,
5768 tr("Could not find a shared folder named '%ls'."),
5769 aName);
5770
5771 return VBOX_E_FILE_ERROR;
5772}
5773
5774/**
5775 * Fetches the list of global or machine shared folders from the server.
5776 *
5777 * @param aGlobal true to fetch global folders.
5778 *
5779 * @note The caller must lock this object for writing.
5780 */
5781HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5782{
5783 /* sanity check */
5784 AssertReturn(AutoCaller(this).state() == InInit ||
5785 isWriteLockOnCurrentThread(), E_FAIL);
5786
5787 /* protect mpVM (if not NULL) */
5788 AutoVMCallerQuietWeak autoVMCaller(this);
5789
5790 HRESULT rc = S_OK;
5791
5792 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5793
5794 if (aGlobal)
5795 {
5796 /// @todo grab & process global folders when they are done
5797 }
5798 else
5799 {
5800 SharedFolderDataMap oldFolders;
5801 if (online)
5802 oldFolders = mMachineSharedFolders;
5803
5804 mMachineSharedFolders.clear();
5805
5806 SafeIfaceArray<ISharedFolder> folders;
5807 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
5808 AssertComRCReturnRC(rc);
5809
5810 for (size_t i = 0; i < folders.size(); ++i)
5811 {
5812 ComPtr<ISharedFolder> folder = folders[i];
5813
5814 Bstr name;
5815 Bstr hostPath;
5816 BOOL writable;
5817
5818 rc = folder->COMGETTER(Name)(name.asOutParam());
5819 if (FAILED(rc)) break;
5820 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
5821 if (FAILED(rc)) break;
5822 rc = folder->COMGETTER(Writable)(&writable);
5823
5824 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable)));
5825
5826 /* send changes to HGCM if the VM is running */
5827 /// @todo report errors as runtime warnings through VMSetError
5828 if (online)
5829 {
5830 SharedFolderDataMap::iterator it = oldFolders.find(name);
5831 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5832 {
5833 /* a new machine folder is added or
5834 * the existing machine folder is changed */
5835 if (mSharedFolders.find(name) != mSharedFolders.end())
5836 ; /* the console folder exists, nothing to do */
5837 else
5838 {
5839 /* remove the old machine folder (when changed)
5840 * or the global folder if any (when new) */
5841 if (it != oldFolders.end() ||
5842 mGlobalSharedFolders.find(name) !=
5843 mGlobalSharedFolders.end())
5844 rc = removeSharedFolder(name);
5845 /* create the new machine folder */
5846 rc = createSharedFolder(name, SharedFolderData(hostPath, writable));
5847 }
5848 }
5849 /* forget the processed (or identical) folder */
5850 if (it != oldFolders.end())
5851 oldFolders.erase(it);
5852
5853 rc = S_OK;
5854 }
5855 }
5856
5857 AssertComRCReturnRC(rc);
5858
5859 /* process outdated (removed) folders */
5860 /// @todo report errors as runtime warnings through VMSetError
5861 if (online)
5862 {
5863 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5864 it != oldFolders.end(); ++ it)
5865 {
5866 if (mSharedFolders.find(it->first) != mSharedFolders.end())
5867 ; /* the console folder exists, nothing to do */
5868 else
5869 {
5870 /* remove the outdated machine folder */
5871 rc = removeSharedFolder(it->first);
5872 /* create the global folder if there is any */
5873 SharedFolderDataMap::const_iterator git =
5874 mGlobalSharedFolders.find(it->first);
5875 if (git != mGlobalSharedFolders.end())
5876 rc = createSharedFolder(git->first, git->second);
5877 }
5878 }
5879
5880 rc = S_OK;
5881 }
5882 }
5883
5884 return rc;
5885}
5886
5887/**
5888 * Searches for a shared folder with the given name in the list of machine
5889 * shared folders and then in the list of the global shared folders.
5890 *
5891 * @param aName Name of the folder to search for.
5892 * @param aIt Where to store the pointer to the found folder.
5893 * @return @c true if the folder was found and @c false otherwise.
5894 *
5895 * @note The caller must lock this object for reading.
5896 */
5897bool Console::findOtherSharedFolder(IN_BSTR aName,
5898 SharedFolderDataMap::const_iterator &aIt)
5899{
5900 /* sanity check */
5901 AssertReturn(isWriteLockOnCurrentThread(), false);
5902
5903 /* first, search machine folders */
5904 aIt = mMachineSharedFolders.find(aName);
5905 if (aIt != mMachineSharedFolders.end())
5906 return true;
5907
5908 /* second, search machine folders */
5909 aIt = mGlobalSharedFolders.find(aName);
5910 if (aIt != mGlobalSharedFolders.end())
5911 return true;
5912
5913 return false;
5914}
5915
5916/**
5917 * Calls the HGCM service to add a shared folder definition.
5918 *
5919 * @param aName Shared folder name.
5920 * @param aHostPath Shared folder path.
5921 *
5922 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5923 * @note Doesn't lock anything.
5924 */
5925HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
5926{
5927 ComAssertRet(aName && *aName, E_FAIL);
5928 ComAssertRet(aData.mHostPath, E_FAIL);
5929
5930 /* sanity checks */
5931 AssertReturn(mpVM, E_FAIL);
5932 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5933
5934 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
5935 SHFLSTRING *pFolderName, *pMapName;
5936 size_t cbString;
5937
5938 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
5939
5940 cbString = (RTUtf16Len(aData.mHostPath) + 1) * sizeof(RTUTF16);
5941 if (cbString >= UINT16_MAX)
5942 return setError(E_INVALIDARG, tr("The name is too long"));
5943 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5944 Assert(pFolderName);
5945 memcpy(pFolderName->String.ucs2, aData.mHostPath, cbString);
5946
5947 pFolderName->u16Size = (uint16_t)cbString;
5948 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5949
5950 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
5951 parms[0].u.pointer.addr = pFolderName;
5952 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5953
5954 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
5955 if (cbString >= UINT16_MAX)
5956 {
5957 RTMemFree(pFolderName);
5958 return setError(E_INVALIDARG, tr("The host path is too long"));
5959 }
5960 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5961 Assert(pMapName);
5962 memcpy(pMapName->String.ucs2, aName, cbString);
5963
5964 pMapName->u16Size = (uint16_t)cbString;
5965 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5966
5967 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
5968 parms[1].u.pointer.addr = pMapName;
5969 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5970
5971 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
5972 parms[2].u.uint32 = aData.mWritable;
5973
5974 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
5975 SHFL_FN_ADD_MAPPING,
5976 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
5977 RTMemFree(pFolderName);
5978 RTMemFree(pMapName);
5979
5980 if (RT_FAILURE(vrc))
5981 return setError(E_FAIL,
5982 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
5983 aName, aData.mHostPath.raw(), vrc);
5984
5985 return S_OK;
5986}
5987
5988/**
5989 * Calls the HGCM service to remove the shared folder definition.
5990 *
5991 * @param aName Shared folder name.
5992 *
5993 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5994 * @note Doesn't lock anything.
5995 */
5996HRESULT Console::removeSharedFolder(CBSTR aName)
5997{
5998 ComAssertRet(aName && *aName, E_FAIL);
5999
6000 /* sanity checks */
6001 AssertReturn(mpVM, E_FAIL);
6002 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6003
6004 VBOXHGCMSVCPARM parms;
6005 SHFLSTRING *pMapName;
6006 size_t cbString;
6007
6008 Log(("Removing shared folder '%ls'\n", aName));
6009
6010 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6011 if (cbString >= UINT16_MAX)
6012 return setError(E_INVALIDARG, tr("The name is too long"));
6013 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6014 Assert(pMapName);
6015 memcpy(pMapName->String.ucs2, aName, cbString);
6016
6017 pMapName->u16Size = (uint16_t)cbString;
6018 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6019
6020 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6021 parms.u.pointer.addr = pMapName;
6022 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6023
6024 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6025 SHFL_FN_REMOVE_MAPPING,
6026 1, &parms);
6027 RTMemFree(pMapName);
6028 if (RT_FAILURE(vrc))
6029 return setError(E_FAIL,
6030 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6031 aName, vrc);
6032
6033 return S_OK;
6034}
6035
6036/**
6037 * VM state callback function. Called by the VMM
6038 * using its state machine states.
6039 *
6040 * Primarily used to handle VM initiated power off, suspend and state saving,
6041 * but also for doing termination completed work (VMSTATE_TERMINATE).
6042 *
6043 * In general this function is called in the context of the EMT.
6044 *
6045 * @param aVM The VM handle.
6046 * @param aState The new state.
6047 * @param aOldState The old state.
6048 * @param aUser The user argument (pointer to the Console object).
6049 *
6050 * @note Locks the Console object for writing.
6051 */
6052DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6053 VMSTATE aState,
6054 VMSTATE aOldState,
6055 void *aUser)
6056{
6057 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6058 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6059
6060 Console *that = static_cast<Console *>(aUser);
6061 AssertReturnVoid(that);
6062
6063 AutoCaller autoCaller(that);
6064
6065 /* Note that we must let this method proceed even if Console::uninit() has
6066 * been already called. In such case this VMSTATE change is a result of:
6067 * 1) powerDown() called from uninit() itself, or
6068 * 2) VM-(guest-)initiated power off. */
6069 AssertReturnVoid( autoCaller.isOk()
6070 || autoCaller.state() == InUninit);
6071
6072 switch (aState)
6073 {
6074 /*
6075 * The VM has terminated
6076 */
6077 case VMSTATE_OFF:
6078 {
6079 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6080
6081 if (that->mVMStateChangeCallbackDisabled)
6082 break;
6083
6084 /* Do we still think that it is running? It may happen if this is a
6085 * VM-(guest-)initiated shutdown/poweroff.
6086 */
6087 if ( that->mMachineState != MachineState_Stopping
6088 && that->mMachineState != MachineState_Saving
6089 && that->mMachineState != MachineState_Restoring
6090 && that->mMachineState != MachineState_TeleportingIn
6091 && that->mMachineState != MachineState_TeleportingPausedVM
6092 && !that->mVMIsAlreadyPoweringOff
6093 )
6094 {
6095 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6096
6097 /* prevent powerDown() from calling VMR3PowerOff() again */
6098 Assert(that->mVMPoweredOff == false);
6099 that->mVMPoweredOff = true;
6100
6101 /* we are stopping now */
6102 that->setMachineState(MachineState_Stopping);
6103
6104 /* Setup task object and thread to carry out the operation
6105 * asynchronously (if we call powerDown() right here but there
6106 * is one or more mpVM callers (added with addVMCaller()) we'll
6107 * deadlock).
6108 */
6109 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6110 true /* aUsesVMPtr */));
6111
6112 /* If creating a task is falied, this can currently mean one of
6113 * two: either Console::uninit() has been called just a ms
6114 * before (so a powerDown() call is already on the way), or
6115 * powerDown() itself is being already executed. Just do
6116 * nothing.
6117 */
6118 if (!task->isOk())
6119 {
6120 LogFlowFunc(("Console is already being uninitialized.\n"));
6121 break;
6122 }
6123
6124 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6125 (void *) task.get(), 0,
6126 RTTHREADTYPE_MAIN_WORKER, 0,
6127 "VMPowerDown");
6128 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6129
6130 /* task is now owned by powerDownThread(), so release it */
6131 task.release();
6132 }
6133 break;
6134 }
6135
6136 /* The VM has been completely destroyed.
6137 *
6138 * Note: This state change can happen at two points:
6139 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6140 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6141 * called by EMT.
6142 */
6143 case VMSTATE_TERMINATED:
6144 {
6145 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6146
6147 if (that->mVMStateChangeCallbackDisabled)
6148 break;
6149
6150 /* Terminate host interface networking. If aVM is NULL, we've been
6151 * manually called from powerUpThread() either before calling
6152 * VMR3Create() or after VMR3Create() failed, so no need to touch
6153 * networking.
6154 */
6155 if (aVM)
6156 that->powerDownHostInterfaces();
6157
6158 /* From now on the machine is officially powered down or remains in
6159 * the Saved state.
6160 */
6161 switch (that->mMachineState)
6162 {
6163 default:
6164 AssertFailed();
6165 /* fall through */
6166 case MachineState_Stopping:
6167 /* successfully powered down */
6168 that->setMachineState(MachineState_PoweredOff);
6169 break;
6170 case MachineState_Saving:
6171 /* successfully saved (note that the machine is already in
6172 * the Saved state on the server due to EndSavingState()
6173 * called from saveStateThread(), so only change the local
6174 * state) */
6175 that->setMachineStateLocally(MachineState_Saved);
6176 break;
6177 case MachineState_Starting:
6178 /* failed to start, but be patient: set back to PoweredOff
6179 * (for similarity with the below) */
6180 that->setMachineState(MachineState_PoweredOff);
6181 break;
6182 case MachineState_Restoring:
6183 /* failed to load the saved state file, but be patient: set
6184 * back to Saved (to preserve the saved state file) */
6185 that->setMachineState(MachineState_Saved);
6186 break;
6187 case MachineState_TeleportingIn:
6188 /* Teleportation failed or was cancelled. Back to powered off. */
6189 that->setMachineState(MachineState_PoweredOff);
6190 break;
6191 case MachineState_TeleportingPausedVM:
6192 /* Successfully teleported the VM. */
6193 that->setMachineState(MachineState_Teleported);
6194 break;
6195 }
6196 break;
6197 }
6198
6199 case VMSTATE_SUSPENDED:
6200 {
6201 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6202
6203 if (that->mVMStateChangeCallbackDisabled)
6204 break;
6205
6206 switch (that->mMachineState)
6207 {
6208 case MachineState_Teleporting:
6209 that->setMachineState(MachineState_TeleportingPausedVM);
6210 break;
6211
6212 case MachineState_LiveSnapshotting:
6213 that->setMachineState(MachineState_Saving);
6214 break;
6215
6216 case MachineState_TeleportingPausedVM:
6217 case MachineState_Saving:
6218 case MachineState_Restoring:
6219 case MachineState_Stopping:
6220 case MachineState_TeleportingIn:
6221 /* The worker threads handles the transition. */
6222 break;
6223
6224 default:
6225 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6226 case MachineState_Running:
6227 that->setMachineState(MachineState_Paused);
6228 break;
6229 }
6230 break;
6231 }
6232
6233 case VMSTATE_SUSPENDED_LS:
6234 case VMSTATE_SUSPENDED_EXT_LS:
6235 {
6236 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6237 if (that->mVMStateChangeCallbackDisabled)
6238 break;
6239 switch (that->mMachineState)
6240 {
6241 case MachineState_Teleporting:
6242 that->setMachineState(MachineState_TeleportingPausedVM);
6243 break;
6244
6245 case MachineState_LiveSnapshotting:
6246 that->setMachineState(MachineState_Saving);
6247 break;
6248
6249 case MachineState_TeleportingPausedVM:
6250 case MachineState_Saving:
6251 /* ignore */
6252 break;
6253
6254 default:
6255 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6256 that->setMachineState(MachineState_Paused);
6257 break;
6258 }
6259 break;
6260 }
6261
6262 case VMSTATE_RUNNING:
6263 {
6264 if ( aOldState == VMSTATE_POWERING_ON
6265 || aOldState == VMSTATE_RESUMING)
6266 {
6267 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6268
6269 if (that->mVMStateChangeCallbackDisabled)
6270 break;
6271
6272 Assert( ( ( that->mMachineState == MachineState_Starting
6273 || that->mMachineState == MachineState_Paused)
6274 && aOldState == VMSTATE_POWERING_ON)
6275 || ( ( that->mMachineState == MachineState_Restoring
6276 || that->mMachineState == MachineState_TeleportingIn
6277 || that->mMachineState == MachineState_Paused
6278 || that->mMachineState == MachineState_Saving
6279 )
6280 && aOldState == VMSTATE_RESUMING));
6281
6282 that->setMachineState(MachineState_Running);
6283 }
6284
6285 break;
6286 }
6287
6288 case VMSTATE_RUNNING_LS:
6289 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6290 || that->mMachineState == MachineState_Teleporting,
6291 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6292 break;
6293
6294 case VMSTATE_FATAL_ERROR:
6295 {
6296 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6297
6298 if (that->mVMStateChangeCallbackDisabled)
6299 break;
6300
6301 /* Fatal errors are only for running VMs. */
6302 Assert(Global::IsOnline(that->mMachineState));
6303
6304 /* Note! 'Pause' is used here in want of something better. There
6305 * are currently only two places where fatal errors might be
6306 * raised, so it is not worth adding a new externally
6307 * visible state for this yet. */
6308 that->setMachineState(MachineState_Paused);
6309 break;
6310 }
6311
6312 case VMSTATE_GURU_MEDITATION:
6313 {
6314 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6315
6316 if (that->mVMStateChangeCallbackDisabled)
6317 break;
6318
6319 /* Guru are only for running VMs */
6320 Assert(Global::IsOnline(that->mMachineState));
6321
6322 that->setMachineState(MachineState_Stuck);
6323 break;
6324 }
6325
6326 default: /* shut up gcc */
6327 break;
6328 }
6329}
6330
6331#ifdef VBOX_WITH_USB
6332
6333/**
6334 * Sends a request to VMM to attach the given host device.
6335 * After this method succeeds, the attached device will appear in the
6336 * mUSBDevices collection.
6337 *
6338 * @param aHostDevice device to attach
6339 *
6340 * @note Synchronously calls EMT.
6341 * @note Must be called from under this object's lock.
6342 */
6343HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6344{
6345 AssertReturn(aHostDevice, E_FAIL);
6346 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6347
6348 /* still want a lock object because we need to leave it */
6349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6350
6351 HRESULT hrc;
6352
6353 /*
6354 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6355 * method in EMT (using usbAttachCallback()).
6356 */
6357 Bstr BstrAddress;
6358 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6359 ComAssertComRCRetRC(hrc);
6360
6361 Utf8Str Address(BstrAddress);
6362
6363 Bstr id;
6364 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6365 ComAssertComRCRetRC(hrc);
6366 Guid uuid(id);
6367
6368 BOOL fRemote = FALSE;
6369 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6370 ComAssertComRCRetRC(hrc);
6371
6372 /* protect mpVM */
6373 AutoVMCaller autoVMCaller(this);
6374 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6375
6376 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6377 Address.raw(), uuid.ptr()));
6378
6379 /* leave the lock before a VMR3* call (EMT will call us back)! */
6380 alock.leave();
6381
6382/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6383 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6384 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
6385
6386 /* restore the lock */
6387 alock.enter();
6388
6389 /* hrc is S_OK here */
6390
6391 if (RT_FAILURE(vrc))
6392 {
6393 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6394 Address.raw(), uuid.ptr(), vrc));
6395
6396 switch (vrc)
6397 {
6398 case VERR_VUSB_NO_PORTS:
6399 hrc = setError(E_FAIL,
6400 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6401 break;
6402 case VERR_VUSB_USBFS_PERMISSION:
6403 hrc = setError(E_FAIL,
6404 tr("Not permitted to open the USB device, check usbfs options"));
6405 break;
6406 default:
6407 hrc = setError(E_FAIL,
6408 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6409 vrc);
6410 break;
6411 }
6412 }
6413
6414 return hrc;
6415}
6416
6417/**
6418 * USB device attach callback used by AttachUSBDevice().
6419 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6420 * so we don't use AutoCaller and don't care about reference counters of
6421 * interface pointers passed in.
6422 *
6423 * @thread EMT
6424 * @note Locks the console object for writing.
6425 */
6426//static
6427DECLCALLBACK(int)
6428Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6429{
6430 LogFlowFuncEnter();
6431 LogFlowFunc(("that={%p}\n", that));
6432
6433 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6434
6435 void *pvRemoteBackend = NULL;
6436 if (aRemote)
6437 {
6438 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6439 Guid guid(*aUuid);
6440
6441 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6442 if (!pvRemoteBackend)
6443 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6444 }
6445
6446 USHORT portVersion = 1;
6447 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6448 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6449 Assert(portVersion == 1 || portVersion == 2);
6450
6451 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6452 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6453 if (RT_SUCCESS(vrc))
6454 {
6455 /* Create a OUSBDevice and add it to the device list */
6456 ComObjPtr<OUSBDevice> device;
6457 device.createObject();
6458 hrc = device->init(aHostDevice);
6459 AssertComRC(hrc);
6460
6461 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6462 that->mUSBDevices.push_back(device);
6463 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6464
6465 /* notify callbacks */
6466 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6467 }
6468
6469 LogFlowFunc(("vrc=%Rrc\n", vrc));
6470 LogFlowFuncLeave();
6471 return vrc;
6472}
6473
6474/**
6475 * Sends a request to VMM to detach the given host device. After this method
6476 * succeeds, the detached device will disappear from the mUSBDevices
6477 * collection.
6478 *
6479 * @param aIt Iterator pointing to the device to detach.
6480 *
6481 * @note Synchronously calls EMT.
6482 * @note Must be called from under this object's lock.
6483 */
6484HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6485{
6486 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6487
6488 /* still want a lock object because we need to leave it */
6489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6490
6491 /* protect mpVM */
6492 AutoVMCaller autoVMCaller(this);
6493 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6494
6495 /* if the device is attached, then there must at least one USB hub. */
6496 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6497
6498 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6499 (*aIt)->id().raw()));
6500
6501 /* leave the lock before a VMR3* call (EMT will call us back)! */
6502 alock.leave();
6503
6504/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6505 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6506 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6507 ComAssertRCRet(vrc, E_FAIL);
6508
6509 return S_OK;
6510}
6511
6512/**
6513 * USB device detach callback used by DetachUSBDevice().
6514 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6515 * so we don't use AutoCaller and don't care about reference counters of
6516 * interface pointers passed in.
6517 *
6518 * @thread EMT
6519 * @note Locks the console object for writing.
6520 */
6521//static
6522DECLCALLBACK(int)
6523Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6524{
6525 LogFlowFuncEnter();
6526 LogFlowFunc(("that={%p}\n", that));
6527
6528 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6529 ComObjPtr<OUSBDevice> device = **aIt;
6530
6531 /*
6532 * If that was a remote device, release the backend pointer.
6533 * The pointer was requested in usbAttachCallback.
6534 */
6535 BOOL fRemote = FALSE;
6536
6537 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6538 ComAssertComRC(hrc2);
6539
6540 if (fRemote)
6541 {
6542 Guid guid(*aUuid);
6543 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6544 }
6545
6546 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6547
6548 if (RT_SUCCESS(vrc))
6549 {
6550 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6551
6552 /* Remove the device from the collection */
6553 that->mUSBDevices.erase(*aIt);
6554 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6555
6556 /* notify callbacks */
6557 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6558 }
6559
6560 LogFlowFunc(("vrc=%Rrc\n", vrc));
6561 LogFlowFuncLeave();
6562 return vrc;
6563}
6564
6565#endif /* VBOX_WITH_USB */
6566#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6567
6568/**
6569 * Helper function to handle host interface device creation and attachment.
6570 *
6571 * @param networkAdapter the network adapter which attachment should be reset
6572 * @return COM status code
6573 *
6574 * @note The caller must lock this object for writing.
6575 *
6576 * @todo Move this back into the driver!
6577 */
6578HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6579{
6580 LogFlowThisFunc(("\n"));
6581 /* sanity check */
6582 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6583
6584# ifdef VBOX_STRICT
6585 /* paranoia */
6586 NetworkAttachmentType_T attachment;
6587 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6588 Assert(attachment == NetworkAttachmentType_Bridged);
6589# endif /* VBOX_STRICT */
6590
6591 HRESULT rc = S_OK;
6592
6593 ULONG slot = 0;
6594 rc = networkAdapter->COMGETTER(Slot)(&slot);
6595 AssertComRC(rc);
6596
6597# ifdef RT_OS_LINUX
6598 /*
6599 * Allocate a host interface device
6600 */
6601 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6602 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6603 if (RT_SUCCESS(rcVBox))
6604 {
6605 /*
6606 * Set/obtain the tap interface.
6607 */
6608 struct ifreq IfReq;
6609 memset(&IfReq, 0, sizeof(IfReq));
6610 /* The name of the TAP interface we are using */
6611 Bstr tapDeviceName;
6612 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6613 if (FAILED(rc))
6614 tapDeviceName.setNull(); /* Is this necessary? */
6615 if (tapDeviceName.isEmpty())
6616 {
6617 LogRel(("No TAP device name was supplied.\n"));
6618 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6619 }
6620
6621 if (SUCCEEDED(rc))
6622 {
6623 /* If we are using a static TAP device then try to open it. */
6624 Utf8Str str(tapDeviceName);
6625 if (str.length() <= sizeof(IfReq.ifr_name))
6626 strcpy(IfReq.ifr_name, str.raw());
6627 else
6628 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6629 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6630 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6631 if (rcVBox != 0)
6632 {
6633 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6634 rc = setError(E_FAIL,
6635 tr("Failed to open the host network interface %ls"),
6636 tapDeviceName.raw());
6637 }
6638 }
6639 if (SUCCEEDED(rc))
6640 {
6641 /*
6642 * Make it pollable.
6643 */
6644 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6645 {
6646 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6647 /*
6648 * Here is the right place to communicate the TAP file descriptor and
6649 * the host interface name to the server if/when it becomes really
6650 * necessary.
6651 */
6652 maTAPDeviceName[slot] = tapDeviceName;
6653 rcVBox = VINF_SUCCESS;
6654 }
6655 else
6656 {
6657 int iErr = errno;
6658
6659 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6660 rcVBox = VERR_HOSTIF_BLOCKING;
6661 rc = setError(E_FAIL,
6662 tr("could not set up the host networking device for non blocking access: %s"),
6663 strerror(errno));
6664 }
6665 }
6666 }
6667 else
6668 {
6669 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6670 switch (rcVBox)
6671 {
6672 case VERR_ACCESS_DENIED:
6673 /* will be handled by our caller */
6674 rc = rcVBox;
6675 break;
6676 default:
6677 rc = setError(E_FAIL,
6678 tr("Could not set up the host networking device: %Rrc"),
6679 rcVBox);
6680 break;
6681 }
6682 }
6683
6684# elif defined(RT_OS_FREEBSD)
6685 /*
6686 * Set/obtain the tap interface.
6687 */
6688 /* The name of the TAP interface we are using */
6689 Bstr tapDeviceName;
6690 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6691 if (FAILED(rc))
6692 tapDeviceName.setNull(); /* Is this necessary? */
6693 if (tapDeviceName.isEmpty())
6694 {
6695 LogRel(("No TAP device name was supplied.\n"));
6696 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6697 }
6698 char szTapdev[1024] = "/dev/";
6699 /* If we are using a static TAP device then try to open it. */
6700 Utf8Str str(tapDeviceName);
6701 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6702 strcat(szTapdev, str.raw());
6703 else
6704 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6705 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6706 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6707
6708 if (RT_SUCCESS(rcVBox))
6709 maTAPDeviceName[slot] = tapDeviceName;
6710 else
6711 {
6712 switch (rcVBox)
6713 {
6714 case VERR_ACCESS_DENIED:
6715 /* will be handled by our caller */
6716 rc = rcVBox;
6717 break;
6718 default:
6719 rc = setError(E_FAIL,
6720 tr("Failed to open the host network interface %ls"),
6721 tapDeviceName.raw());
6722 break;
6723 }
6724 }
6725# else
6726# error "huh?"
6727# endif
6728 /* in case of failure, cleanup. */
6729 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6730 {
6731 LogRel(("General failure attaching to host interface\n"));
6732 rc = setError(E_FAIL,
6733 tr("General failure attaching to host interface"));
6734 }
6735 LogFlowThisFunc(("rc=%d\n", rc));
6736 return rc;
6737}
6738
6739
6740/**
6741 * Helper function to handle detachment from a host interface
6742 *
6743 * @param networkAdapter the network adapter which attachment should be reset
6744 * @return COM status code
6745 *
6746 * @note The caller must lock this object for writing.
6747 *
6748 * @todo Move this back into the driver!
6749 */
6750HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6751{
6752 /* sanity check */
6753 LogFlowThisFunc(("\n"));
6754 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6755
6756 HRESULT rc = S_OK;
6757# ifdef VBOX_STRICT
6758 /* paranoia */
6759 NetworkAttachmentType_T attachment;
6760 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6761 Assert(attachment == NetworkAttachmentType_Bridged);
6762# endif /* VBOX_STRICT */
6763
6764 ULONG slot = 0;
6765 rc = networkAdapter->COMGETTER(Slot)(&slot);
6766 AssertComRC(rc);
6767
6768 /* is there an open TAP device? */
6769 if (maTapFD[slot] != NIL_RTFILE)
6770 {
6771 /*
6772 * Close the file handle.
6773 */
6774 Bstr tapDeviceName, tapTerminateApplication;
6775 bool isStatic = true;
6776 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6777 if (FAILED(rc) || tapDeviceName.isEmpty())
6778 {
6779 /* If the name is empty, this is a dynamic TAP device, so close it now,
6780 so that the termination script can remove the interface. Otherwise we still
6781 need the FD to pass to the termination script. */
6782 isStatic = false;
6783 int rcVBox = RTFileClose(maTapFD[slot]);
6784 AssertRC(rcVBox);
6785 maTapFD[slot] = NIL_RTFILE;
6786 }
6787 if (isStatic)
6788 {
6789 /* If we are using a static TAP device, we close it now, after having called the
6790 termination script. */
6791 int rcVBox = RTFileClose(maTapFD[slot]);
6792 AssertRC(rcVBox);
6793 }
6794 /* the TAP device name and handle are no longer valid */
6795 maTapFD[slot] = NIL_RTFILE;
6796 maTAPDeviceName[slot] = "";
6797 }
6798 LogFlowThisFunc(("returning %d\n", rc));
6799 return rc;
6800}
6801
6802#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6803
6804/**
6805 * Called at power down to terminate host interface networking.
6806 *
6807 * @note The caller must lock this object for writing.
6808 */
6809HRESULT Console::powerDownHostInterfaces()
6810{
6811 LogFlowThisFunc(("\n"));
6812
6813 /* sanity check */
6814 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6815
6816 /*
6817 * host interface termination handling
6818 */
6819 HRESULT rc;
6820 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6821 {
6822 ComPtr<INetworkAdapter> networkAdapter;
6823 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6824 if (FAILED(rc)) break;
6825
6826 BOOL enabled = FALSE;
6827 networkAdapter->COMGETTER(Enabled)(&enabled);
6828 if (!enabled)
6829 continue;
6830
6831 NetworkAttachmentType_T attachment;
6832 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6833 if (attachment == NetworkAttachmentType_Bridged)
6834 {
6835#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6836 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6837 if (FAILED(rc2) && SUCCEEDED(rc))
6838 rc = rc2;
6839#endif
6840 }
6841 }
6842
6843 return rc;
6844}
6845
6846
6847/**
6848 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
6849 * and VMR3Teleport.
6850 *
6851 * @param pVM The VM handle.
6852 * @param uPercent Completetion precentage (0-100).
6853 * @param pvUser Pointer to the VMProgressTask structure.
6854 * @return VINF_SUCCESS.
6855 */
6856/*static*/
6857DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6858{
6859 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6860 AssertReturn(task, VERR_INVALID_PARAMETER);
6861
6862 /* update the progress object */
6863 if (task->mProgress)
6864 task->mProgress->SetCurrentOperationProgress(uPercent);
6865
6866 return VINF_SUCCESS;
6867}
6868
6869/**
6870 * @copydoc FNVMATERROR
6871 *
6872 * @remarks Might be some tiny serialization concerns with access to the string
6873 * object here...
6874 */
6875/*static*/ DECLCALLBACK(void)
6876Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6877 const char *pszErrorFmt, va_list va)
6878{
6879 Utf8Str *pErrorText = (Utf8Str *)pvUser;
6880 AssertPtr(pErrorText);
6881
6882 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
6883 va_list va2;
6884 va_copy(va2, va);
6885
6886 /* Append to any the existing error message. */
6887 if (pErrorText->length())
6888 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
6889 pszErrorFmt, &va2, rc, rc);
6890 else
6891 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
6892
6893 va_end(va2);
6894}
6895
6896/**
6897 * VM runtime error callback function.
6898 * See VMSetRuntimeError for the detailed description of parameters.
6899 *
6900 * @param pVM The VM handle.
6901 * @param pvUser The user argument.
6902 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6903 * @param pszErrorId Error ID string.
6904 * @param pszFormat Error message format string.
6905 * @param va Error message arguments.
6906 * @thread EMT.
6907 */
6908/* static */ DECLCALLBACK(void)
6909Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
6910 const char *pszErrorId,
6911 const char *pszFormat, va_list va)
6912{
6913 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6914 LogFlowFuncEnter();
6915
6916 Console *that = static_cast<Console *>(pvUser);
6917 AssertReturnVoid(that);
6918
6919 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
6920
6921 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
6922 fFatal, pszErrorId, message.raw()));
6923
6924 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
6925
6926 LogFlowFuncLeave();
6927}
6928
6929/**
6930 * Captures USB devices that match filters of the VM.
6931 * Called at VM startup.
6932 *
6933 * @param pVM The VM handle.
6934 *
6935 * @note The caller must lock this object for writing.
6936 */
6937HRESULT Console::captureUSBDevices(PVM pVM)
6938{
6939 LogFlowThisFunc(("\n"));
6940
6941 /* sanity check */
6942 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6943
6944 /* If the machine has an USB controller, ask the USB proxy service to
6945 * capture devices */
6946 PPDMIBASE pBase;
6947 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
6948 if (RT_SUCCESS(vrc))
6949 {
6950 /* leave the lock before calling Host in VBoxSVC since Host may call
6951 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6952 * produce an inter-process dead-lock otherwise. */
6953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6954 alock.leave();
6955
6956 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6957 ComAssertComRCRetRC(hrc);
6958 }
6959 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6960 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6961 vrc = VINF_SUCCESS;
6962 else
6963 AssertRC(vrc);
6964
6965 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
6966}
6967
6968
6969/**
6970 * Detach all USB device which are attached to the VM for the
6971 * purpose of clean up and such like.
6972 *
6973 * @note The caller must lock this object for writing.
6974 */
6975void Console::detachAllUSBDevices(bool aDone)
6976{
6977 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6978
6979 /* sanity check */
6980 AssertReturnVoid(isWriteLockOnCurrentThread());
6981
6982 mUSBDevices.clear();
6983
6984 /* leave the lock before calling Host in VBoxSVC since Host may call
6985 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6986 * produce an inter-process dead-lock otherwise. */
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 alock.leave();
6989
6990 mControl->DetachAllUSBDevices(aDone);
6991}
6992
6993/**
6994 * @note Locks this object for writing.
6995 */
6996void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6997{
6998 LogFlowThisFuncEnter();
6999 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7000
7001 AutoCaller autoCaller(this);
7002 if (!autoCaller.isOk())
7003 {
7004 /* Console has been already uninitialized, deny request */
7005 AssertMsgFailed(("Console is already uninitialized\n"));
7006 LogFlowThisFunc(("Console is already uninitialized\n"));
7007 LogFlowThisFuncLeave();
7008 return;
7009 }
7010
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012
7013 /*
7014 * Mark all existing remote USB devices as dirty.
7015 */
7016 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7017 it != mRemoteUSBDevices.end();
7018 ++it)
7019 {
7020 (*it)->dirty(true);
7021 }
7022
7023 /*
7024 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7025 */
7026 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7027 VRDPUSBDEVICEDESC *e = pDevList;
7028
7029 /* The cbDevList condition must be checked first, because the function can
7030 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7031 */
7032 while (cbDevList >= 2 && e->oNext)
7033 {
7034 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7035 e->idVendor, e->idProduct,
7036 e->oProduct? (char *)e + e->oProduct: ""));
7037
7038 bool fNewDevice = true;
7039
7040 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7041 it != mRemoteUSBDevices.end();
7042 ++it)
7043 {
7044 if ((*it)->devId() == e->id
7045 && (*it)->clientId() == u32ClientId)
7046 {
7047 /* The device is already in the list. */
7048 (*it)->dirty(false);
7049 fNewDevice = false;
7050 break;
7051 }
7052 }
7053
7054 if (fNewDevice)
7055 {
7056 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7057 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7058
7059 /* Create the device object and add the new device to list. */
7060 ComObjPtr<RemoteUSBDevice> device;
7061 device.createObject();
7062 device->init(u32ClientId, e);
7063
7064 mRemoteUSBDevices.push_back(device);
7065
7066 /* Check if the device is ok for current USB filters. */
7067 BOOL fMatched = FALSE;
7068 ULONG fMaskedIfs = 0;
7069
7070 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7071
7072 AssertComRC(hrc);
7073
7074 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7075
7076 if (fMatched)
7077 {
7078 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7079
7080 /// @todo (r=dmik) warning reporting subsystem
7081
7082 if (hrc == S_OK)
7083 {
7084 LogFlowThisFunc(("Device attached\n"));
7085 device->captured(true);
7086 }
7087 }
7088 }
7089
7090 if (cbDevList < e->oNext)
7091 {
7092 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7093 cbDevList, e->oNext));
7094 break;
7095 }
7096
7097 cbDevList -= e->oNext;
7098
7099 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7100 }
7101
7102 /*
7103 * Remove dirty devices, that is those which are not reported by the server anymore.
7104 */
7105 for (;;)
7106 {
7107 ComObjPtr<RemoteUSBDevice> device;
7108
7109 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7110 while (it != mRemoteUSBDevices.end())
7111 {
7112 if ((*it)->dirty())
7113 {
7114 device = *it;
7115 break;
7116 }
7117
7118 ++ it;
7119 }
7120
7121 if (!device)
7122 {
7123 break;
7124 }
7125
7126 USHORT vendorId = 0;
7127 device->COMGETTER(VendorId)(&vendorId);
7128
7129 USHORT productId = 0;
7130 device->COMGETTER(ProductId)(&productId);
7131
7132 Bstr product;
7133 device->COMGETTER(Product)(product.asOutParam());
7134
7135 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7136 vendorId, productId, product.raw()));
7137
7138 /* Detach the device from VM. */
7139 if (device->captured())
7140 {
7141 Bstr uuid;
7142 device->COMGETTER(Id)(uuid.asOutParam());
7143 onUSBDeviceDetach(uuid, NULL);
7144 }
7145
7146 /* And remove it from the list. */
7147 mRemoteUSBDevices.erase(it);
7148 }
7149
7150 LogFlowThisFuncLeave();
7151}
7152
7153/**
7154 * Thread function which starts the VM (also from saved state) and
7155 * track progress.
7156 *
7157 * @param Thread The thread id.
7158 * @param pvUser Pointer to a VMPowerUpTask structure.
7159 * @return VINF_SUCCESS (ignored).
7160 *
7161 * @note Locks the Console object for writing.
7162 */
7163/*static*/
7164DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7165{
7166 LogFlowFuncEnter();
7167
7168 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7169 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7170
7171 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7172 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7173
7174#if defined(RT_OS_WINDOWS)
7175 {
7176 /* initialize COM */
7177 HRESULT hrc = CoInitializeEx(NULL,
7178 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7179 COINIT_SPEED_OVER_MEMORY);
7180 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7181 }
7182#endif
7183
7184 HRESULT rc = S_OK;
7185 int vrc = VINF_SUCCESS;
7186
7187 /* Set up a build identifier so that it can be seen from core dumps what
7188 * exact build was used to produce the core. */
7189 static char saBuildID[40];
7190 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7191 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7192
7193 ComObjPtr<Console> console = task->mConsole;
7194
7195 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7196
7197 /* The lock is also used as a signal from the task initiator (which
7198 * releases it only after RTThreadCreate()) that we can start the job */
7199 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7200
7201 /* sanity */
7202 Assert(console->mpVM == NULL);
7203
7204 try
7205 {
7206 /* wait for auto reset ops to complete so that we can successfully lock
7207 * the attached hard disks by calling LockMedia() below */
7208 for (VMPowerUpTask::ProgressList::const_iterator
7209 it = task->hardDiskProgresses.begin();
7210 it != task->hardDiskProgresses.end(); ++ it)
7211 {
7212 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7213 AssertComRC(rc2);
7214 }
7215
7216 /*
7217 * Lock attached media. This method will also check their accessibility.
7218 * If we're a teleporter, we'll have to postpone this action so we can
7219 * migrate between local processes.
7220 *
7221 * Note! The media will be unlocked automatically by
7222 * SessionMachine::setMachineState() when the VM is powered down.
7223 */
7224 if (!task->mTeleporterEnabled)
7225 {
7226 rc = console->mControl->LockMedia();
7227 if (FAILED(rc)) throw rc;
7228 }
7229
7230#ifdef VBOX_WITH_VRDP
7231
7232 /* Create the VRDP server. In case of headless operation, this will
7233 * also create the framebuffer, required at VM creation.
7234 */
7235 ConsoleVRDPServer *server = console->consoleVRDPServer();
7236 Assert(server);
7237
7238 /* Does VRDP server call Console from the other thread?
7239 * Not sure (and can change), so leave the lock just in case.
7240 */
7241 alock.leave();
7242 vrc = server->Launch();
7243 alock.enter();
7244
7245 if (vrc == VERR_NET_ADDRESS_IN_USE)
7246 {
7247 Utf8Str errMsg;
7248 Bstr bstr;
7249 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7250 Utf8Str ports = bstr;
7251 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7252 ports.raw());
7253 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7254 vrc, errMsg.raw()));
7255 }
7256 else if (RT_FAILURE(vrc))
7257 {
7258 Utf8Str errMsg;
7259 switch (vrc)
7260 {
7261 case VERR_FILE_NOT_FOUND:
7262 {
7263 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7264 break;
7265 }
7266 default:
7267 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7268 vrc);
7269 }
7270 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7271 vrc, errMsg.raw()));
7272 throw setError(E_FAIL, errMsg.c_str());
7273 }
7274
7275#endif /* VBOX_WITH_VRDP */
7276
7277 ComPtr<IMachine> pMachine = console->machine();
7278 ULONG cCpus = 1;
7279 pMachine->COMGETTER(CPUCount)(&cCpus);
7280
7281 /*
7282 * Create the VM
7283 */
7284 PVM pVM;
7285 /*
7286 * leave the lock since EMT will call Console. It's safe because
7287 * mMachineState is either Starting or Restoring state here.
7288 */
7289 alock.leave();
7290
7291 vrc = VMR3Create(cCpus, Console::genericVMSetErrorCallback, &task->mErrorMsg,
7292 task->mConfigConstructor, static_cast<Console *>(console),
7293 &pVM);
7294
7295 alock.enter();
7296
7297#ifdef VBOX_WITH_VRDP
7298 /* Enable client connections to the server. */
7299 console->consoleVRDPServer()->EnableConnections();
7300#endif /* VBOX_WITH_VRDP */
7301
7302 if (RT_SUCCESS(vrc))
7303 {
7304 do
7305 {
7306 /*
7307 * Register our load/save state file handlers
7308 */
7309 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7310 NULL, NULL, NULL,
7311 NULL, saveStateFileExec, NULL,
7312 NULL, loadStateFileExec, NULL,
7313 static_cast<Console *>(console));
7314 AssertRCBreak(vrc);
7315
7316 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7317 AssertRC(vrc);
7318 if (RT_FAILURE(vrc))
7319 break;
7320
7321 /*
7322 * Synchronize debugger settings
7323 */
7324 MachineDebugger *machineDebugger = console->getMachineDebugger();
7325 if (machineDebugger)
7326 {
7327 machineDebugger->flushQueuedSettings();
7328 }
7329
7330 /*
7331 * Shared Folders
7332 */
7333 if (console->getVMMDev()->isShFlActive())
7334 {
7335 /* Does the code below call Console from the other thread?
7336 * Not sure, so leave the lock just in case. */
7337 alock.leave();
7338
7339 for (SharedFolderDataMap::const_iterator
7340 it = task->mSharedFolders.begin();
7341 it != task->mSharedFolders.end();
7342 ++ it)
7343 {
7344 rc = console->createSharedFolder((*it).first, (*it).second);
7345 if (FAILED(rc)) break;
7346 }
7347 if (FAILED(rc)) break;
7348
7349 /* enter the lock again */
7350 alock.enter();
7351 }
7352
7353 /*
7354 * Capture USB devices.
7355 */
7356 rc = console->captureUSBDevices(pVM);
7357 if (FAILED(rc)) break;
7358
7359 /* leave the lock before a lengthy operation */
7360 alock.leave();
7361
7362 /* Load saved state? */
7363 if (task->mSavedStateFile.length())
7364 {
7365 LogFlowFunc(("Restoring saved state from '%s'...\n",
7366 task->mSavedStateFile.raw()));
7367
7368 vrc = VMR3LoadFromFile(pVM,
7369 task->mSavedStateFile.c_str(),
7370 Console::stateProgressCallback,
7371 static_cast<VMProgressTask*>(task.get()));
7372
7373 if (RT_SUCCESS(vrc))
7374 {
7375 if (task->mStartPaused)
7376 /* done */
7377 console->setMachineState(MachineState_Paused);
7378 else
7379 {
7380 /* Start/Resume the VM execution */
7381 vrc = VMR3Resume(pVM);
7382 AssertRC(vrc);
7383 }
7384 }
7385
7386 /* Power off in case we failed loading or resuming the VM */
7387 if (RT_FAILURE(vrc))
7388 {
7389 int vrc2 = VMR3PowerOff(pVM);
7390 AssertRC(vrc2);
7391 }
7392 }
7393 else if (task->mTeleporterEnabled)
7394 {
7395 /* -> ConsoleImplTeleporter.cpp */
7396 bool fPowerOffOnFailure;
7397 rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7398 task->mProgress, &fPowerOffOnFailure);
7399 if (FAILED(rc) && fPowerOffOnFailure)
7400 {
7401 ErrorInfoKeeper eik;
7402 int vrc2 = VMR3PowerOff(pVM);
7403 AssertRC(vrc2);
7404 }
7405 }
7406 else if (task->mStartPaused)
7407 /* done */
7408 console->setMachineState(MachineState_Paused);
7409 else
7410 {
7411 /* Power on the VM (i.e. start executing) */
7412 vrc = VMR3PowerOn(pVM);
7413 AssertRC(vrc);
7414 }
7415
7416 /* enter the lock again */
7417 alock.enter();
7418 }
7419 while (0);
7420
7421 /* On failure, destroy the VM */
7422 if (FAILED(rc) || RT_FAILURE(vrc))
7423 {
7424 /* preserve existing error info */
7425 ErrorInfoKeeper eik;
7426
7427 /* powerDown() will call VMR3Destroy() and do all necessary
7428 * cleanup (VRDP, USB devices) */
7429 HRESULT rc2 = console->powerDown();
7430 AssertComRC(rc2);
7431 }
7432 else
7433 {
7434 /*
7435 * Deregister the VMSetError callback. This is necessary as the
7436 * pfnVMAtError() function passed to VMR3Create() is supposed to
7437 * be sticky but our error callback isn't.
7438 */
7439 alock.leave();
7440 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7441 /** @todo register another VMSetError callback? */
7442 alock.enter();
7443 }
7444 }
7445 else
7446 {
7447 /*
7448 * If VMR3Create() failed it has released the VM memory.
7449 */
7450 console->mpVM = NULL;
7451 }
7452
7453 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7454 {
7455 /* If VMR3Create() or one of the other calls in this function fail,
7456 * an appropriate error message has been set in task->mErrorMsg.
7457 * However since that happens via a callback, the rc status code in
7458 * this function is not updated.
7459 */
7460 if (!task->mErrorMsg.length())
7461 {
7462 /* If the error message is not set but we've got a failure,
7463 * convert the VBox status code into a meaningful error message.
7464 * This becomes unused once all the sources of errors set the
7465 * appropriate error message themselves.
7466 */
7467 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7468 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7469 vrc);
7470 }
7471
7472 /* Set the error message as the COM error.
7473 * Progress::notifyComplete() will pick it up later. */
7474 throw setError(E_FAIL, task->mErrorMsg.c_str());
7475 }
7476 }
7477 catch (HRESULT aRC) { rc = aRC; }
7478
7479 if ( console->mMachineState == MachineState_Starting
7480 || console->mMachineState == MachineState_Restoring
7481 || console->mMachineState == MachineState_TeleportingIn
7482 )
7483 {
7484 /* We are still in the Starting/Restoring state. This means one of:
7485 *
7486 * 1) we failed before VMR3Create() was called;
7487 * 2) VMR3Create() failed.
7488 *
7489 * In both cases, there is no need to call powerDown(), but we still
7490 * need to go back to the PoweredOff/Saved state. Reuse
7491 * vmstateChangeCallback() for that purpose.
7492 */
7493
7494 /* preserve existing error info */
7495 ErrorInfoKeeper eik;
7496
7497 Assert(console->mpVM == NULL);
7498 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7499 console);
7500 }
7501
7502 /*
7503 * Evaluate the final result. Note that the appropriate mMachineState value
7504 * is already set by vmstateChangeCallback() in all cases.
7505 */
7506
7507 /* leave the lock, don't need it any more */
7508 alock.leave();
7509
7510 if (SUCCEEDED(rc))
7511 {
7512 /* Notify the progress object of the success */
7513 task->mProgress->notifyComplete(S_OK);
7514 }
7515 else
7516 {
7517 /* The progress object will fetch the current error info */
7518 task->mProgress->notifyComplete(rc);
7519 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7520 }
7521
7522 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7523 console->mControl->EndPowerUp(rc);
7524
7525#if defined(RT_OS_WINDOWS)
7526 /* uninitialize COM */
7527 CoUninitialize();
7528#endif
7529
7530 LogFlowFuncLeave();
7531
7532 return VINF_SUCCESS;
7533}
7534
7535
7536/**
7537 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7538 *
7539 * @param pConsole Reference to the console object.
7540 * @param pVM The VM handle.
7541 * @param lInstance The instance of the controller.
7542 * @param pcszDevice The name of the controller type.
7543 * @param enmBus The storage bus type of the controller.
7544 * @param fSetupMerge Whether to set up a medium merge
7545 * @param uMergeSource Merge source image index
7546 * @param uMergeTarget Merge target image index
7547 * @param aMediumAtt The medium attachment.
7548 * @param aMachineState The current machine state.
7549 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7550 * @return VBox status code.
7551 */
7552/* static */
7553DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7554 PVM pVM,
7555 const char *pcszDevice,
7556 unsigned uInstance,
7557 StorageBus_T enmBus,
7558 bool fUseHostIOCache,
7559 bool fSetupMerge,
7560 unsigned uMergeSource,
7561 unsigned uMergeTarget,
7562 IMediumAttachment *aMediumAtt,
7563 MachineState_T aMachineState,
7564 HRESULT *phrc)
7565{
7566 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7567
7568 int rc;
7569 HRESULT hrc;
7570 Bstr bstr;
7571 *phrc = S_OK;
7572#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7573#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7574
7575 /* Ignore attachments other than hard disks, since at the moment they are
7576 * not subject to snapshotting in general. */
7577 DeviceType_T lType;
7578 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7579 if (lType != DeviceType_HardDisk)
7580 return VINF_SUCCESS;
7581
7582 /* Determine the base path for the device instance. */
7583 PCFGMNODE pCtlInst;
7584 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7585 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7586
7587 /* Update the device instance configuration. */
7588 rc = pConsole->configMediumAttachment(pCtlInst,
7589 pcszDevice,
7590 uInstance,
7591 enmBus,
7592 fUseHostIOCache,
7593 fSetupMerge,
7594 uMergeSource,
7595 uMergeTarget,
7596 aMediumAtt,
7597 aMachineState,
7598 phrc,
7599 true /* fAttachDetach */,
7600 false /* fForceUnmount */,
7601 pVM,
7602 NULL /* paLedDevType */);
7603 /** @todo this dumps everything attached to this device instance, which
7604 * is more than necessary. Dumping the changed LUN would be enough. */
7605 CFGMR3Dump(pCtlInst);
7606 RC_CHECK();
7607
7608#undef RC_CHECK
7609#undef H
7610
7611 LogFlowFunc(("Returns success\n"));
7612 return VINF_SUCCESS;
7613}
7614
7615/**
7616 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7617 */
7618static void takesnapshotProgressCancelCallback(void *pvUser)
7619{
7620 PVM pVM = (PVM)pvUser;
7621 SSMR3Cancel(pVM);
7622}
7623
7624/**
7625 * Worker thread created by Console::TakeSnapshot.
7626 * @param Thread The current thread (ignored).
7627 * @param pvUser The task.
7628 * @return VINF_SUCCESS (ignored).
7629 */
7630/*static*/
7631DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7632{
7633 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7634
7635 // taking a snapshot consists of the following:
7636
7637 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7638 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7639 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7640 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7641 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7642
7643 Console *that = pTask->mConsole;
7644 bool fBeganTakingSnapshot = false;
7645 bool fSuspenededBySave = false;
7646
7647 AutoCaller autoCaller(that);
7648 if (FAILED(autoCaller.rc()))
7649 {
7650 that->mptrCancelableProgress.setNull();
7651 return autoCaller.rc();
7652 }
7653
7654 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7655
7656 HRESULT rc = S_OK;
7657
7658 try
7659 {
7660 /* STEP 1 + 2:
7661 * request creating the diff images on the server and create the snapshot object
7662 * (this will set the machine state to Saving on the server to block
7663 * others from accessing this machine)
7664 */
7665 rc = that->mControl->BeginTakingSnapshot(that,
7666 pTask->bstrName,
7667 pTask->bstrDescription,
7668 pTask->mProgress,
7669 pTask->fTakingSnapshotOnline,
7670 pTask->bstrSavedStateFile.asOutParam());
7671 if (FAILED(rc))
7672 throw rc;
7673
7674 fBeganTakingSnapshot = true;
7675
7676 /*
7677 * state file is non-null only when the VM is paused
7678 * (i.e. creating a snapshot online)
7679 */
7680 ComAssertThrow( (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7681 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline),
7682 rc = E_FAIL);
7683
7684 /* sync the state with the server */
7685 if (pTask->lastMachineState == MachineState_Running)
7686 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7687 else
7688 that->setMachineStateLocally(MachineState_Saving);
7689
7690 // STEP 3: save the VM state (if online)
7691 if (pTask->fTakingSnapshotOnline)
7692 {
7693 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7694
7695 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7696 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7697 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7698
7699 alock.leave();
7700 LogFlowFunc(("VMR3Save...\n"));
7701 int vrc = VMR3Save(that->mpVM,
7702 strSavedStateFile.c_str(),
7703 true /*fContinueAfterwards*/,
7704 Console::stateProgressCallback,
7705 (void*)pTask,
7706 &fSuspenededBySave);
7707 alock.enter();
7708 if (RT_FAILURE(vrc))
7709 throw setError(E_FAIL,
7710 tr("Failed to save the machine state to '%s' (%Rrc)"),
7711 strSavedStateFile.c_str(), vrc);
7712
7713 pTask->mProgress->setCancelCallback(NULL, NULL);
7714 if (!pTask->mProgress->notifyPointOfNoReturn())
7715 throw setError(E_FAIL, tr("Cancelled"));
7716 that->mptrCancelableProgress.setNull();
7717
7718 // STEP 4: reattach hard disks
7719 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7720
7721 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7722 1); // operation weight, same as computed when setting up progress object
7723
7724 com::SafeIfaceArray<IMediumAttachment> atts;
7725 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7726 if (FAILED(rc))
7727 throw rc;
7728
7729 for (size_t i = 0;
7730 i < atts.size();
7731 ++i)
7732 {
7733 ComPtr<IStorageController> controller;
7734 BSTR controllerName;
7735 ULONG lInstance;
7736 StorageControllerType_T enmController;
7737 StorageBus_T enmBus;
7738 BOOL fUseHostIOCache;
7739
7740 /*
7741 * We can't pass a storage controller object directly
7742 * (g++ complains about not being able to pass non POD types through '...')
7743 * so we have to query needed values here and pass them.
7744 */
7745 rc = atts[i]->COMGETTER(Controller)(&controllerName);
7746 if (FAILED(rc))
7747 throw rc;
7748
7749 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7750 if (FAILED(rc))
7751 throw rc;
7752
7753 rc = controller->COMGETTER(ControllerType)(&enmController);
7754 if (FAILED(rc))
7755 throw rc;
7756 rc = controller->COMGETTER(Instance)(&lInstance);
7757 if (FAILED(rc))
7758 throw rc;
7759 rc = controller->COMGETTER(Bus)(&enmBus);
7760 if (FAILED(rc))
7761 throw rc;
7762 rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
7763 if (FAILED(rc))
7764 throw rc;
7765
7766 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7767
7768 /*
7769 * don't leave the lock since reconfigureMediumAttachment
7770 * isn't going to need the Console lock.
7771 */
7772 vrc = VMR3ReqCallWait(that->mpVM,
7773 VMCPUID_ANY,
7774 (PFNRT)reconfigureMediumAttachment,
7775 12,
7776 that,
7777 that->mpVM,
7778 pcszDevice,
7779 lInstance,
7780 enmBus,
7781 fUseHostIOCache,
7782 false /* fSetupMerge */,
7783 0 /* uMergeSource */,
7784 0 /* uMergeTarget */,
7785 atts[i],
7786 that->mMachineState,
7787 &rc);
7788 if (RT_FAILURE(vrc))
7789 throw setError(E_FAIL, Console::tr("%Rrc"), vrc);
7790 if (FAILED(rc))
7791 throw rc;
7792 }
7793 }
7794
7795 /*
7796 * finalize the requested snapshot object.
7797 * This will reset the machine state to the state it had right
7798 * before calling mControl->BeginTakingSnapshot().
7799 */
7800 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
7801 // do not throw rc here because we can't call EndTakingSnapshot() twice
7802 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7803 }
7804 catch (HRESULT rcThrown)
7805 {
7806 /* preserve existing error info */
7807 ErrorInfoKeeper eik;
7808
7809 if (fBeganTakingSnapshot)
7810 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
7811
7812 rc = rcThrown;
7813 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7814 }
7815 Assert(alock.isWriteLockOnCurrentThread());
7816
7817 if (FAILED(rc)) /* Must come before calling setMachineState. */
7818 pTask->mProgress->notifyComplete(rc);
7819
7820 /*
7821 * Fix up the machine state.
7822 *
7823 * For live snapshots we do all the work, for the two other variantions we
7824 * just update the local copy.
7825 */
7826 MachineState_T enmMachineState;
7827 that->mMachine->COMGETTER(State)(&enmMachineState);
7828 if ( that->mMachineState == MachineState_LiveSnapshotting
7829 || that->mMachineState == MachineState_Saving)
7830 {
7831
7832 if (!pTask->fTakingSnapshotOnline)
7833 that->setMachineStateLocally(pTask->lastMachineState);
7834 else if (SUCCEEDED(rc))
7835 {
7836 Assert( pTask->lastMachineState == MachineState_Running
7837 || pTask->lastMachineState == MachineState_Paused);
7838 Assert(that->mMachineState == MachineState_Saving);
7839 if (pTask->lastMachineState == MachineState_Running)
7840 {
7841 LogFlowFunc(("VMR3Resume...\n"));
7842 alock.leave();
7843 int vrc = VMR3Resume(that->mpVM);
7844 alock.enter();
7845 if (RT_FAILURE(vrc))
7846 {
7847 rc = setError(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
7848 pTask->mProgress->notifyComplete(rc);
7849 if (that->mMachineState == MachineState_Saving)
7850 that->setMachineStateLocally(MachineState_Paused);
7851 }
7852 }
7853 else
7854 that->setMachineStateLocally(MachineState_Paused);
7855 }
7856 else
7857 {
7858 /** @todo this could probably be made more generic and reused elsewhere. */
7859 /* paranoid cleanup on for a failed online snapshot. */
7860 VMSTATE enmVMState = VMR3GetState(that->mpVM);
7861 switch (enmVMState)
7862 {
7863 case VMSTATE_RUNNING:
7864 case VMSTATE_RUNNING_LS:
7865 case VMSTATE_DEBUGGING:
7866 case VMSTATE_DEBUGGING_LS:
7867 case VMSTATE_POWERING_OFF:
7868 case VMSTATE_POWERING_OFF_LS:
7869 case VMSTATE_RESETTING:
7870 case VMSTATE_RESETTING_LS:
7871 Assert(!fSuspenededBySave);
7872 that->setMachineState(MachineState_Running);
7873 break;
7874
7875 case VMSTATE_GURU_MEDITATION:
7876 case VMSTATE_GURU_MEDITATION_LS:
7877 that->setMachineState(MachineState_Stuck);
7878 break;
7879
7880 case VMSTATE_FATAL_ERROR:
7881 case VMSTATE_FATAL_ERROR_LS:
7882 if (pTask->lastMachineState == MachineState_Paused)
7883 that->setMachineStateLocally(pTask->lastMachineState);
7884 else
7885 that->setMachineState(MachineState_Paused);
7886 break;
7887
7888 default:
7889 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
7890 case VMSTATE_SUSPENDED:
7891 case VMSTATE_SUSPENDED_LS:
7892 case VMSTATE_SUSPENDING:
7893 case VMSTATE_SUSPENDING_LS:
7894 case VMSTATE_SUSPENDING_EXT_LS:
7895 if (fSuspenededBySave)
7896 {
7897 Assert(pTask->lastMachineState == MachineState_Running);
7898 LogFlowFunc(("VMR3Resume (on failure)...\n"));
7899 alock.leave();
7900 int vrc = VMR3Resume(that->mpVM);
7901 alock.enter();
7902 AssertLogRelRC(vrc);
7903 if (RT_FAILURE(vrc))
7904 that->setMachineState(MachineState_Paused);
7905 }
7906 else if (pTask->lastMachineState == MachineState_Paused)
7907 that->setMachineStateLocally(pTask->lastMachineState);
7908 else
7909 that->setMachineState(MachineState_Paused);
7910 break;
7911 }
7912
7913 }
7914 }
7915 /*else: somebody else has change the state... Leave it. */
7916
7917 /* check the remote state to see that we got it right. */
7918 that->mMachine->COMGETTER(State)(&enmMachineState);
7919 AssertLogRelMsg(that->mMachineState == enmMachineState,
7920 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
7921 Global::stringifyMachineState(enmMachineState) ));
7922
7923
7924 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
7925 pTask->mProgress->notifyComplete(rc);
7926
7927 delete pTask;
7928
7929 LogFlowFuncLeave();
7930 return VINF_SUCCESS;
7931}
7932
7933/**
7934 * Thread for executing the saved state operation.
7935 *
7936 * @param Thread The thread handle.
7937 * @param pvUser Pointer to a VMSaveTask structure.
7938 * @return VINF_SUCCESS (ignored).
7939 *
7940 * @note Locks the Console object for writing.
7941 */
7942/*static*/
7943DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
7944{
7945 LogFlowFuncEnter();
7946
7947 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
7948 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7949
7950 Assert(task->mSavedStateFile.length());
7951 Assert(!task->mProgress.isNull());
7952
7953 const ComObjPtr<Console> &that = task->mConsole;
7954 Utf8Str errMsg;
7955 HRESULT rc = S_OK;
7956
7957 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7958
7959 bool fSuspenededBySave;
7960 int vrc = VMR3Save(that->mpVM,
7961 task->mSavedStateFile.c_str(),
7962 false, /*fContinueAfterwards*/
7963 Console::stateProgressCallback,
7964 static_cast<VMProgressTask*>(task.get()),
7965 &fSuspenededBySave);
7966 if (RT_FAILURE(vrc))
7967 {
7968 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
7969 task->mSavedStateFile.raw(), vrc);
7970 rc = E_FAIL;
7971 }
7972 Assert(!fSuspenededBySave);
7973
7974 /* lock the console once we're going to access it */
7975 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
7976
7977 /*
7978 * finalize the requested save state procedure.
7979 * In case of success, the server will set the machine state to Saved;
7980 * in case of failure it will reset the it to the state it had right
7981 * before calling mControl->BeginSavingState().
7982 */
7983 that->mControl->EndSavingState(SUCCEEDED(rc));
7984
7985 /* synchronize the state with the server */
7986 if (!FAILED(rc))
7987 {
7988 /*
7989 * The machine has been successfully saved, so power it down
7990 * (vmstateChangeCallback() will set state to Saved on success).
7991 * Note: we release the task's VM caller, otherwise it will
7992 * deadlock.
7993 */
7994 task->releaseVMCaller();
7995
7996 rc = that->powerDown();
7997 }
7998
7999 /* notify the progress object about operation completion */
8000 if (SUCCEEDED(rc))
8001 task->mProgress->notifyComplete(S_OK);
8002 else
8003 {
8004 if (errMsg.length())
8005 task->mProgress->notifyComplete(rc,
8006 COM_IIDOF(IConsole),
8007 (CBSTR)Console::getComponentName(),
8008 errMsg.c_str());
8009 else
8010 task->mProgress->notifyComplete(rc);
8011 }
8012
8013 LogFlowFuncLeave();
8014 return VINF_SUCCESS;
8015}
8016
8017/**
8018 * Thread for powering down the Console.
8019 *
8020 * @param Thread The thread handle.
8021 * @param pvUser Pointer to the VMTask structure.
8022 * @return VINF_SUCCESS (ignored).
8023 *
8024 * @note Locks the Console object for writing.
8025 */
8026/*static*/
8027DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8028{
8029 LogFlowFuncEnter();
8030
8031 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8032 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8033
8034 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8035
8036 const ComObjPtr<Console> &that = task->mConsole;
8037
8038 /* Note: no need to use addCaller() to protect Console because VMTask does
8039 * that */
8040
8041 /* wait until the method tat started us returns */
8042 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8043
8044 /* release VM caller to avoid the powerDown() deadlock */
8045 task->releaseVMCaller();
8046
8047 that->powerDown(task->mProgress);
8048
8049 LogFlowFuncLeave();
8050 return VINF_SUCCESS;
8051}
8052
8053/**
8054 * The Main status driver instance data.
8055 */
8056typedef struct DRVMAINSTATUS
8057{
8058 /** The LED connectors. */
8059 PDMILEDCONNECTORS ILedConnectors;
8060 /** Pointer to the LED ports interface above us. */
8061 PPDMILEDPORTS pLedPorts;
8062 /** Pointer to the array of LED pointers. */
8063 PPDMLED *papLeds;
8064 /** The unit number corresponding to the first entry in the LED array. */
8065 RTUINT iFirstLUN;
8066 /** The unit number corresponding to the last entry in the LED array.
8067 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8068 RTUINT iLastLUN;
8069} DRVMAINSTATUS, *PDRVMAINSTATUS;
8070
8071
8072/**
8073 * Notification about a unit which have been changed.
8074 *
8075 * The driver must discard any pointers to data owned by
8076 * the unit and requery it.
8077 *
8078 * @param pInterface Pointer to the interface structure containing the called function pointer.
8079 * @param iLUN The unit number.
8080 */
8081DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8082{
8083 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8084 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8085 {
8086 PPDMLED pLed;
8087 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8088 if (RT_FAILURE(rc))
8089 pLed = NULL;
8090 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8091 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8092 }
8093}
8094
8095
8096/**
8097 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8098 */
8099DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8100{
8101 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8102 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8103 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8104 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8105 return NULL;
8106}
8107
8108
8109/**
8110 * Destruct a status driver instance.
8111 *
8112 * @returns VBox status.
8113 * @param pDrvIns The driver instance data.
8114 */
8115DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8116{
8117 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8118 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8119 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8120
8121 if (pData->papLeds)
8122 {
8123 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8124 while (iLed-- > 0)
8125 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL);
8126 }
8127}
8128
8129
8130/**
8131 * Construct a status driver instance.
8132 *
8133 * @copydoc FNPDMDRVCONSTRUCT
8134 */
8135DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8136{
8137 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8138 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8139 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8140
8141 /*
8142 * Validate configuration.
8143 */
8144 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8145 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8146 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8147 ("Configuration error: Not possible to attach anything to this driver!\n"),
8148 VERR_PDM_DRVINS_NO_ATTACH);
8149
8150 /*
8151 * Data.
8152 */
8153 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8154 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8155
8156 /*
8157 * Read config.
8158 */
8159 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8160 if (RT_FAILURE(rc))
8161 {
8162 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8163 return rc;
8164 }
8165
8166 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8167 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8168 pData->iFirstLUN = 0;
8169 else if (RT_FAILURE(rc))
8170 {
8171 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8172 return rc;
8173 }
8174
8175 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8176 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8177 pData->iLastLUN = 0;
8178 else if (RT_FAILURE(rc))
8179 {
8180 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8181 return rc;
8182 }
8183 if (pData->iFirstLUN > pData->iLastLUN)
8184 {
8185 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8186 return VERR_GENERAL_FAILURE;
8187 }
8188
8189 /*
8190 * Get the ILedPorts interface of the above driver/device and
8191 * query the LEDs we want.
8192 */
8193 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8194 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8195 VERR_PDM_MISSING_INTERFACE_ABOVE);
8196
8197 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8198 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8199
8200 return VINF_SUCCESS;
8201}
8202
8203
8204/**
8205 * Keyboard driver registration record.
8206 */
8207const PDMDRVREG Console::DrvStatusReg =
8208{
8209 /* u32Version */
8210 PDM_DRVREG_VERSION,
8211 /* szName */
8212 "MainStatus",
8213 /* szRCMod */
8214 "",
8215 /* szR0Mod */
8216 "",
8217 /* pszDescription */
8218 "Main status driver (Main as in the API).",
8219 /* fFlags */
8220 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8221 /* fClass. */
8222 PDM_DRVREG_CLASS_STATUS,
8223 /* cMaxInstances */
8224 ~0,
8225 /* cbInstance */
8226 sizeof(DRVMAINSTATUS),
8227 /* pfnConstruct */
8228 Console::drvStatus_Construct,
8229 /* pfnDestruct */
8230 Console::drvStatus_Destruct,
8231 /* pfnRelocate */
8232 NULL,
8233 /* pfnIOCtl */
8234 NULL,
8235 /* pfnPowerOn */
8236 NULL,
8237 /* pfnReset */
8238 NULL,
8239 /* pfnSuspend */
8240 NULL,
8241 /* pfnResume */
8242 NULL,
8243 /* pfnAttach */
8244 NULL,
8245 /* pfnDetach */
8246 NULL,
8247 /* pfnPowerOff */
8248 NULL,
8249 /* pfnSoftReset */
8250 NULL,
8251 /* u32EndVersion */
8252 PDM_DRVREG_VERSION
8253};
8254
8255/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use