VirtualBox

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

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

Main: bring back boolean argument to IConsole::discardSavedState()

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

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