VirtualBox

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

最後變更 在這個檔案從32827是 32827,由 vboxsync 提交於 14 年 前

Main/Guest+Console: fix lock ordering issues, and mark code where this still needs to be done

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

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