VirtualBox

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

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

com/string: Solaris build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 276.0 KB
Line 
1/* $Id: ConsoleImpl.cpp 32730 2010-09-23 14:49:25Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @todo Move the TAP mess back into the driver! */
19#if defined(RT_OS_WINDOWS)
20#elif defined(RT_OS_LINUX)
21# include <errno.h>
22# include <sys/ioctl.h>
23# include <sys/poll.h>
24# include <sys/fcntl.h>
25# include <sys/types.h>
26# include <sys/wait.h>
27# include <net/if.h>
28# include <linux/if_tun.h>
29# include <stdio.h>
30# include <stdlib.h>
31# include <string.h>
32#elif defined(RT_OS_FREEBSD)
33# include <errno.h>
34# include <sys/ioctl.h>
35# include <sys/poll.h>
36# include <sys/fcntl.h>
37# include <sys/types.h>
38# include <sys/wait.h>
39# include <stdio.h>
40# include <stdlib.h>
41# include <string.h>
42#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 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 if ( mMachineState != MachineState_Running
1825 && mMachineState != MachineState_Teleporting
1826 && mMachineState != MachineState_LiveSnapshotting
1827 )
1828 return setInvalidMachineStateError();
1829
1830 /* protect mpVM */
1831 AutoVMCaller autoVMCaller(this);
1832 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
1833
1834 /* Check if the CPU is present */
1835 BOOL fCpuAttached;
1836 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
1837 if (FAILED(rc)) return rc;
1838
1839 if (!fCpuAttached)
1840 return setError(E_FAIL,
1841 tr("CPU %d is not attached"), aCpu);
1842
1843 /* Check if the CPU is unlocked */
1844 PPDMIBASE pBase;
1845 int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, aCpu, &pBase);
1846 bool fLocked = true;
1847 if (RT_SUCCESS(vrc))
1848 {
1849 uint32_t idCpuCore, idCpuPackage;
1850
1851 /* Notify the guest if possible. */
1852 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage);
1853 AssertRC(vrc);
1854
1855 Assert(pBase);
1856
1857 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
1858
1859 vrc = getVMMDev()->getVMMDevPort()->pfnCpuHotUnplug(getVMMDev()->getVMMDevPort(), idCpuCore, idCpuPackage);
1860 if (RT_SUCCESS(vrc))
1861 {
1862 unsigned cTries = 100;
1863
1864 do
1865 {
1866 /* It will take some time until the event is processed in the guest. Wait */
1867 vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
1868
1869 if (RT_SUCCESS(vrc) && !fLocked)
1870 break;
1871
1872 /* Sleep a bit */
1873 RTThreadSleep(100);
1874 } while (cTries-- > 0);
1875 }
1876 else if (vrc == VERR_CPU_HOTPLUG_NOT_MONITORED_BY_GUEST)
1877 {
1878 /* Query one time. It is possible that the user ejected the CPU. */
1879 vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
1880 }
1881 }
1882
1883 /* If the CPU was unlocked we can detach it now. */
1884 if (RT_SUCCESS(vrc) && !fLocked)
1885 {
1886 /*
1887 * Call worker in EMT, that's faster and safer than doing everything
1888 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
1889 * here to make requests from under the lock in order to serialize them.
1890 */
1891 PVMREQ pReq;
1892 vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
1893 (PFNRT)Console::unplugCpu, 2,
1894 this, aCpu);
1895
1896 /* leave the lock before a VMR3* call (EMT will call us back)! */
1897 alock.leave();
1898
1899 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
1900 {
1901 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
1902 AssertRC(vrc);
1903 if (RT_SUCCESS(vrc))
1904 vrc = pReq->iStatus;
1905 }
1906 VMR3ReqFree(pReq);
1907
1908 if (RT_SUCCESS(vrc))
1909 {
1910 /* Detach it from the VM */
1911 vrc = VMR3HotUnplugCpu(mpVM, aCpu);
1912 AssertRC(vrc);
1913 }
1914 else
1915 rc = setError(VBOX_E_VM_ERROR,
1916 tr("Hot-Remove failed (rc=%Rrc)"), vrc);
1917 }
1918 else
1919 rc = setError(VBOX_E_VM_ERROR,
1920 tr("Hot-Remove was aborted because the CPU may still be used by the guest"), VERR_RESOURCE_BUSY);
1921
1922 LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
1923 LogFlowThisFuncLeave();
1924 return rc;
1925}
1926
1927DECLCALLBACK(int) Console::plugCpu(Console *pThis, unsigned uCpu)
1928{
1929 LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
1930
1931 AssertReturn(pThis, VERR_INVALID_PARAMETER);
1932
1933 int rc = VMR3HotPlugCpu(pThis->mpVM, uCpu);
1934 AssertRC(rc);
1935
1936 PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pThis->mpVM), "Devices/acpi/0/");
1937 AssertRelease(pInst);
1938 /* nuke anything which might have been left behind. */
1939 CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu));
1940
1941#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
1942
1943 PCFGMNODE pLunL0;
1944 PCFGMNODE pCfg;
1945 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", uCpu); RC_CHECK();
1946 rc = CFGMR3InsertString(pLunL0, "Driver", "ACPICpu"); RC_CHECK();
1947 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
1948
1949 /*
1950 * Attach the driver.
1951 */
1952 PPDMIBASE pBase;
1953 rc = PDMR3DeviceAttach(pThis->mpVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK();
1954
1955 Log(("PlugCpu: rc=%Rrc\n", rc));
1956
1957 CFGMR3Dump(pInst);
1958
1959#undef RC_CHECK
1960
1961 return VINF_SUCCESS;
1962}
1963
1964HRESULT Console::doCPUAdd(ULONG aCpu)
1965{
1966 HRESULT rc = S_OK;
1967
1968 LogFlowThisFuncEnter();
1969 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
1970
1971 AutoCaller autoCaller(this);
1972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1973
1974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 if ( mMachineState != MachineState_Running
1977 && mMachineState != MachineState_Teleporting
1978 && mMachineState != MachineState_LiveSnapshotting
1979 /** @todo r=bird: This should be allowed on paused VMs as well. Later. */
1980 )
1981 return setInvalidMachineStateError();
1982
1983 /* protect mpVM */
1984 AutoVMCaller autoVMCaller(this);
1985 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
1986
1987 /* Check if the CPU is present */
1988 BOOL fCpuAttached;
1989 rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
1990 if (FAILED(rc)) return rc;
1991
1992 if (fCpuAttached)
1993 return setError(E_FAIL,
1994 tr("CPU %d is already attached"), aCpu);
1995
1996 /*
1997 * Call worker in EMT, that's faster and safer than doing everything
1998 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
1999 * here to make requests from under the lock in order to serialize them.
2000 */
2001 PVMREQ pReq;
2002 int vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
2003 (PFNRT)Console::plugCpu, 2,
2004 this, aCpu);
2005
2006 /* leave the lock before a VMR3* call (EMT will call us back)! */
2007 alock.leave();
2008
2009 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
2010 {
2011 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
2012 AssertRC(vrc);
2013 if (RT_SUCCESS(vrc))
2014 vrc = pReq->iStatus;
2015 }
2016 VMR3ReqFree(pReq);
2017
2018 rc = RT_SUCCESS(vrc) ? S_OK :
2019 setError(VBOX_E_VM_ERROR,
2020 tr("Could not add CPU to the machine (%Rrc)"),
2021 vrc);
2022
2023 if (RT_SUCCESS(vrc))
2024 {
2025 uint32_t idCpuCore, idCpuPackage;
2026
2027 /* Notify the guest if possible. */
2028 vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage);
2029 AssertRC(vrc);
2030
2031 vrc = getVMMDev()->getVMMDevPort()->pfnCpuHotPlug(getVMMDev()->getVMMDevPort(), idCpuCore, idCpuPackage);
2032 /** @todo warning if the guest doesn't support it */
2033 }
2034
2035 LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
2036 LogFlowThisFuncLeave();
2037 return rc;
2038}
2039
2040STDMETHODIMP Console::Pause()
2041{
2042 LogFlowThisFuncEnter();
2043
2044 AutoCaller autoCaller(this);
2045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2046
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 switch (mMachineState)
2050 {
2051 case MachineState_Running:
2052 case MachineState_Teleporting:
2053 case MachineState_LiveSnapshotting:
2054 break;
2055
2056 case MachineState_Paused:
2057 case MachineState_TeleportingPausedVM:
2058 case MachineState_Saving:
2059 return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
2060
2061 default:
2062 return setInvalidMachineStateError();
2063 }
2064
2065 /* protect mpVM */
2066 AutoVMCaller autoVMCaller(this);
2067 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2068
2069 LogFlowThisFunc(("Sending PAUSE request...\n"));
2070
2071 /* leave the lock before a VMR3* call (EMT will call us back)! */
2072 alock.leave();
2073
2074 int vrc = VMR3Suspend(mpVM);
2075
2076 HRESULT hrc = S_OK;
2077 if (RT_FAILURE(vrc))
2078 hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
2079
2080 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
2081 LogFlowThisFuncLeave();
2082 return hrc;
2083}
2084
2085STDMETHODIMP Console::Resume()
2086{
2087 LogFlowThisFuncEnter();
2088
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 if (mMachineState != MachineState_Paused)
2095 return setError(VBOX_E_INVALID_VM_STATE,
2096 tr("Cannot resume the machine as it is not paused (machine state: %s)"),
2097 Global::stringifyMachineState(mMachineState));
2098
2099 /* protect mpVM */
2100 AutoVMCaller autoVMCaller(this);
2101 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2102
2103 LogFlowThisFunc(("Sending RESUME request...\n"));
2104
2105 /* leave the lock before a VMR3* call (EMT will call us back)! */
2106 alock.leave();
2107
2108 int vrc;
2109 if (VMR3GetState(mpVM) == VMSTATE_CREATED)
2110 vrc = VMR3PowerOn(mpVM); /* (PowerUpPaused) */
2111 else
2112 vrc = VMR3Resume(mpVM);
2113
2114 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2115 setError(VBOX_E_VM_ERROR,
2116 tr("Could not resume the machine execution (%Rrc)"),
2117 vrc);
2118
2119 LogFlowThisFunc(("rc=%08X\n", rc));
2120 LogFlowThisFuncLeave();
2121 return rc;
2122}
2123
2124STDMETHODIMP Console::PowerButton()
2125{
2126 LogFlowThisFuncEnter();
2127
2128 AutoCaller autoCaller(this);
2129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2130
2131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2132
2133 if ( mMachineState != MachineState_Running
2134 && mMachineState != MachineState_Teleporting
2135 && mMachineState != MachineState_LiveSnapshotting
2136 )
2137 return setInvalidMachineStateError();
2138
2139 /* protect mpVM */
2140 AutoVMCaller autoVMCaller(this);
2141 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2142
2143 PPDMIBASE pBase;
2144 int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
2145 if (RT_SUCCESS(vrc))
2146 {
2147 Assert(pBase);
2148 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2149 vrc = pPort ? pPort->pfnPowerButtonPress(pPort) : VERR_INVALID_POINTER;
2150 }
2151
2152 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2153 setError(VBOX_E_PDM_ERROR,
2154 tr("Controlled power off failed (%Rrc)"),
2155 vrc);
2156
2157 LogFlowThisFunc(("rc=%08X\n", rc));
2158 LogFlowThisFuncLeave();
2159 return rc;
2160}
2161
2162STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
2163{
2164 LogFlowThisFuncEnter();
2165
2166 CheckComArgOutPointerValid(aHandled);
2167
2168 *aHandled = FALSE;
2169
2170 AutoCaller autoCaller(this);
2171
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 if ( mMachineState != MachineState_Running
2175 && mMachineState != MachineState_Teleporting
2176 && mMachineState != MachineState_LiveSnapshotting
2177 )
2178 return setInvalidMachineStateError();
2179
2180 /* protect mpVM */
2181 AutoVMCaller autoVMCaller(this);
2182 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2183
2184 PPDMIBASE pBase;
2185 int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
2186 bool handled = false;
2187 if (RT_SUCCESS(vrc))
2188 {
2189 Assert(pBase);
2190 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2191 vrc = pPort ? pPort->pfnGetPowerButtonHandled(pPort, &handled) : VERR_INVALID_POINTER;
2192 }
2193
2194 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2195 setError(VBOX_E_PDM_ERROR,
2196 tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"),
2197 vrc);
2198
2199 *aHandled = handled;
2200
2201 LogFlowThisFunc(("rc=%08X\n", rc));
2202 LogFlowThisFuncLeave();
2203 return rc;
2204}
2205
2206STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
2207{
2208 LogFlowThisFuncEnter();
2209
2210 CheckComArgOutPointerValid(aEntered);
2211
2212 *aEntered = FALSE;
2213
2214 AutoCaller autoCaller(this);
2215
2216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2217
2218 if ( mMachineState != MachineState_Running
2219 && mMachineState != MachineState_Teleporting
2220 && mMachineState != MachineState_LiveSnapshotting
2221 )
2222 return setError(VBOX_E_INVALID_VM_STATE,
2223 tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"),
2224 Global::stringifyMachineState(mMachineState));
2225
2226 /* protect mpVM */
2227 AutoVMCaller autoVMCaller(this);
2228 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2229
2230 PPDMIBASE pBase;
2231 int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
2232 bool entered = false;
2233 if (RT_SUCCESS(vrc))
2234 {
2235 Assert(pBase);
2236 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2237 vrc = pPort ? pPort->pfnGetGuestEnteredACPIMode(pPort, &entered) : VERR_INVALID_POINTER;
2238 }
2239
2240 *aEntered = RT_SUCCESS(vrc) ? entered : false;
2241
2242 LogFlowThisFuncLeave();
2243 return S_OK;
2244}
2245
2246STDMETHODIMP Console::SleepButton()
2247{
2248 LogFlowThisFuncEnter();
2249
2250 AutoCaller autoCaller(this);
2251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2252
2253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2254
2255 if (mMachineState != MachineState_Running) /** @todo Live Migration: ??? */
2256 return setInvalidMachineStateError();
2257
2258 /* protect mpVM */
2259 AutoVMCaller autoVMCaller(this);
2260 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2261
2262 PPDMIBASE pBase;
2263 int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
2264 if (RT_SUCCESS(vrc))
2265 {
2266 Assert(pBase);
2267 PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
2268 vrc = pPort ? pPort->pfnSleepButtonPress(pPort) : VERR_INVALID_POINTER;
2269 }
2270
2271 HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
2272 setError(VBOX_E_PDM_ERROR,
2273 tr("Sending sleep button event failed (%Rrc)"),
2274 vrc);
2275
2276 LogFlowThisFunc(("rc=%08X\n", rc));
2277 LogFlowThisFuncLeave();
2278 return rc;
2279}
2280
2281STDMETHODIMP Console::SaveState(IProgress **aProgress)
2282{
2283 LogFlowThisFuncEnter();
2284 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
2285
2286 CheckComArgOutPointerValid(aProgress);
2287
2288 AutoCaller autoCaller(this);
2289 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2290
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 if ( mMachineState != MachineState_Running
2294 && mMachineState != MachineState_Paused)
2295 {
2296 return setError(VBOX_E_INVALID_VM_STATE,
2297 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
2298 Global::stringifyMachineState(mMachineState));
2299 }
2300
2301 /* memorize the current machine state */
2302 MachineState_T lastMachineState = mMachineState;
2303
2304 if (mMachineState == MachineState_Running)
2305 {
2306 HRESULT rc = Pause();
2307 if (FAILED(rc)) return rc;
2308 }
2309
2310 HRESULT rc = S_OK;
2311
2312 /* create a progress object to track operation completion */
2313 ComObjPtr<Progress> progress;
2314 progress.createObject();
2315 progress->init(static_cast<IConsole *>(this),
2316 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
2317 FALSE /* aCancelable */);
2318
2319 bool fBeganSavingState = false;
2320 bool fTaskCreationFailed = false;
2321
2322 do
2323 {
2324 /* create a task object early to ensure mpVM protection is successful */
2325 std::auto_ptr <VMSaveTask> task(new VMSaveTask(this, progress));
2326 rc = task->rc();
2327 /*
2328 * If we fail here it means a PowerDown() call happened on another
2329 * thread while we were doing Pause() (which leaves the Console lock).
2330 * We assign PowerDown() a higher precedence than SaveState(),
2331 * therefore just return the error to the caller.
2332 */
2333 if (FAILED(rc))
2334 {
2335 fTaskCreationFailed = true;
2336 break;
2337 }
2338
2339 Bstr stateFilePath;
2340
2341 /*
2342 * request a saved state file path from the server
2343 * (this will set the machine state to Saving on the server to block
2344 * others from accessing this machine)
2345 */
2346 rc = mControl->BeginSavingState(progress, stateFilePath.asOutParam());
2347 if (FAILED(rc)) break;
2348
2349 fBeganSavingState = true;
2350
2351 /* sync the state with the server */
2352 setMachineStateLocally(MachineState_Saving);
2353
2354 /* ensure the directory for the saved state file exists */
2355 {
2356 Utf8Str dir = stateFilePath;
2357 dir.stripFilename();
2358 if (!RTDirExists(dir.c_str()))
2359 {
2360 int vrc = RTDirCreateFullPath(dir.c_str(), 0777);
2361 if (RT_FAILURE(vrc))
2362 {
2363 rc = setError(VBOX_E_FILE_ERROR,
2364 tr("Could not create a directory '%s' to save the state to (%Rrc)"),
2365 dir.c_str(), vrc);
2366 break;
2367 }
2368 }
2369 }
2370
2371 /* setup task object and thread to carry out the operation asynchronously */
2372 task->mSavedStateFile = stateFilePath;
2373 /* set the state the operation thread will restore when it is finished */
2374 task->mLastMachineState = lastMachineState;
2375
2376 /* create a thread to wait until the VM state is saved */
2377 int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *) task.get(),
2378 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
2379 if (RT_FAILURE(vrc))
2380 {
2381 rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
2382 break;
2383 }
2384
2385 /* task is now owned by saveStateThread(), so release it */
2386 task.release();
2387
2388 /* return the progress to the caller */
2389 progress.queryInterfaceTo(aProgress);
2390 }
2391 while (0);
2392
2393 if (FAILED(rc) && !fTaskCreationFailed)
2394 {
2395 /* preserve existing error info */
2396 ErrorInfoKeeper eik;
2397
2398 if (fBeganSavingState)
2399 {
2400 /*
2401 * cancel the requested save state procedure.
2402 * This will reset the machine state to the state it had right
2403 * before calling mControl->BeginSavingState().
2404 */
2405 mControl->EndSavingState(FALSE);
2406 }
2407
2408 if (lastMachineState == MachineState_Running)
2409 {
2410 /* restore the paused state if appropriate */
2411 setMachineStateLocally(MachineState_Paused);
2412 /* restore the running state if appropriate */
2413 Resume();
2414 }
2415 else
2416 setMachineStateLocally(lastMachineState);
2417 }
2418
2419 LogFlowThisFunc(("rc=%08X\n", rc));
2420 LogFlowThisFuncLeave();
2421 return rc;
2422}
2423
2424STDMETHODIMP Console::AdoptSavedState(IN_BSTR aSavedStateFile)
2425{
2426 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
2427
2428 AutoCaller autoCaller(this);
2429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2430
2431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
2433 if ( mMachineState != MachineState_PoweredOff
2434 && mMachineState != MachineState_Teleported
2435 && mMachineState != MachineState_Aborted
2436 )
2437 return setError(VBOX_E_INVALID_VM_STATE,
2438 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
2439 Global::stringifyMachineState(mMachineState));
2440
2441 return mControl->AdoptSavedState(aSavedStateFile);
2442}
2443
2444STDMETHODIMP Console::DiscardSavedState(BOOL aRemoveFile)
2445{
2446 AutoCaller autoCaller(this);
2447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2448
2449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2450
2451 if (mMachineState != MachineState_Saved)
2452 return setError(VBOX_E_INVALID_VM_STATE,
2453 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
2454 Global::stringifyMachineState(mMachineState));
2455
2456 HRESULT rc = mControl->SetRemoveSavedStateFile(aRemoveFile);
2457 if (FAILED(rc)) return rc;
2458
2459 /*
2460 * Saved -> PoweredOff transition will be detected in the SessionMachine
2461 * and properly handled.
2462 */
2463 rc = setMachineState(MachineState_PoweredOff);
2464
2465 return rc;
2466}
2467
2468/** read the value of a LEd. */
2469inline uint32_t readAndClearLed(PPDMLED pLed)
2470{
2471 if (!pLed)
2472 return 0;
2473 uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;
2474 pLed->Asserted.u32 = 0;
2475 return u32;
2476}
2477
2478STDMETHODIMP Console::GetDeviceActivity(DeviceType_T aDeviceType,
2479 DeviceActivity_T *aDeviceActivity)
2480{
2481 CheckComArgNotNull(aDeviceActivity);
2482
2483 AutoCaller autoCaller(this);
2484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2485
2486 /*
2487 * Note: we don't lock the console object here because
2488 * readAndClearLed() should be thread safe.
2489 */
2490
2491 /* Get LED array to read */
2492 PDMLEDCORE SumLed = {0};
2493 switch (aDeviceType)
2494 {
2495 case DeviceType_Floppy:
2496 case DeviceType_DVD:
2497 case DeviceType_HardDisk:
2498 {
2499 for (unsigned i = 0; i < RT_ELEMENTS(mapStorageLeds); ++i)
2500 if (maStorageDevType[i] == aDeviceType)
2501 SumLed.u32 |= readAndClearLed(mapStorageLeds[i]);
2502 break;
2503 }
2504
2505 case DeviceType_Network:
2506 {
2507 for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); ++i)
2508 SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);
2509 break;
2510 }
2511
2512 case DeviceType_USB:
2513 {
2514 for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); ++i)
2515 SumLed.u32 |= readAndClearLed(mapUSBLed[i]);
2516 break;
2517 }
2518
2519 case DeviceType_SharedFolder:
2520 {
2521 SumLed.u32 |= readAndClearLed(mapSharedFolderLed);
2522 break;
2523 }
2524
2525 default:
2526 return setError(E_INVALIDARG,
2527 tr("Invalid device type: %d"),
2528 aDeviceType);
2529 }
2530
2531 /* Compose the result */
2532 switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))
2533 {
2534 case 0:
2535 *aDeviceActivity = DeviceActivity_Idle;
2536 break;
2537 case PDMLED_READING:
2538 *aDeviceActivity = DeviceActivity_Reading;
2539 break;
2540 case PDMLED_WRITING:
2541 case PDMLED_READING | PDMLED_WRITING:
2542 *aDeviceActivity = DeviceActivity_Writing;
2543 break;
2544 }
2545
2546 return S_OK;
2547}
2548
2549STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId)
2550{
2551#ifdef VBOX_WITH_USB
2552 AutoCaller autoCaller(this);
2553 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2554
2555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2556
2557 if ( mMachineState != MachineState_Running
2558 && mMachineState != MachineState_Paused)
2559 return setError(VBOX_E_INVALID_VM_STATE,
2560 tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"),
2561 Global::stringifyMachineState(mMachineState));
2562
2563 /* protect mpVM */
2564 AutoVMCaller autoVMCaller(this);
2565 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
2566
2567 /* Don't proceed unless we've found the usb controller. */
2568 PPDMIBASE pBase = NULL;
2569 int vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
2570 if (RT_FAILURE(vrc))
2571 return setError(VBOX_E_PDM_ERROR,
2572 tr("The virtual machine does not have a USB controller"));
2573
2574 /* leave the lock because the USB Proxy service may call us back
2575 * (via onUSBDeviceAttach()) */
2576 alock.leave();
2577
2578 /* Request the device capture */
2579 HRESULT rc = mControl->CaptureUSBDevice(aId);
2580 if (FAILED(rc)) return rc;
2581
2582 return rc;
2583
2584#else /* !VBOX_WITH_USB */
2585 return setError(VBOX_E_PDM_ERROR,
2586 tr("The virtual machine does not have a USB controller"));
2587#endif /* !VBOX_WITH_USB */
2588}
2589
2590STDMETHODIMP Console::DetachUSBDevice(IN_BSTR aId, IUSBDevice **aDevice)
2591{
2592#ifdef VBOX_WITH_USB
2593 CheckComArgOutPointerValid(aDevice);
2594
2595 AutoCaller autoCaller(this);
2596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2597
2598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 /* Find it. */
2601 ComObjPtr<OUSBDevice> device;
2602 USBDeviceList::iterator it = mUSBDevices.begin();
2603 Guid uuid(aId);
2604 while (it != mUSBDevices.end())
2605 {
2606 if ((*it)->id() == uuid)
2607 {
2608 device = *it;
2609 break;
2610 }
2611 ++ it;
2612 }
2613
2614 if (!device)
2615 return setError(E_INVALIDARG,
2616 tr("USB device with UUID {%RTuuid} is not attached to this machine"),
2617 Guid(aId).raw());
2618
2619 /*
2620 * Inform the USB device and USB proxy about what's cooking.
2621 */
2622 alock.leave();
2623 HRESULT rc2 = mControl->DetachUSBDevice(aId, false /* aDone */);
2624 if (FAILED(rc2))
2625 return rc2;
2626 alock.enter();
2627
2628 /* Request the PDM to detach the USB device. */
2629 HRESULT rc = detachUSBDevice(it);
2630
2631 if (SUCCEEDED(rc))
2632 {
2633 /* leave the lock since we don't need it any more (note though that
2634 * the USB Proxy service must not call us back here) */
2635 alock.leave();
2636
2637 /* Request the device release. Even if it fails, the device will
2638 * remain as held by proxy, which is OK for us (the VM process). */
2639 rc = mControl->DetachUSBDevice(aId, true /* aDone */);
2640 }
2641
2642 return rc;
2643
2644
2645#else /* !VBOX_WITH_USB */
2646 return setError(VBOX_E_PDM_ERROR,
2647 tr("The virtual machine does not have a USB controller"));
2648#endif /* !VBOX_WITH_USB */
2649}
2650
2651STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDevice)
2652{
2653#ifdef VBOX_WITH_USB
2654 CheckComArgStrNotEmptyOrNull(aAddress);
2655 CheckComArgOutPointerValid(aDevice);
2656
2657 *aDevice = NULL;
2658
2659 SafeIfaceArray<IUSBDevice> devsvec;
2660 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
2661 if (FAILED(rc)) return rc;
2662
2663 for (size_t i = 0; i < devsvec.size(); ++i)
2664 {
2665 Bstr address;
2666 rc = devsvec[i]->COMGETTER(Address)(address.asOutParam());
2667 if (FAILED(rc)) return rc;
2668 if (address == aAddress)
2669 {
2670 ComObjPtr<OUSBDevice> found;
2671 found.createObject();
2672 found->init(devsvec[i]);
2673 return found.queryInterfaceTo(aDevice);
2674 }
2675 }
2676
2677 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
2678 tr("Could not find a USB device with address '%ls'"),
2679 aAddress);
2680
2681#else /* !VBOX_WITH_USB */
2682 return E_NOTIMPL;
2683#endif /* !VBOX_WITH_USB */
2684}
2685
2686STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
2687{
2688#ifdef VBOX_WITH_USB
2689 CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
2690 CheckComArgOutPointerValid(aDevice);
2691
2692 *aDevice = NULL;
2693
2694 SafeIfaceArray<IUSBDevice> devsvec;
2695 HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
2696 if (FAILED(rc)) return rc;
2697
2698 for (size_t i = 0; i < devsvec.size(); ++i)
2699 {
2700 Bstr id;
2701 rc = devsvec[i]->COMGETTER(Id)(id.asOutParam());
2702 if (FAILED(rc)) return rc;
2703 if (id == aId)
2704 {
2705 ComObjPtr<OUSBDevice> found;
2706 found.createObject();
2707 found->init(devsvec[i]);
2708 return found.queryInterfaceTo(aDevice);
2709 }
2710 }
2711
2712 return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
2713 tr("Could not find a USB device with uuid {%RTuuid}"),
2714 Guid(aId).raw());
2715
2716#else /* !VBOX_WITH_USB */
2717 return E_NOTIMPL;
2718#endif /* !VBOX_WITH_USB */
2719}
2720
2721STDMETHODIMP
2722Console::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
2723{
2724 CheckComArgStrNotEmptyOrNull(aName);
2725 CheckComArgStrNotEmptyOrNull(aHostPath);
2726
2727 AutoCaller autoCaller(this);
2728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2729
2730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 /// @todo see @todo in AttachUSBDevice() about the Paused state
2733 if (mMachineState == MachineState_Saved)
2734 return setError(VBOX_E_INVALID_VM_STATE,
2735 tr("Cannot create a transient shared folder on the machine in the saved state"));
2736 if ( mMachineState != MachineState_PoweredOff
2737 && mMachineState != MachineState_Teleported
2738 && mMachineState != MachineState_Aborted
2739 && mMachineState != MachineState_Running
2740 && mMachineState != MachineState_Paused
2741 )
2742 return setError(VBOX_E_INVALID_VM_STATE,
2743 tr("Cannot create a transient shared folder on the machine while it is changing the state (machine state: %s)"),
2744 Global::stringifyMachineState(mMachineState));
2745
2746 ComObjPtr<SharedFolder> sharedFolder;
2747 HRESULT rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
2748 if (SUCCEEDED(rc))
2749 return setError(VBOX_E_FILE_ERROR,
2750 tr("Shared folder named '%ls' already exists"),
2751 aName);
2752
2753 sharedFolder.createObject();
2754 rc = sharedFolder->init(this, aName, aHostPath, aWritable, aAutoMount);
2755 if (FAILED(rc)) return rc;
2756
2757 /* protect mpVM (if not NULL) */
2758 AutoVMCallerQuietWeak autoVMCaller(this);
2759
2760 if (mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive())
2761 {
2762 /* If the VM is online and supports shared folders, share this folder
2763 * under the specified name. */
2764
2765 /* first, remove the machine or the global folder if there is any */
2766 SharedFolderDataMap::const_iterator it;
2767 if (findOtherSharedFolder(aName, it))
2768 {
2769 rc = removeSharedFolder(aName);
2770 if (FAILED(rc)) return rc;
2771 }
2772
2773 /* second, create the given folder */
2774 rc = createSharedFolder(aName, SharedFolderData(aHostPath, aWritable, aAutoMount));
2775 if (FAILED(rc)) return rc;
2776 }
2777
2778 mSharedFolders.insert(std::make_pair(aName, sharedFolder));
2779
2780 /* notify console callbacks after the folder is added to the list */
2781 CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Session);
2782
2783 return rc;
2784}
2785
2786STDMETHODIMP Console::RemoveSharedFolder(IN_BSTR aName)
2787{
2788 CheckComArgStrNotEmptyOrNull(aName);
2789
2790 AutoCaller autoCaller(this);
2791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2792
2793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 /// @todo see @todo in AttachUSBDevice() about the Paused state
2796 if (mMachineState == MachineState_Saved)
2797 return setError(VBOX_E_INVALID_VM_STATE,
2798 tr("Cannot remove a transient shared folder from the machine in the saved state"));
2799 if ( mMachineState != MachineState_PoweredOff
2800 && mMachineState != MachineState_Teleported
2801 && mMachineState != MachineState_Aborted
2802 && mMachineState != MachineState_Running
2803 && mMachineState != MachineState_Paused
2804 )
2805 return setError(VBOX_E_INVALID_VM_STATE,
2806 tr("Cannot remove a transient shared folder from the machine while it is changing the state (machine state: %s)"),
2807 Global::stringifyMachineState(mMachineState));
2808
2809 ComObjPtr<SharedFolder> sharedFolder;
2810 HRESULT rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
2811 if (FAILED(rc)) return rc;
2812
2813 /* protect mpVM (if not NULL) */
2814 AutoVMCallerQuietWeak autoVMCaller(this);
2815
2816 if (mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive())
2817 {
2818 /* if the VM is online and supports shared folders, UNshare this
2819 * folder. */
2820
2821 /* first, remove the given folder */
2822 rc = removeSharedFolder(aName);
2823 if (FAILED(rc)) return rc;
2824
2825 /* first, remove the machine or the global folder if there is any */
2826 SharedFolderDataMap::const_iterator it;
2827 if (findOtherSharedFolder(aName, it))
2828 {
2829 rc = createSharedFolder(aName, it->second);
2830 /* don't check rc here because we need to remove the console
2831 * folder from the collection even on failure */
2832 }
2833 }
2834
2835 mSharedFolders.erase(aName);
2836
2837 /* notify console callbacks after the folder is removed to the list */
2838 CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Session);
2839
2840 return rc;
2841}
2842
2843STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
2844 IN_BSTR aDescription,
2845 IProgress **aProgress)
2846{
2847 LogFlowThisFuncEnter();
2848 LogFlowThisFunc(("aName='%ls' mMachineState=%08X\n", aName, mMachineState));
2849
2850 CheckComArgStrNotEmptyOrNull(aName);
2851 CheckComArgOutPointerValid(aProgress);
2852
2853 AutoCaller autoCaller(this);
2854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2855
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 if (Global::IsTransient(mMachineState))
2859 return setError(VBOX_E_INVALID_VM_STATE,
2860 tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
2861 Global::stringifyMachineState(mMachineState));
2862
2863 HRESULT rc = S_OK;
2864
2865 /* prepare the progress object:
2866 a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
2867 ULONG cOperations = 2; // always at least setting up + finishing up
2868 ULONG ulTotalOperationsWeight = 2; // one each for setting up + finishing up
2869 SafeIfaceArray<IMediumAttachment> aMediumAttachments;
2870 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aMediumAttachments));
2871 if (FAILED(rc))
2872 return setError(rc, tr("Cannot get medium attachments of the machine"));
2873
2874 ULONG ulMemSize;
2875 rc = mMachine->COMGETTER(MemorySize)(&ulMemSize);
2876 if (FAILED(rc))
2877 return rc;
2878
2879 for (size_t i = 0;
2880 i < aMediumAttachments.size();
2881 ++i)
2882 {
2883 DeviceType_T type;
2884 rc = aMediumAttachments[i]->COMGETTER(Type)(&type);
2885 if (FAILED(rc))
2886 return rc;
2887
2888 if (type == DeviceType_HardDisk)
2889 {
2890 ++cOperations;
2891
2892 // assume that creating a diff image takes as long as saving a 1MB state
2893 // (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
2894 ulTotalOperationsWeight += 1;
2895 }
2896 }
2897
2898 // b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
2899 bool fTakingSnapshotOnline = ((mMachineState == MachineState_Running) || (mMachineState == MachineState_Paused));
2900
2901 LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
2902
2903 if ( fTakingSnapshotOnline
2904 || mMachineState == MachineState_Saved
2905 )
2906 {
2907 ++cOperations;
2908
2909 ulTotalOperationsWeight += ulMemSize;
2910 }
2911
2912 // finally, create the progress object
2913 ComObjPtr<Progress> pProgress;
2914 pProgress.createObject();
2915 rc = pProgress->init(static_cast<IConsole*>(this),
2916 Bstr(tr("Taking a snapshot of the virtual machine")).raw(),
2917 mMachineState == MachineState_Running /* aCancelable */,
2918 cOperations,
2919 ulTotalOperationsWeight,
2920 Bstr(tr("Setting up snapshot operation")).raw(), // first sub-op description
2921 1); // ulFirstOperationWeight
2922
2923 if (FAILED(rc))
2924 return rc;
2925
2926 VMTakeSnapshotTask *pTask;
2927 if (!(pTask = new VMTakeSnapshotTask(this, pProgress, aName, aDescription)))
2928 return E_OUTOFMEMORY;
2929
2930 Assert(pTask->mProgress);
2931
2932 try
2933 {
2934 mptrCancelableProgress = pProgress;
2935
2936 /*
2937 * If we fail here it means a PowerDown() call happened on another
2938 * thread while we were doing Pause() (which leaves the Console lock).
2939 * We assign PowerDown() a higher precedence than TakeSnapshot(),
2940 * therefore just return the error to the caller.
2941 */
2942 rc = pTask->rc();
2943 if (FAILED(rc)) throw rc;
2944
2945 pTask->ulMemSize = ulMemSize;
2946
2947 /* memorize the current machine state */
2948 pTask->lastMachineState = mMachineState;
2949 pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
2950
2951 int vrc = RTThreadCreate(NULL,
2952 Console::fntTakeSnapshotWorker,
2953 (void*)pTask,
2954 0,
2955 RTTHREADTYPE_MAIN_WORKER,
2956 0,
2957 "ConsoleTakeSnap");
2958 if (FAILED(vrc))
2959 throw setError(E_FAIL,
2960 tr("Could not create VMTakeSnap thread (%Rrc)"),
2961 vrc);
2962
2963 pTask->mProgress.queryInterfaceTo(aProgress);
2964 }
2965 catch (HRESULT erc)
2966 {
2967 delete pTask;
2968 NOREF(erc);
2969 mptrCancelableProgress.setNull();
2970 }
2971
2972 LogFlowThisFunc(("rc=%Rhrc\n", rc));
2973 LogFlowThisFuncLeave();
2974 return rc;
2975}
2976
2977STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
2978{
2979 CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
2980 CheckComArgOutPointerValid(aProgress);
2981
2982 AutoCaller autoCaller(this);
2983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2984
2985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 if (Global::IsTransient(mMachineState))
2988 return setError(VBOX_E_INVALID_VM_STATE,
2989 tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
2990 Global::stringifyMachineState(mMachineState));
2991
2992
2993 MachineState_T machineState = MachineState_Null;
2994 HRESULT rc = mControl->DeleteSnapshot(this, aId, &machineState, aProgress);
2995 if (FAILED(rc)) return rc;
2996
2997 setMachineStateLocally(machineState);
2998 return S_OK;
2999}
3000
3001STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
3002{
3003 AutoCaller autoCaller(this);
3004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3005
3006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 if (Global::IsOnlineOrTransient(mMachineState))
3009 return setError(VBOX_E_INVALID_VM_STATE,
3010 tr("Cannot delete the current state of the running machine (machine state: %s)"),
3011 Global::stringifyMachineState(mMachineState));
3012
3013 MachineState_T machineState = MachineState_Null;
3014 HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
3015 if (FAILED(rc)) return rc;
3016
3017 setMachineStateLocally(machineState);
3018 return S_OK;
3019}
3020
3021// Non-interface public methods
3022/////////////////////////////////////////////////////////////////////////////
3023
3024/*static*/
3025HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
3026{
3027 va_list args;
3028 va_start(args, pcsz);
3029 HRESULT rc = setErrorInternal(aResultCode,
3030 getStaticClassIID(),
3031 getStaticComponentName(),
3032 Utf8StrFmtVA(pcsz, args),
3033 false /* aWarning */,
3034 true /* aLogIt */);
3035 va_end(args);
3036 return rc;
3037}
3038
3039HRESULT Console::setInvalidMachineStateError()
3040{
3041 return setError(VBOX_E_INVALID_VM_STATE,
3042 tr("Invalid machine state: %s"),
3043 Global::stringifyMachineState(mMachineState));
3044}
3045
3046
3047/**
3048 * @copydoc VirtualBox::handleUnexpectedExceptions
3049 */
3050/* static */
3051HRESULT Console::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3052{
3053 try
3054 {
3055 /* re-throw the current exception */
3056 throw;
3057 }
3058 catch (const std::exception &err)
3059 {
3060 return setErrorStatic(E_FAIL,
3061 tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3062 err.what(), typeid(err).name(),
3063 pszFile, iLine, pszFunction);
3064 }
3065 catch (...)
3066 {
3067 return setErrorStatic(E_FAIL,
3068 tr("Unknown exception\n%s[%d] (%s)"),
3069 pszFile, iLine, pszFunction);
3070 }
3071
3072 /* should not get here */
3073 AssertFailed();
3074 return E_FAIL;
3075}
3076
3077/* static */
3078const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3079{
3080 switch (enmCtrlType)
3081 {
3082 case StorageControllerType_LsiLogic:
3083 return "lsilogicscsi";
3084 case StorageControllerType_BusLogic:
3085 return "buslogic";
3086 case StorageControllerType_LsiLogicSas:
3087 return "lsilogicsas";
3088 case StorageControllerType_IntelAhci:
3089 return "ahci";
3090 case StorageControllerType_PIIX3:
3091 case StorageControllerType_PIIX4:
3092 case StorageControllerType_ICH6:
3093 return "piix3ide";
3094 case StorageControllerType_I82078:
3095 return "i82078";
3096 default:
3097 return NULL;
3098 }
3099}
3100
3101HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3102{
3103 switch (enmBus)
3104 {
3105 case StorageBus_IDE:
3106 case StorageBus_Floppy:
3107 {
3108 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3109 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3110 uLun = 2 * port + device;
3111 return S_OK;
3112 }
3113 case StorageBus_SATA:
3114 case StorageBus_SCSI:
3115 case StorageBus_SAS:
3116 {
3117 uLun = port;
3118 return S_OK;
3119 }
3120 default:
3121 uLun = 0;
3122 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3123 }
3124}
3125
3126// private methods
3127/////////////////////////////////////////////////////////////////////////////
3128
3129/**
3130 * Process a medium change.
3131 *
3132 * @param aMediumAttachment The medium attachment with the new medium state.
3133 * @param fForce Force medium chance, if it is locked or not.
3134 *
3135 * @note Locks this object for writing.
3136 */
3137HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce)
3138{
3139 AutoCaller autoCaller(this);
3140 AssertComRCReturnRC(autoCaller.rc());
3141
3142 /* We will need to release the write lock before calling EMT */
3143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3144
3145 HRESULT rc = S_OK;
3146 const char *pszDevice = NULL;
3147
3148 SafeIfaceArray<IStorageController> ctrls;
3149 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3150 AssertComRC(rc);
3151 IMedium *pMedium;
3152 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3153 AssertComRC(rc);
3154 Bstr mediumLocation;
3155 if (pMedium)
3156 {
3157 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3158 AssertComRC(rc);
3159 }
3160
3161 Bstr attCtrlName;
3162 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3163 AssertComRC(rc);
3164 ComPtr<IStorageController> ctrl;
3165 for (size_t i = 0; i < ctrls.size(); ++i)
3166 {
3167 Bstr ctrlName;
3168 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3169 AssertComRC(rc);
3170 if (attCtrlName == ctrlName)
3171 {
3172 ctrl = ctrls[i];
3173 break;
3174 }
3175 }
3176 if (ctrl.isNull())
3177 {
3178 return setError(E_FAIL,
3179 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3180 }
3181 StorageControllerType_T enmCtrlType;
3182 rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
3183 AssertComRC(rc);
3184 pszDevice = convertControllerTypeToDev(enmCtrlType);
3185
3186 StorageBus_T enmBus;
3187 rc = ctrl->COMGETTER(Bus)(&enmBus);
3188 AssertComRC(rc);
3189 ULONG uInstance;
3190 rc = ctrl->COMGETTER(Instance)(&uInstance);
3191 AssertComRC(rc);
3192 BOOL fUseHostIOCache;
3193 rc = ctrl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3194 AssertComRC(rc);
3195
3196 /* protect mpVM */
3197 AutoVMCaller autoVMCaller(this);
3198 AssertComRCReturnRC(autoVMCaller.rc());
3199
3200 /*
3201 * Call worker in EMT, that's faster and safer than doing everything
3202 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3203 * here to make requests from under the lock in order to serialize them.
3204 */
3205 PVMREQ pReq;
3206 int vrc = VMR3ReqCall(mpVM,
3207 VMCPUID_ANY,
3208&pReq,
3209 0 /* no wait! */,
3210 VMREQFLAGS_VBOX_STATUS,
3211 (PFNRT)Console::changeRemovableMedium,
3212 7,
3213 this,
3214 pszDevice,
3215 uInstance,
3216 enmBus,
3217 fUseHostIOCache,
3218 aMediumAttachment,
3219 fForce);
3220
3221 /* leave the lock before waiting for a result (EMT will call us back!) */
3222 alock.leave();
3223
3224 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3225 {
3226 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3227 AssertRC(vrc);
3228 if (RT_SUCCESS(vrc))
3229 vrc = pReq->iStatus;
3230 }
3231 VMR3ReqFree(pReq);
3232
3233 if (RT_SUCCESS(vrc))
3234 {
3235 LogFlowThisFunc(("Returns S_OK\n"));
3236 return S_OK;
3237 }
3238
3239 if (!pMedium)
3240 return setError(E_FAIL,
3241 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3242 mediumLocation.raw(), vrc);
3243
3244 return setError(E_FAIL,
3245 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3246 vrc);
3247}
3248
3249/**
3250 * Performs the medium change in EMT.
3251 *
3252 * @returns VBox status code.
3253 *
3254 * @param pThis Pointer to the Console object.
3255 * @param pcszDevice The PDM device name.
3256 * @param uInstance The PDM device instance.
3257 * @param uLun The PDM LUN number of the drive.
3258 * @param fHostDrive True if this is a host drive attachment.
3259 * @param pszPath The path to the media / drive which is now being mounted / captured.
3260 * If NULL no media or drive is attached and the LUN will be configured with
3261 * the default block driver with no media. This will also be the state if
3262 * mounting / capturing the specified media / drive fails.
3263 * @param pszFormat Medium format string, usually "RAW".
3264 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3265 *
3266 * @thread EMT
3267 */
3268DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3269 const char *pcszDevice,
3270 unsigned uInstance,
3271 StorageBus_T enmBus,
3272 bool fUseHostIOCache,
3273 IMediumAttachment *aMediumAtt,
3274 bool fForce)
3275{
3276 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3277 pConsole, uInstance, pcszDevice, enmBus, fForce));
3278
3279 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3280
3281 AutoCaller autoCaller(pConsole);
3282 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3283
3284 PVM pVM = pConsole->mpVM;
3285
3286 /*
3287 * Suspend the VM first.
3288 *
3289 * The VM must not be running since it might have pending I/O to
3290 * the drive which is being changed.
3291 */
3292 bool fResume;
3293 VMSTATE enmVMState = VMR3GetState(pVM);
3294 switch (enmVMState)
3295 {
3296 case VMSTATE_RESETTING:
3297 case VMSTATE_RUNNING:
3298 {
3299 LogFlowFunc(("Suspending the VM...\n"));
3300 /* disable the callback to prevent Console-level state change */
3301 pConsole->mVMStateChangeCallbackDisabled = true;
3302 int rc = VMR3Suspend(pVM);
3303 pConsole->mVMStateChangeCallbackDisabled = false;
3304 AssertRCReturn(rc, rc);
3305 fResume = true;
3306 break;
3307 }
3308
3309 case VMSTATE_SUSPENDED:
3310 case VMSTATE_CREATED:
3311 case VMSTATE_OFF:
3312 fResume = false;
3313 break;
3314
3315 case VMSTATE_RUNNING_LS:
3316 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3317 COM_IIDOF(IConsole),
3318 getStaticComponentName(),
3319 Utf8Str(tr("Cannot change drive during live migration")),
3320 false /*aWarning*/,
3321 true /*aLogIt*/);
3322
3323 default:
3324 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3325 }
3326
3327 /* Determine the base path for the device instance. */
3328 PCFGMNODE pCtlInst;
3329 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice,
3330 uInstance);
3331 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3332
3333 int rc = VINF_SUCCESS;
3334 int rcRet = VINF_SUCCESS;
3335
3336 rcRet = pConsole->configMediumAttachment(pCtlInst,
3337 pcszDevice,
3338 uInstance,
3339 enmBus,
3340 fUseHostIOCache,
3341 false /* fSetupMerge */,
3342 0 /* uMergeSource */,
3343 0 /* uMergeTarget */,
3344 aMediumAtt,
3345 pConsole->mMachineState,
3346 NULL /* phrc */,
3347 true /* fAttachDetach */,
3348 fForce /* fForceUnmount */,
3349 pVM,
3350 NULL /* paLedDevType */);
3351 /** @todo this dumps everything attached to this device instance, which
3352 * is more than necessary. Dumping the changed LUN would be enough. */
3353 CFGMR3Dump(pCtlInst);
3354
3355 /*
3356 * Resume the VM if necessary.
3357 */
3358 if (fResume)
3359 {
3360 LogFlowFunc(("Resuming the VM...\n"));
3361 /* disable the callback to prevent Console-level state change */
3362 pConsole->mVMStateChangeCallbackDisabled = true;
3363 rc = VMR3Resume(pVM);
3364 pConsole->mVMStateChangeCallbackDisabled = false;
3365 AssertRC(rc);
3366 if (RT_FAILURE(rc))
3367 {
3368 /* too bad, we failed. try to sync the console state with the VMM state */
3369 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3370 }
3371 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3372 // error (if any) will be hidden from the caller. For proper reporting
3373 // of such multiple errors to the caller we need to enhance the
3374 // IVirtualBoxError interface. For now, give the first error the higher
3375 // priority.
3376 if (RT_SUCCESS(rcRet))
3377 rcRet = rc;
3378 }
3379
3380 LogFlowFunc(("Returning %Rrc\n", rcRet));
3381 return rcRet;
3382}
3383
3384
3385/**
3386 * Called by IInternalSessionControl::OnNetworkAdapterChange().
3387 *
3388 * @note Locks this object for writing.
3389 */
3390HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
3391{
3392 LogFlowThisFunc(("\n"));
3393
3394 AutoCaller autoCaller(this);
3395 AssertComRCReturnRC(autoCaller.rc());
3396
3397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3398
3399 HRESULT rc = S_OK;
3400
3401 /* don't trigger network change if the VM isn't running */
3402 if (mpVM)
3403 {
3404 /* protect mpVM */
3405 AutoVMCaller autoVMCaller(this);
3406 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3407
3408 /* Get the properties we need from the adapter */
3409 BOOL fCableConnected, fTraceEnabled;
3410 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
3411 AssertComRC(rc);
3412 if (SUCCEEDED(rc))
3413 {
3414 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
3415 AssertComRC(rc);
3416 }
3417 if (SUCCEEDED(rc))
3418 {
3419 ULONG ulInstance;
3420 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
3421 AssertComRC(rc);
3422 if (SUCCEEDED(rc))
3423 {
3424 /*
3425 * Find the pcnet instance, get the config interface and update
3426 * the link state.
3427 */
3428 NetworkAdapterType_T adapterType;
3429 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3430 AssertComRC(rc);
3431 const char *pszAdapterName = NULL;
3432 switch (adapterType)
3433 {
3434 case NetworkAdapterType_Am79C970A:
3435 case NetworkAdapterType_Am79C973:
3436 pszAdapterName = "pcnet";
3437 break;
3438#ifdef VBOX_WITH_E1000
3439 case NetworkAdapterType_I82540EM:
3440 case NetworkAdapterType_I82543GC:
3441 case NetworkAdapterType_I82545EM:
3442 pszAdapterName = "e1000";
3443 break;
3444#endif
3445#ifdef VBOX_WITH_VIRTIO
3446 case NetworkAdapterType_Virtio:
3447 pszAdapterName = "virtio-net";
3448 break;
3449#endif
3450 default:
3451 AssertFailed();
3452 pszAdapterName = "unknown";
3453 break;
3454 }
3455
3456 PPDMIBASE pBase;
3457 int vrc = PDMR3QueryDeviceLun(mpVM, pszAdapterName, ulInstance, 0, &pBase);
3458 ComAssertRC(vrc);
3459 if (RT_SUCCESS(vrc))
3460 {
3461 Assert(pBase);
3462 PPDMINETWORKCONFIG pINetCfg;
3463 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
3464 if (pINetCfg)
3465 {
3466 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
3467 fCableConnected));
3468 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
3469 fCableConnected ? PDMNETWORKLINKSTATE_UP
3470 : PDMNETWORKLINKSTATE_DOWN);
3471 ComAssertRC(vrc);
3472 }
3473#ifdef VBOX_DYNAMIC_NET_ATTACH
3474 if (RT_SUCCESS(vrc) && changeAdapter)
3475 {
3476 VMSTATE enmVMState = VMR3GetState(mpVM);
3477 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
3478 || enmVMState == VMSTATE_SUSPENDED)
3479 {
3480 if (fTraceEnabled && fCableConnected && pINetCfg)
3481 {
3482 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
3483 ComAssertRC(vrc);
3484 }
3485
3486 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3487
3488 if (fTraceEnabled && fCableConnected && pINetCfg)
3489 {
3490 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
3491 ComAssertRC(vrc);
3492 }
3493 }
3494 }
3495#endif /* VBOX_DYNAMIC_NET_ATTACH */
3496 }
3497
3498 if (RT_FAILURE(vrc))
3499 rc = E_FAIL;
3500 }
3501 }
3502 }
3503
3504 /* notify console callbacks on success */
3505 if (SUCCEEDED(rc))
3506 CONSOLE_DO_CALLBACKS1(OnNetworkAdapterChanged, aNetworkAdapter);
3507
3508 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3509 return rc;
3510}
3511
3512
3513#ifdef VBOX_DYNAMIC_NET_ATTACH
3514/**
3515 * Process a network adaptor change.
3516 *
3517 * @returns COM status code.
3518 *
3519 * @param pszDevice The PDM device name.
3520 * @param uInstance The PDM device instance.
3521 * @param uLun The PDM LUN number of the drive.
3522 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3523 *
3524 * @note Locks this object for writing.
3525 */
3526HRESULT Console::doNetworkAdapterChange(const char *pszDevice,
3527 unsigned uInstance,
3528 unsigned uLun,
3529 INetworkAdapter *aNetworkAdapter)
3530{
3531 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3532 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3533
3534 AutoCaller autoCaller(this);
3535 AssertComRCReturnRC(autoCaller.rc());
3536
3537 /* We will need to release the write lock before calling EMT */
3538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3539
3540 /* protect mpVM */
3541 AutoVMCaller autoVMCaller(this);
3542 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3543
3544 /*
3545 * Call worker in EMT, that's faster and safer than doing everything
3546 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3547 * here to make requests from under the lock in order to serialize them.
3548 */
3549 PVMREQ pReq;
3550 int vrc = VMR3ReqCall(mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3551 (PFNRT) Console::changeNetworkAttachment, 5,
3552 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3553
3554 /* leave the lock before waiting for a result (EMT will call us back!) */
3555 alock.leave();
3556
3557 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3558 {
3559 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3560 AssertRC(vrc);
3561 if (RT_SUCCESS(vrc))
3562 vrc = pReq->iStatus;
3563 }
3564 VMR3ReqFree(pReq);
3565
3566 if (RT_SUCCESS(vrc))
3567 {
3568 LogFlowThisFunc(("Returns S_OK\n"));
3569 return S_OK;
3570 }
3571
3572 return setError(E_FAIL,
3573 tr("Could not change the network adaptor attachement type (%Rrc)"),
3574 vrc);
3575}
3576
3577
3578/**
3579 * Performs the Network Adaptor change in EMT.
3580 *
3581 * @returns VBox status code.
3582 *
3583 * @param pThis Pointer to the Console object.
3584 * @param pszDevice The PDM device name.
3585 * @param uInstance The PDM device instance.
3586 * @param uLun The PDM LUN number of the drive.
3587 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3588 *
3589 * @thread EMT
3590 * @note Locks the Console object for writing.
3591 */
3592DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
3593 const char *pszDevice,
3594 unsigned uInstance,
3595 unsigned uLun,
3596 INetworkAdapter *aNetworkAdapter)
3597{
3598 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3599 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3600
3601 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3602
3603 AssertMsg( ( !strcmp(pszDevice, "pcnet")
3604 || !strcmp(pszDevice, "e1000")
3605 || !strcmp(pszDevice, "virtio-net"))
3606 && (uLun == 0)
3607 && (uInstance < SchemaDefs::NetworkAdapterCount),
3608 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3609 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3610
3611 AutoCaller autoCaller(pThis);
3612 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3613
3614 /* protect mpVM */
3615 AutoVMCaller autoVMCaller(pThis);
3616 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3617
3618 PVM pVM = pThis->mpVM;
3619
3620 /*
3621 * Suspend the VM first.
3622 *
3623 * The VM must not be running since it might have pending I/O to
3624 * the drive which is being changed.
3625 */
3626 bool fResume;
3627 VMSTATE enmVMState = VMR3GetState(pVM);
3628 switch (enmVMState)
3629 {
3630 case VMSTATE_RESETTING:
3631 case VMSTATE_RUNNING:
3632 {
3633 LogFlowFunc(("Suspending the VM...\n"));
3634 /* disable the callback to prevent Console-level state change */
3635 pThis->mVMStateChangeCallbackDisabled = true;
3636 int rc = VMR3Suspend(pVM);
3637 pThis->mVMStateChangeCallbackDisabled = false;
3638 AssertRCReturn(rc, rc);
3639 fResume = true;
3640 break;
3641 }
3642
3643 case VMSTATE_SUSPENDED:
3644 case VMSTATE_CREATED:
3645 case VMSTATE_OFF:
3646 fResume = false;
3647 break;
3648
3649 default:
3650 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3651 }
3652
3653 int rc = VINF_SUCCESS;
3654 int rcRet = VINF_SUCCESS;
3655
3656 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3657 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3658 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
3659 AssertRelease(pInst);
3660
3661 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
3662
3663 /*
3664 * Resume the VM if necessary.
3665 */
3666 if (fResume)
3667 {
3668 LogFlowFunc(("Resuming the VM...\n"));
3669 /* disable the callback to prevent Console-level state change */
3670 pThis->mVMStateChangeCallbackDisabled = true;
3671 rc = VMR3Resume(pVM);
3672 pThis->mVMStateChangeCallbackDisabled = false;
3673 AssertRC(rc);
3674 if (RT_FAILURE(rc))
3675 {
3676 /* too bad, we failed. try to sync the console state with the VMM state */
3677 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3678 }
3679 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3680 // error (if any) will be hidden from the caller. For proper reporting
3681 // of such multiple errors to the caller we need to enhance the
3682 // IVirtualBoxError interface. For now, give the first error the higher
3683 // priority.
3684 if (RT_SUCCESS(rcRet))
3685 rcRet = rc;
3686 }
3687
3688 LogFlowFunc(("Returning %Rrc\n", rcRet));
3689 return rcRet;
3690}
3691#endif /* VBOX_DYNAMIC_NET_ATTACH */
3692
3693
3694/**
3695 * Called by IInternalSessionControl::OnSerialPortChange().
3696 *
3697 * @note Locks this object for writing.
3698 */
3699HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
3700{
3701 LogFlowThisFunc(("\n"));
3702
3703 AutoCaller autoCaller(this);
3704 AssertComRCReturnRC(autoCaller.rc());
3705
3706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3707
3708 HRESULT rc = S_OK;
3709
3710 /* don't trigger serial port change if the VM isn't running */
3711 if (mpVM)
3712 {
3713 /* protect mpVM */
3714 AutoVMCaller autoVMCaller(this);
3715 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3716
3717 /* nothing to do so far */
3718 }
3719
3720 /* notify console callbacks on success */
3721 if (SUCCEEDED(rc))
3722 CONSOLE_DO_CALLBACKS1(OnSerialPortChanged, aSerialPort);
3723
3724 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3725 return rc;
3726}
3727
3728/**
3729 * Called by IInternalSessionControl::OnParallelPortChange().
3730 *
3731 * @note Locks this object for writing.
3732 */
3733HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
3734{
3735 LogFlowThisFunc(("\n"));
3736
3737 AutoCaller autoCaller(this);
3738 AssertComRCReturnRC(autoCaller.rc());
3739
3740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3741
3742 HRESULT rc = S_OK;
3743
3744 /* don't trigger parallel port change if the VM isn't running */
3745 if (mpVM)
3746 {
3747 /* protect mpVM */
3748 AutoVMCaller autoVMCaller(this);
3749 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3750
3751 /* nothing to do so far */
3752 }
3753
3754 /* notify console callbacks on success */
3755 if (SUCCEEDED(rc))
3756 CONSOLE_DO_CALLBACKS1(OnParallelPortChanged, aParallelPort);
3757
3758 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3759 return rc;
3760}
3761
3762/**
3763 * Called by IInternalSessionControl::OnStorageControllerChange().
3764 *
3765 * @note Locks this object for writing.
3766 */
3767HRESULT Console::onStorageControllerChange()
3768{
3769 LogFlowThisFunc(("\n"));
3770
3771 AutoCaller autoCaller(this);
3772 AssertComRCReturnRC(autoCaller.rc());
3773
3774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3775
3776 HRESULT rc = S_OK;
3777
3778 /* don't trigger storage controller change if the VM isn't running */
3779 if (mpVM)
3780 {
3781 /* protect mpVM */
3782 AutoVMCaller autoVMCaller(this);
3783 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3784
3785 /* nothing to do so far */
3786 }
3787
3788 /* notify console callbacks on success */
3789 if (SUCCEEDED(rc))
3790 CONSOLE_DO_CALLBACKS0(OnStorageControllerChanged);
3791
3792 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3793 return rc;
3794}
3795
3796/**
3797 * Called by IInternalSessionControl::OnMediumChange().
3798 *
3799 * @note Locks this object for writing.
3800 */
3801HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
3802{
3803 LogFlowThisFunc(("\n"));
3804
3805 AutoCaller autoCaller(this);
3806 AssertComRCReturnRC(autoCaller.rc());
3807
3808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3809
3810 HRESULT rc = S_OK;
3811
3812 /* don't trigger medium change if the VM isn't running */
3813 if (mpVM)
3814 {
3815 /* protect mpVM */
3816 AutoVMCaller autoVMCaller(this);
3817 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3818
3819 rc = doMediumChange(aMediumAttachment, !!aForce);
3820 }
3821
3822 /* notify console callbacks on success */
3823 if (SUCCEEDED(rc))
3824 CONSOLE_DO_CALLBACKS1(OnMediumChanged, aMediumAttachment);
3825
3826 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3827 return rc;
3828}
3829
3830/**
3831 * Called by IInternalSessionControl::OnCPUChange().
3832 *
3833 * @note Locks this object for writing.
3834 */
3835HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
3836{
3837 LogFlowThisFunc(("\n"));
3838
3839 AutoCaller autoCaller(this);
3840 AssertComRCReturnRC(autoCaller.rc());
3841
3842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3843
3844 HRESULT rc = S_OK;
3845
3846 /* don't trigger CPU change if the VM isn't running */
3847 if (mpVM)
3848 {
3849 /* protect mpVM */
3850 AutoVMCaller autoVMCaller(this);
3851 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3852
3853 if (aRemove)
3854 rc = doCPURemove(aCPU);
3855 else
3856 rc = doCPUAdd(aCPU);
3857 }
3858
3859 /* notify console callbacks on success */
3860 if (SUCCEEDED(rc))
3861 CONSOLE_DO_CALLBACKS2(OnCPUChanged, aCPU, aRemove);
3862
3863 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3864 return rc;
3865}
3866
3867/**
3868 * Called by IInternalSessionControl::OnCPUPriorityChange().
3869 *
3870 * @note Locks this object for writing.
3871 */
3872HRESULT Console::onCPUPriorityChange(ULONG aCpuPriority)
3873{
3874 LogFlowThisFunc(("\n"));
3875
3876 AutoCaller autoCaller(this);
3877 AssertComRCReturnRC(autoCaller.rc());
3878
3879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3880
3881 HRESULT rc = S_OK;
3882
3883 /* don't trigger the CPU priority change if the VM isn't running */
3884 if (mpVM)
3885 {
3886 /* protect mpVM */
3887 AutoVMCaller autoVMCaller(this);
3888 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3889
3890 if ( mMachineState == MachineState_Running
3891 || mMachineState == MachineState_Teleporting
3892 || mMachineState == MachineState_LiveSnapshotting
3893 )
3894 {
3895 /* No need to call in the EMT thread. */
3896 rc = VMR3SetCpuPriority(mpVM, aCpuPriority);
3897 }
3898 else
3899 rc = setInvalidMachineStateError();
3900 }
3901
3902 /* notify console callbacks on success */
3903 if (SUCCEEDED(rc))
3904 CONSOLE_DO_CALLBACKS1(OnCPUPriorityChanged, aCpuPriority);
3905
3906 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3907 return rc;
3908}
3909
3910/**
3911 * Called by IInternalSessionControl::OnVRDPServerChange().
3912 *
3913 * @note Locks this object for writing.
3914 */
3915HRESULT Console::onVRDPServerChange(BOOL aRestart)
3916{
3917 AutoCaller autoCaller(this);
3918 AssertComRCReturnRC(autoCaller.rc());
3919
3920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3921
3922 HRESULT rc = S_OK;
3923
3924 if ( mVRDPServer
3925 && ( mMachineState == MachineState_Running
3926 || mMachineState == MachineState_Teleporting
3927 || mMachineState == MachineState_LiveSnapshotting
3928 )
3929 )
3930 {
3931 BOOL vrdpEnabled = FALSE;
3932
3933 rc = mVRDPServer->COMGETTER(Enabled)(&vrdpEnabled);
3934 ComAssertComRCRetRC(rc);
3935
3936 if (aRestart)
3937 {
3938 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
3939 alock.leave();
3940
3941 if (vrdpEnabled)
3942 {
3943 // If there was no VRDP server started the 'stop' will do nothing.
3944 // However if a server was started and this notification was called,
3945 // we have to restart the server.
3946 mConsoleVRDPServer->Stop();
3947
3948 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
3949 {
3950 rc = E_FAIL;
3951 }
3952 else
3953 {
3954 mConsoleVRDPServer->EnableConnections();
3955 }
3956 }
3957 else
3958 {
3959 mConsoleVRDPServer->Stop();
3960 }
3961
3962 alock.enter();
3963 }
3964 }
3965
3966 /* notify console callbacks on success */
3967 if (SUCCEEDED(rc))
3968 CONSOLE_DO_CALLBACKS0(OnVRDPServerChanged);
3969
3970 return rc;
3971}
3972
3973/**
3974 * @note Locks this object for reading.
3975 */
3976void Console::onRemoteDisplayInfoChange()
3977{
3978 AutoCaller autoCaller(this);
3979 AssertComRCReturnVoid(autoCaller.rc());
3980
3981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3982
3983 CONSOLE_DO_CALLBACKS0(OnRemoteDisplayInfoChanged);
3984}
3985
3986
3987
3988/**
3989 * Called by IInternalSessionControl::OnUSBControllerChange().
3990 *
3991 * @note Locks this object for writing.
3992 */
3993HRESULT Console::onUSBControllerChange()
3994{
3995 LogFlowThisFunc(("\n"));
3996
3997 AutoCaller autoCaller(this);
3998 AssertComRCReturnRC(autoCaller.rc());
3999
4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4001
4002 HRESULT rc = S_OK;
4003
4004 /* don't trigger USB controller change if the VM isn't running */
4005 if (mpVM)
4006 {
4007 /// @todo implement one day.
4008 // Anyway, if we want to query the machine's USB Controller we need
4009 // to cache it to mUSBController in #init() (similar to mDVDDrive).
4010 //
4011 // bird: While the VM supports hot-plugging, I doubt any guest can
4012 // handle it at this time... :-)
4013
4014 /* protect mpVM */
4015 AutoVMCaller autoVMCaller(this);
4016 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4017
4018 /* nothing to do so far */
4019 }
4020
4021 /* notify console callbacks on success */
4022 if (SUCCEEDED(rc))
4023 CONSOLE_DO_CALLBACKS0(OnUSBControllerChanged);
4024
4025 return rc;
4026}
4027
4028/**
4029 * Called by IInternalSessionControl::OnSharedFolderChange().
4030 *
4031 * @note Locks this object for writing.
4032 */
4033HRESULT Console::onSharedFolderChange(BOOL aGlobal)
4034{
4035 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
4036
4037 AutoCaller autoCaller(this);
4038 AssertComRCReturnRC(autoCaller.rc());
4039
4040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4041
4042 HRESULT rc = fetchSharedFolders(aGlobal);
4043
4044 /* notify console callbacks on success */
4045 if (SUCCEEDED(rc))
4046 {
4047 if (aGlobal)
4048 CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Global);
4049 else
4050 CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Machine);
4051 }
4052
4053 return rc;
4054}
4055
4056/**
4057 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4058 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4059 * returns TRUE for a given remote USB device.
4060 *
4061 * @return S_OK if the device was attached to the VM.
4062 * @return failure if not attached.
4063 *
4064 * @param aDevice
4065 * The device in question.
4066 * @param aMaskedIfs
4067 * The interfaces to hide from the guest.
4068 *
4069 * @note Locks this object for writing.
4070 */
4071HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4072{
4073#ifdef VBOX_WITH_USB
4074 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4075
4076 AutoCaller autoCaller(this);
4077 ComAssertComRCRetRC(autoCaller.rc());
4078
4079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4080
4081 /* protect mpVM (we don't need error info, since it's a callback) */
4082 AutoVMCallerQuiet autoVMCaller(this);
4083 if (FAILED(autoVMCaller.rc()))
4084 {
4085 /* The VM may be no more operational when this message arrives
4086 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4087 * autoVMCaller.rc() will return a failure in this case. */
4088 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4089 mMachineState));
4090 return autoVMCaller.rc();
4091 }
4092
4093 if (aError != NULL)
4094 {
4095 /* notify callbacks about the error */
4096 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4097 return S_OK;
4098 }
4099
4100 /* Don't proceed unless there's at least one USB hub. */
4101 if (!PDMR3USBHasHub(mpVM))
4102 {
4103 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4104 return E_FAIL;
4105 }
4106
4107 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4108 if (FAILED(rc))
4109 {
4110 /* take the current error info */
4111 com::ErrorInfoKeeper eik;
4112 /* the error must be a VirtualBoxErrorInfo instance */
4113 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
4114 Assert(!error.isNull());
4115 if (!error.isNull())
4116 {
4117 /* notify callbacks about the error */
4118 onUSBDeviceStateChange(aDevice, true /* aAttached */, error);
4119 }
4120 }
4121
4122 return rc;
4123
4124#else /* !VBOX_WITH_USB */
4125 return E_FAIL;
4126#endif /* !VBOX_WITH_USB */
4127}
4128
4129/**
4130 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
4131 * processRemoteUSBDevices().
4132 *
4133 * @note Locks this object for writing.
4134 */
4135HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
4136 IVirtualBoxErrorInfo *aError)
4137{
4138#ifdef VBOX_WITH_USB
4139 Guid Uuid(aId);
4140 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
4141
4142 AutoCaller autoCaller(this);
4143 AssertComRCReturnRC(autoCaller.rc());
4144
4145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4146
4147 /* Find the device. */
4148 ComObjPtr<OUSBDevice> device;
4149 USBDeviceList::iterator it = mUSBDevices.begin();
4150 while (it != mUSBDevices.end())
4151 {
4152 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
4153 if ((*it)->id() == Uuid)
4154 {
4155 device = *it;
4156 break;
4157 }
4158 ++ it;
4159 }
4160
4161
4162 if (device.isNull())
4163 {
4164 LogFlowThisFunc(("USB device not found.\n"));
4165
4166 /* The VM may be no more operational when this message arrives
4167 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
4168 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
4169 * failure in this case. */
4170
4171 AutoVMCallerQuiet autoVMCaller(this);
4172 if (FAILED(autoVMCaller.rc()))
4173 {
4174 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
4175 mMachineState));
4176 return autoVMCaller.rc();
4177 }
4178
4179 /* the device must be in the list otherwise */
4180 AssertFailedReturn(E_FAIL);
4181 }
4182
4183 if (aError != NULL)
4184 {
4185 /* notify callback about an error */
4186 onUSBDeviceStateChange(device, false /* aAttached */, aError);
4187 return S_OK;
4188 }
4189
4190 HRESULT rc = detachUSBDevice(it);
4191
4192 if (FAILED(rc))
4193 {
4194 /* take the current error info */
4195 com::ErrorInfoKeeper eik;
4196 /* the error must be a VirtualBoxErrorInfo instance */
4197 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
4198 Assert(!error.isNull());
4199 if (!error.isNull())
4200 {
4201 /* notify callbacks about the error */
4202 onUSBDeviceStateChange(device, false /* aAttached */, error);
4203 }
4204 }
4205
4206 return rc;
4207
4208#else /* !VBOX_WITH_USB */
4209 return E_FAIL;
4210#endif /* !VBOX_WITH_USB */
4211}
4212
4213/**
4214 * @note Temporarily locks this object for writing.
4215 */
4216HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
4217 LONG64 *aTimestamp, BSTR *aFlags)
4218{
4219#ifndef VBOX_WITH_GUEST_PROPS
4220 ReturnComNotImplemented();
4221#else /* VBOX_WITH_GUEST_PROPS */
4222 if (!VALID_PTR(aName))
4223 return E_INVALIDARG;
4224 if (!VALID_PTR(aValue))
4225 return E_POINTER;
4226 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
4227 return E_POINTER;
4228 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4229 return E_POINTER;
4230
4231 AutoCaller autoCaller(this);
4232 AssertComRCReturnRC(autoCaller.rc());
4233
4234 /* protect mpVM (if not NULL) */
4235 AutoVMCallerWeak autoVMCaller(this);
4236 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4237
4238 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4239 * autoVMCaller, so there is no need to hold a lock of this */
4240
4241 HRESULT rc = E_UNEXPECTED;
4242 using namespace guestProp;
4243
4244 try
4245 {
4246 VBOXHGCMSVCPARM parm[4];
4247 Utf8Str Utf8Name = aName;
4248 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4249
4250 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4251 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4252 /* The + 1 is the null terminator */
4253 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4254 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4255 parm[1].u.pointer.addr = pszBuffer;
4256 parm[1].u.pointer.size = sizeof(pszBuffer);
4257 int vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
4258 4, &parm[0]);
4259 /* The returned string should never be able to be greater than our buffer */
4260 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
4261 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4262 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4263 {
4264 rc = S_OK;
4265 if (vrc != VERR_NOT_FOUND)
4266 {
4267 Utf8Str strBuffer(pszBuffer);
4268 strBuffer.cloneTo(aValue);
4269
4270 *aTimestamp = parm[2].u.uint64;
4271
4272 size_t iFlags = strBuffer.length() + 1;
4273 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4274 }
4275 else
4276 aValue = NULL;
4277 }
4278 else
4279 rc = setError(E_UNEXPECTED,
4280 tr("The service call failed with the error %Rrc"),
4281 vrc);
4282 }
4283 catch(std::bad_alloc & /*e*/)
4284 {
4285 rc = E_OUTOFMEMORY;
4286 }
4287 return rc;
4288#endif /* VBOX_WITH_GUEST_PROPS */
4289}
4290
4291/**
4292 * @note Temporarily locks this object for writing.
4293 */
4294HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4295{
4296#ifndef VBOX_WITH_GUEST_PROPS
4297 ReturnComNotImplemented();
4298#else /* VBOX_WITH_GUEST_PROPS */
4299 if (!VALID_PTR(aName))
4300 return E_INVALIDARG;
4301 if ((aValue != NULL) && !VALID_PTR(aValue))
4302 return E_INVALIDARG;
4303 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4304 return E_INVALIDARG;
4305
4306 AutoCaller autoCaller(this);
4307 AssertComRCReturnRC(autoCaller.rc());
4308
4309 /* protect mpVM (if not NULL) */
4310 AutoVMCallerWeak autoVMCaller(this);
4311 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4312
4313 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4314 * autoVMCaller, so there is no need to hold a lock of this */
4315
4316 HRESULT rc = E_UNEXPECTED;
4317 using namespace guestProp;
4318
4319 VBOXHGCMSVCPARM parm[3];
4320 Utf8Str Utf8Name = aName;
4321 int vrc = VINF_SUCCESS;
4322
4323 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4324 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4325 /* The + 1 is the null terminator */
4326 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4327 Utf8Str Utf8Value = aValue;
4328 if (aValue != NULL)
4329 {
4330 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4331 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4332 /* The + 1 is the null terminator */
4333 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4334 }
4335 Utf8Str Utf8Flags = aFlags;
4336 if (aFlags != NULL)
4337 {
4338 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4339 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4340 /* The + 1 is the null terminator */
4341 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4342 }
4343 if ((aValue != NULL) && (aFlags != NULL))
4344 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
4345 3, &parm[0]);
4346 else if (aValue != NULL)
4347 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4348 2, &parm[0]);
4349 else
4350 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
4351 1, &parm[0]);
4352 if (RT_SUCCESS(vrc))
4353 rc = S_OK;
4354 else
4355 rc = setError(E_UNEXPECTED,
4356 tr("The service call failed with the error %Rrc"),
4357 vrc);
4358 return rc;
4359#endif /* VBOX_WITH_GUEST_PROPS */
4360}
4361
4362
4363/**
4364 * @note Temporarily locks this object for writing.
4365 */
4366HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
4367 ComSafeArrayOut(BSTR, aNames),
4368 ComSafeArrayOut(BSTR, aValues),
4369 ComSafeArrayOut(LONG64, aTimestamps),
4370 ComSafeArrayOut(BSTR, aFlags))
4371{
4372#ifndef VBOX_WITH_GUEST_PROPS
4373 ReturnComNotImplemented();
4374#else /* VBOX_WITH_GUEST_PROPS */
4375 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4376 return E_POINTER;
4377 if (ComSafeArrayOutIsNull(aNames))
4378 return E_POINTER;
4379 if (ComSafeArrayOutIsNull(aValues))
4380 return E_POINTER;
4381 if (ComSafeArrayOutIsNull(aTimestamps))
4382 return E_POINTER;
4383 if (ComSafeArrayOutIsNull(aFlags))
4384 return E_POINTER;
4385
4386 AutoCaller autoCaller(this);
4387 AssertComRCReturnRC(autoCaller.rc());
4388
4389 /* protect mpVM (if not NULL) */
4390 AutoVMCallerWeak autoVMCaller(this);
4391 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4392
4393 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4394 * autoVMCaller, so there is no need to hold a lock of this */
4395
4396 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
4397 ComSafeArrayOutArg(aValues),
4398 ComSafeArrayOutArg(aTimestamps),
4399 ComSafeArrayOutArg(aFlags));
4400#endif /* VBOX_WITH_GUEST_PROPS */
4401}
4402
4403
4404/*
4405 * Internal: helper function for connecting progress reporting
4406 */
4407static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
4408{
4409 HRESULT rc = S_OK;
4410 IProgress *pProgress = static_cast<IProgress *>(pvUser);
4411 if (pProgress)
4412 rc = pProgress->SetCurrentOperationProgress(uPercentage);
4413 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
4414}
4415
4416/**
4417 * @note Temporarily locks this object for writing.
4418 */
4419HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
4420 ULONG aSourceIdx, ULONG aTargetIdx,
4421 IMedium *aSource, IMedium *aTarget,
4422 BOOL aMergeForward,
4423 IMedium *aParentForTarget,
4424 ComSafeArrayIn(IMedium *, aChildrenToReparent),
4425 IProgress *aProgress)
4426{
4427 AutoCaller autoCaller(this);
4428 AssertComRCReturnRC(autoCaller.rc());
4429
4430 HRESULT rc = S_OK;
4431 int vrc = VINF_SUCCESS;
4432 PVM pVM = mpVM;
4433
4434 /* We will need to release the lock before doing the actual merge */
4435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4436
4437 /* paranoia - we don't want merges to happen while teleporting etc. */
4438 switch (mMachineState)
4439 {
4440 case MachineState_DeletingSnapshotOnline:
4441 case MachineState_DeletingSnapshotPaused:
4442 break;
4443
4444 default:
4445 return setInvalidMachineStateError();
4446 }
4447
4448 SafeIfaceArray<IStorageController> ctrls;
4449 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4450 AssertComRC(rc);
4451 LONG lDev;
4452 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
4453 AssertComRC(rc);
4454 LONG lPort;
4455 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
4456 AssertComRC(rc);
4457 IMedium *pMedium;
4458 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4459 AssertComRC(rc);
4460 Bstr mediumLocation;
4461 if (pMedium)
4462 {
4463 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4464 AssertComRC(rc);
4465 }
4466
4467 Bstr attCtrlName;
4468 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4469 AssertComRC(rc);
4470 ComPtr<IStorageController> ctrl;
4471 for (size_t i = 0; i < ctrls.size(); ++i)
4472 {
4473 Bstr ctrlName;
4474 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4475 AssertComRC(rc);
4476 if (attCtrlName == ctrlName)
4477 {
4478 ctrl = ctrls[i];
4479 break;
4480 }
4481 }
4482 if (ctrl.isNull())
4483 {
4484 return setError(E_FAIL,
4485 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
4486 }
4487 StorageControllerType_T enmCtrlType;
4488 rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
4489 AssertComRC(rc);
4490 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
4491
4492 StorageBus_T enmBus;
4493 rc = ctrl->COMGETTER(Bus)(&enmBus);
4494 AssertComRC(rc);
4495 ULONG uInstance;
4496 rc = ctrl->COMGETTER(Instance)(&uInstance);
4497 AssertComRC(rc);
4498 BOOL fUseHostIOCache;
4499 rc = ctrl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
4500 AssertComRC(rc);
4501
4502 unsigned uLUN;
4503 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
4504 AssertComRCReturnRC(rc);
4505
4506 alock.release();
4507
4508 /* Pause the VM, as it might have pending IO on this drive */
4509 VMSTATE enmVMState = VMR3GetState(pVM);
4510 if (mMachineState == MachineState_DeletingSnapshotOnline)
4511 {
4512 LogFlowFunc(("Suspending the VM...\n"));
4513 /* disable the callback to prevent Console-level state change */
4514 mVMStateChangeCallbackDisabled = true;
4515 int vrc2 = VMR3Suspend(pVM);
4516 mVMStateChangeCallbackDisabled = false;
4517 AssertRCReturn(vrc2, E_FAIL);
4518 }
4519
4520 vrc = VMR3ReqCallWait(pVM,
4521 VMCPUID_ANY,
4522 (PFNRT)reconfigureMediumAttachment,
4523 12,
4524 this,
4525 pVM,
4526 pcszDevice,
4527 uInstance,
4528 enmBus,
4529 fUseHostIOCache,
4530 true /* fSetupMerge */,
4531 aSourceIdx,
4532 aTargetIdx,
4533 aMediumAttachment,
4534 mMachineState,
4535 &rc);
4536 /* error handling is after resuming the VM */
4537
4538 if (mMachineState == MachineState_DeletingSnapshotOnline)
4539 {
4540 LogFlowFunc(("Resuming the VM...\n"));
4541 /* disable the callback to prevent Console-level state change */
4542 mVMStateChangeCallbackDisabled = true;
4543 int vrc2 = VMR3Resume(pVM);
4544 mVMStateChangeCallbackDisabled = false;
4545 AssertRC(vrc2);
4546 if (RT_FAILURE(vrc2))
4547 {
4548 /* too bad, we failed. try to sync the console state with the VMM state */
4549 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4550 }
4551 }
4552
4553 if (RT_FAILURE(vrc))
4554 return setError(E_FAIL, tr("%Rrc"), vrc);
4555 if (FAILED(rc))
4556 return rc;
4557
4558 PPDMIBASE pIBase = NULL;
4559 PPDMIMEDIA pIMedium = NULL;
4560 vrc = PDMR3QueryDriverOnLun(pVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
4561 if (RT_SUCCESS(vrc))
4562 {
4563 if (pIBase)
4564 {
4565 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
4566 if (!pIMedium)
4567 return setError(E_FAIL, tr("could not query medium interface of controller"));
4568 }
4569 else
4570 return setError(E_FAIL, tr("could not query base interface of controller"));
4571 }
4572
4573 /* Finally trigger the merge. */
4574 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
4575 if (RT_FAILURE(vrc))
4576 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
4577
4578 /* Pause the VM, as it might have pending IO on this drive */
4579 enmVMState = VMR3GetState(pVM);
4580 if (mMachineState == MachineState_DeletingSnapshotOnline)
4581 {
4582 LogFlowFunc(("Suspending the VM...\n"));
4583 /* disable the callback to prevent Console-level state change */
4584 mVMStateChangeCallbackDisabled = true;
4585 int vrc2 = VMR3Suspend(pVM);
4586 mVMStateChangeCallbackDisabled = false;
4587 AssertRCReturn(vrc2, E_FAIL);
4588 }
4589
4590 /* Update medium chain and state now, so that the VM can continue. */
4591 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
4592 aMergeForward, aParentForTarget,
4593 ComSafeArrayInArg(aChildrenToReparent));
4594
4595 vrc = VMR3ReqCallWait(pVM,
4596 VMCPUID_ANY,
4597 (PFNRT)reconfigureMediumAttachment,
4598 12,
4599 this,
4600 pVM,
4601 pcszDevice,
4602 uInstance,
4603 enmBus,
4604 fUseHostIOCache,
4605 false /* fSetupMerge */,
4606 0 /* uMergeSource */,
4607 0 /* uMergeTarget */,
4608 aMediumAttachment,
4609 mMachineState,
4610 &rc);
4611 /* error handling is after resuming the VM */
4612
4613 if (mMachineState == MachineState_DeletingSnapshotOnline)
4614 {
4615 LogFlowFunc(("Resuming the VM...\n"));
4616 /* disable the callback to prevent Console-level state change */
4617 mVMStateChangeCallbackDisabled = true;
4618 int vrc2 = VMR3Resume(pVM);
4619 mVMStateChangeCallbackDisabled = false;
4620 AssertRC(vrc2);
4621 if (RT_FAILURE(vrc2))
4622 {
4623 /* too bad, we failed. try to sync the console state with the VMM state */
4624 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4625 }
4626 }
4627
4628 if (RT_FAILURE(vrc))
4629 return setError(E_FAIL, tr("%Rrc"), vrc);
4630 if (FAILED(rc))
4631 return rc;
4632
4633 return rc;
4634}
4635
4636
4637/**
4638 * Gets called by Session::UpdateMachineState()
4639 * (IInternalSessionControl::updateMachineState()).
4640 *
4641 * Must be called only in certain cases (see the implementation).
4642 *
4643 * @note Locks this object for writing.
4644 */
4645HRESULT Console::updateMachineState(MachineState_T aMachineState)
4646{
4647 AutoCaller autoCaller(this);
4648 AssertComRCReturnRC(autoCaller.rc());
4649
4650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4651
4652 AssertReturn( mMachineState == MachineState_Saving
4653 || mMachineState == MachineState_LiveSnapshotting
4654 || mMachineState == MachineState_RestoringSnapshot
4655 || mMachineState == MachineState_DeletingSnapshot
4656 || mMachineState == MachineState_DeletingSnapshotOnline
4657 || mMachineState == MachineState_DeletingSnapshotPaused
4658 , E_FAIL);
4659
4660 return setMachineStateLocally(aMachineState);
4661}
4662
4663/**
4664 * @note Locks this object for writing.
4665 */
4666void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4667 uint32_t xHot, uint32_t yHot,
4668 uint32_t width, uint32_t height,
4669 ComSafeArrayIn(BYTE,pShape))
4670{
4671#if 0
4672 LogFlowThisFuncEnter();
4673 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
4674 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4675#endif
4676
4677 AutoCaller autoCaller(this);
4678 AssertComRCReturnVoid(autoCaller.rc());
4679
4680 /* We need a write lock because we alter the cached callback data */
4681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 /* Save the callback arguments */
4684 mCallbackData.mpsc.visible = fVisible;
4685 mCallbackData.mpsc.alpha = fAlpha;
4686 mCallbackData.mpsc.xHot = xHot;
4687 mCallbackData.mpsc.yHot = yHot;
4688 mCallbackData.mpsc.width = width;
4689 mCallbackData.mpsc.height = height;
4690
4691 /* start with not valid */
4692 bool wasValid = mCallbackData.mpsc.valid;
4693 mCallbackData.mpsc.valid = false;
4694
4695 com::SafeArray <BYTE> aShape(ComSafeArrayInArg (pShape));
4696 if (aShape.size() != 0)
4697 mCallbackData.mpsc.shape.initFrom(aShape);
4698 else
4699 mCallbackData.mpsc.shape.resize(0);
4700 mCallbackData.mpsc.valid = true;
4701
4702 /**
4703 * Although looks stupid, this is result of fact that safearrays params in XPCOM
4704 * passed as separate pointer and length arguments.
4705 * @todo: better solution
4706 */
4707#ifdef RT_OS_WINDOWS
4708 CONSOLE_DO_CALLBACKS7(OnMousePointerShapeChanged, fVisible, fAlpha, xHot, yHot, width, height, pShape);
4709#else
4710 CONSOLE_DO_CALLBACKS8(OnMousePointerShapeChanged, fVisible, fAlpha, xHot, yHot, width, height, pShapeSize, pShape);
4711#endif
4712
4713#if 0
4714 LogFlowThisFuncLeave();
4715#endif
4716}
4717
4718/**
4719 * @note Locks this object for writing.
4720 */
4721void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
4722{
4723 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
4724 supportsAbsolute, supportsRelative, needsHostCursor));
4725
4726 AutoCaller autoCaller(this);
4727 AssertComRCReturnVoid(autoCaller.rc());
4728
4729 /* We need a write lock because we alter the cached callback data */
4730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4731
4732 /* save the callback arguments */
4733 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
4734 mCallbackData.mcc.supportsRelative = supportsRelative;
4735 mCallbackData.mcc.needsHostCursor = needsHostCursor;
4736 mCallbackData.mcc.valid = true;
4737
4738 CONSOLE_DO_CALLBACKS3(OnMouseCapabilityChanged, supportsAbsolute, supportsRelative, needsHostCursor);
4739}
4740
4741/**
4742 * @note Locks this object for reading.
4743 */
4744void Console::onStateChange(MachineState_T machineState)
4745{
4746 AutoCaller autoCaller(this);
4747 AssertComRCReturnVoid(autoCaller.rc());
4748
4749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4750 CONSOLE_DO_CALLBACKS1(OnStateChanged, machineState);
4751}
4752
4753/**
4754 * @note Locks this object for reading.
4755 */
4756void Console::onAdditionsStateChange()
4757{
4758 AutoCaller autoCaller(this);
4759 AssertComRCReturnVoid(autoCaller.rc());
4760
4761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4762 CONSOLE_DO_CALLBACKS0(OnAdditionsStateChanged);
4763}
4764
4765/**
4766 * @note Locks this object for reading.
4767 * This notification only is for reporting an incompatible
4768 * Guest Additions interface, *not* the Guest Additions version!
4769 *
4770 * The user will be notified inside the guest if new Guest
4771 * Additions are available (via VBoxTray/VBoxClient).
4772 */
4773void Console::onAdditionsOutdated()
4774{
4775 AutoCaller autoCaller(this);
4776 AssertComRCReturnVoid(autoCaller.rc());
4777
4778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4779}
4780
4781/**
4782 * @note Locks this object for writing.
4783 */
4784void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
4785{
4786 AutoCaller autoCaller(this);
4787 AssertComRCReturnVoid(autoCaller.rc());
4788
4789 /* We need a write lock because we alter the cached callback data */
4790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4791
4792 /* save the callback arguments */
4793 mCallbackData.klc.numLock = fNumLock;
4794 mCallbackData.klc.capsLock = fCapsLock;
4795 mCallbackData.klc.scrollLock = fScrollLock;
4796 mCallbackData.klc.valid = true;
4797
4798 CONSOLE_DO_CALLBACKS3(OnKeyboardLedsChanged, fNumLock, fCapsLock, fScrollLock);
4799}
4800
4801/**
4802 * @note Locks this object for reading.
4803 */
4804void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
4805 IVirtualBoxErrorInfo *aError)
4806{
4807 AutoCaller autoCaller(this);
4808 AssertComRCReturnVoid(autoCaller.rc());
4809
4810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4811 CONSOLE_DO_CALLBACKS3(OnUSBDeviceStateChanged, aDevice, aAttached, aError);
4812}
4813
4814/**
4815 * @note Locks this object for reading.
4816 */
4817void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
4818{
4819 AutoCaller autoCaller(this);
4820 AssertComRCReturnVoid(autoCaller.rc());
4821
4822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4823 CONSOLE_DO_CALLBACKS3(OnRuntimeError, aFatal, aErrorID, aMessage);
4824}
4825
4826/**
4827 * @note Locks this object for reading.
4828 */
4829HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
4830{
4831 AssertReturn(aCanShow, E_POINTER);
4832 AssertReturn(aWinId, E_POINTER);
4833
4834 *aCanShow = FALSE;
4835 *aWinId = 0;
4836
4837 AutoCaller autoCaller(this);
4838 AssertComRCReturnRC(autoCaller.rc());
4839
4840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4841 VBoxEventDesc evDesc;
4842
4843 if (aCheck)
4844 {
4845 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
4846 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
4847 //Assert(fDelivered);
4848 if (fDelivered)
4849 {
4850 ComPtr<IEvent> aEvent;
4851 evDesc.getEvent(aEvent.asOutParam());
4852 // bit clumsy
4853 ComPtr<ICanShowWindowEvent> aCanShowEvent = aEvent;
4854 if (aCanShowEvent)
4855 {
4856 BOOL fVetoed = FALSE;
4857 aCanShowEvent->IsVetoed(&fVetoed);
4858 *aCanShow = !fVetoed;
4859 }
4860 else
4861 {
4862 Assert(FALSE);
4863 *aCanShow = TRUE;
4864 }
4865 }
4866 else
4867 *aCanShow = TRUE;
4868 }
4869 else
4870 {
4871 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
4872 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
4873 //Assert(fDelivered);
4874 if (fDelivered)
4875 {
4876 ComPtr<IEvent> aEvent;
4877 evDesc.getEvent(aEvent.asOutParam());
4878 ComPtr<IShowWindowEvent> aShowEvent = aEvent;
4879 LONG64 aEvWinId = 0;
4880 if (aShowEvent)
4881 {
4882 aShowEvent->COMGETTER(WinId)(&aEvWinId);
4883 if ((aEvWinId != 0) && (*aWinId == 0))
4884 *aWinId = aEvWinId;
4885 }
4886 else
4887 Assert(FALSE);
4888 }
4889 }
4890
4891 return S_OK;
4892}
4893
4894// private methods
4895////////////////////////////////////////////////////////////////////////////////
4896
4897/**
4898 * Increases the usage counter of the mpVM pointer. Guarantees that
4899 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
4900 * is called.
4901 *
4902 * If this method returns a failure, the caller is not allowed to use mpVM
4903 * and may return the failed result code to the upper level. This method sets
4904 * the extended error info on failure if \a aQuiet is false.
4905 *
4906 * Setting \a aQuiet to true is useful for methods that don't want to return
4907 * the failed result code to the caller when this method fails (e.g. need to
4908 * silently check for the mpVM availability).
4909 *
4910 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
4911 * returned instead of asserting. Having it false is intended as a sanity check
4912 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
4913 *
4914 * @param aQuiet true to suppress setting error info
4915 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
4916 * (otherwise this method will assert if mpVM is NULL)
4917 *
4918 * @note Locks this object for writing.
4919 */
4920HRESULT Console::addVMCaller(bool aQuiet /* = false */,
4921 bool aAllowNullVM /* = false */)
4922{
4923 AutoCaller autoCaller(this);
4924 AssertComRCReturnRC(autoCaller.rc());
4925
4926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4927
4928 if (mVMDestroying)
4929 {
4930 /* powerDown() is waiting for all callers to finish */
4931 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
4932 tr("The virtual machine is being powered down"));
4933 }
4934
4935 if (mpVM == NULL)
4936 {
4937 Assert(aAllowNullVM == true);
4938
4939 /* The machine is not powered up */
4940 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
4941 tr("The virtual machine is not powered up"));
4942 }
4943
4944 ++ mVMCallers;
4945
4946 return S_OK;
4947}
4948
4949/**
4950 * Decreases the usage counter of the mpVM pointer. Must always complete
4951 * the addVMCaller() call after the mpVM pointer is no more necessary.
4952 *
4953 * @note Locks this object for writing.
4954 */
4955void Console::releaseVMCaller()
4956{
4957 AutoCaller autoCaller(this);
4958 AssertComRCReturnVoid(autoCaller.rc());
4959
4960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4961
4962 AssertReturnVoid(mpVM != NULL);
4963
4964 Assert(mVMCallers > 0);
4965 --mVMCallers;
4966
4967 if (mVMCallers == 0 && mVMDestroying)
4968 {
4969 /* inform powerDown() there are no more callers */
4970 RTSemEventSignal(mVMZeroCallersSem);
4971 }
4972}
4973
4974/**
4975 * Initialize the release logging facility. In case something
4976 * goes wrong, there will be no release logging. Maybe in the future
4977 * we can add some logic to use different file names in this case.
4978 * Note that the logic must be in sync with Machine::DeleteSettings().
4979 */
4980HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
4981{
4982 HRESULT hrc = S_OK;
4983
4984 Bstr logFolder;
4985 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
4986 if (FAILED(hrc)) return hrc;
4987
4988 Utf8Str logDir = logFolder;
4989
4990 /* make sure the Logs folder exists */
4991 Assert(logDir.length());
4992 if (!RTDirExists(logDir.c_str()))
4993 RTDirCreateFullPath(logDir.c_str(), 0777);
4994
4995 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
4996 logDir.c_str(), RTPATH_DELIMITER);
4997 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
4998 logDir.c_str(), RTPATH_DELIMITER);
4999
5000 /*
5001 * Age the old log files
5002 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
5003 * Overwrite target files in case they exist.
5004 */
5005 ComPtr<IVirtualBox> virtualBox;
5006 aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5007 ComPtr<ISystemProperties> systemProperties;
5008 virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5009 ULONG cHistoryFiles = 3;
5010 systemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
5011 if (cHistoryFiles)
5012 {
5013 for (int i = cHistoryFiles-1; i >= 0; i--)
5014 {
5015 Utf8Str *files[] = { &logFile, &pngFile };
5016 Utf8Str oldName, newName;
5017
5018 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
5019 {
5020 if (i > 0)
5021 oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
5022 else
5023 oldName = *files[j];
5024 newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
5025 /* If the old file doesn't exist, delete the new file (if it
5026 * exists) to provide correct rotation even if the sequence is
5027 * broken */
5028 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
5029 == VERR_FILE_NOT_FOUND)
5030 RTFileDelete(newName.c_str());
5031 }
5032 }
5033 }
5034
5035 PRTLOGGER loggerRelease;
5036 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
5037 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
5038#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5039 fFlags |= RTLOGFLAGS_USECRLF;
5040#endif
5041 char szError[RTPATH_MAX + 128] = "";
5042 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
5043 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
5044 RTLOGDEST_FILE, szError, sizeof(szError), logFile.c_str());
5045 if (RT_SUCCESS(vrc))
5046 {
5047 /* some introductory information */
5048 RTTIMESPEC timeSpec;
5049 char szTmp[256];
5050 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
5051 RTLogRelLogger(loggerRelease, 0, ~0U,
5052 "VirtualBox %s r%u %s (%s %s) release log\n"
5053#ifdef VBOX_BLEEDING_EDGE
5054 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
5055#endif
5056 "Log opened %s\n",
5057 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
5058 __DATE__, __TIME__, szTmp);
5059
5060 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
5061 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5062 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
5063 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
5064 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5065 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
5066 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
5067 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5068 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
5069 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
5070 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5071 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
5072 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
5073 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5074 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp);
5075 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
5076 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5077 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp);
5078
5079 ComPtr<IHost> host;
5080 virtualBox->COMGETTER(Host)(host.asOutParam());
5081 ULONG cMbHostRam = 0;
5082 ULONG cMbHostRamAvail = 0;
5083 host->COMGETTER(MemorySize)(&cMbHostRam);
5084 host->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5085 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5086 cMbHostRam, cMbHostRamAvail);
5087
5088 /* the package type is interesting for Linux distributions */
5089 char szExecName[RTPATH_MAX];
5090 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
5091 RTLogRelLogger(loggerRelease, 0, ~0U,
5092 "Executable: %s\n"
5093 "Process ID: %u\n"
5094 "Package type: %s"
5095#ifdef VBOX_OSE
5096 " (OSE)"
5097#endif
5098 "\n",
5099 pszExecName ? pszExecName : "unknown",
5100 RTProcSelf(),
5101 VBOX_PACKAGE_STRING);
5102
5103 /* register this logger as the release logger */
5104 RTLogRelSetDefaultInstance(loggerRelease);
5105 hrc = S_OK;
5106
5107 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5108 RTLogFlush(loggerRelease);
5109 }
5110 else
5111 hrc = setError(E_FAIL,
5112 tr("Failed to open release log (%s, %Rrc)"),
5113 szError, vrc);
5114
5115 /* If we've made any directory changes, flush the directory to increase
5116 the likelyhood that the log file will be usable after a system panic.
5117
5118 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5119 is missing. Just don't have too high hopes for this to help. */
5120 if (SUCCEEDED(hrc) || cHistoryFiles)
5121 RTDirFlush(logDir.c_str());
5122
5123 return hrc;
5124}
5125
5126/**
5127 * Common worker for PowerUp and PowerUpPaused.
5128 *
5129 * @returns COM status code.
5130 *
5131 * @param aProgress Where to return the progress object.
5132 * @param aPaused true if PowerUpPaused called.
5133 *
5134 * @todo move down to powerDown();
5135 */
5136HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5137{
5138 if (aProgress == NULL)
5139 return E_POINTER;
5140
5141 LogFlowThisFuncEnter();
5142 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5143
5144 AutoCaller autoCaller(this);
5145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5146
5147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5148
5149 if (Global::IsOnlineOrTransient(mMachineState))
5150 return setError(VBOX_E_INVALID_VM_STATE,
5151 tr("The virtual machine is already running or busy (machine state: %s)"),
5152 Global::stringifyMachineState(mMachineState));
5153
5154 HRESULT rc = S_OK;
5155
5156 /* the network cards will undergo a quick consistency check */
5157 for (ULONG slot = 0;
5158 slot < SchemaDefs::NetworkAdapterCount;
5159 ++slot)
5160 {
5161 ComPtr<INetworkAdapter> adapter;
5162 mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
5163 BOOL enabled = FALSE;
5164 adapter->COMGETTER(Enabled)(&enabled);
5165 if (!enabled)
5166 continue;
5167
5168 NetworkAttachmentType_T netattach;
5169 adapter->COMGETTER(AttachmentType)(&netattach);
5170 switch (netattach)
5171 {
5172 case NetworkAttachmentType_Bridged:
5173 {
5174#ifdef RT_OS_WINDOWS
5175 /* a valid host interface must have been set */
5176 Bstr hostif;
5177 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
5178 if (!hostif)
5179 {
5180 return setError(VBOX_E_HOST_ERROR,
5181 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5182 }
5183 ComPtr<IVirtualBox> virtualBox;
5184 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5185 ComPtr<IHost> host;
5186 virtualBox->COMGETTER(Host)(host.asOutParam());
5187 ComPtr<IHostNetworkInterface> hostInterface;
5188 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif.raw(),
5189 hostInterface.asOutParam())))
5190 {
5191 return setError(VBOX_E_HOST_ERROR,
5192 tr("VM cannot start because the host interface '%ls' does not exist"),
5193 hostif.raw());
5194 }
5195#endif /* RT_OS_WINDOWS */
5196 break;
5197 }
5198 default:
5199 break;
5200 }
5201 }
5202
5203 /* Read console data stored in the saved state file (if not yet done) */
5204 rc = loadDataFromSavedState();
5205 if (FAILED(rc)) return rc;
5206
5207 /* Check all types of shared folders and compose a single list */
5208 SharedFolderDataMap sharedFolders;
5209 {
5210 /* first, insert global folders */
5211 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5212 it != mGlobalSharedFolders.end(); ++ it)
5213 sharedFolders[it->first] = it->second;
5214
5215 /* second, insert machine folders */
5216 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5217 it != mMachineSharedFolders.end(); ++ it)
5218 sharedFolders[it->first] = it->second;
5219
5220 /* third, insert console folders */
5221 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5222 it != mSharedFolders.end(); ++ it)
5223 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(),
5224 it->second->isWritable(),
5225 it->second->isAutoMounted());
5226 }
5227
5228 Bstr savedStateFile;
5229
5230 /*
5231 * Saved VMs will have to prove that their saved states seem kosher.
5232 */
5233 if (mMachineState == MachineState_Saved)
5234 {
5235 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5236 if (FAILED(rc)) return rc;
5237 ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
5238 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5239 if (RT_FAILURE(vrc))
5240 return setError(VBOX_E_FILE_ERROR,
5241 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5242 savedStateFile.raw(), vrc);
5243 }
5244
5245 /* test and clear the TeleporterEnabled property */
5246 BOOL fTeleporterEnabled;
5247 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5248 if (FAILED(rc)) return rc;
5249#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5250 if (fTeleporterEnabled)
5251 {
5252 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5253 if (FAILED(rc)) return rc;
5254 }
5255#endif
5256
5257 /* test the FaultToleranceState property */
5258 FaultToleranceState_T enmFaultToleranceState;
5259 rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
5260 if (FAILED(rc)) return rc;
5261 BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
5262
5263 /* create a progress object to track progress of this operation */
5264 ComObjPtr<Progress> powerupProgress;
5265 powerupProgress.createObject();
5266 Bstr progressDesc;
5267 if (mMachineState == MachineState_Saved)
5268 progressDesc = tr("Restoring virtual machine");
5269 else if (fTeleporterEnabled)
5270 progressDesc = tr("Teleporting virtual machine");
5271 else if (fFaultToleranceSyncEnabled)
5272 progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
5273 else
5274 progressDesc = tr("Starting virtual machine");
5275 if ( mMachineState == MachineState_Saved
5276 || (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
5277 rc = powerupProgress->init(static_cast<IConsole *>(this),
5278 progressDesc.raw(),
5279 FALSE /* aCancelable */);
5280 else
5281 if (fTeleporterEnabled)
5282 rc = powerupProgress->init(static_cast<IConsole *>(this),
5283 progressDesc.raw(),
5284 TRUE /* aCancelable */,
5285 3 /* cOperations */,
5286 10 /* ulTotalOperationsWeight */,
5287 Bstr(tr("Teleporting virtual machine")).raw(),
5288 1 /* ulFirstOperationWeight */,
5289 NULL);
5290 else
5291 if (fFaultToleranceSyncEnabled)
5292 rc = powerupProgress->init(static_cast<IConsole *>(this),
5293 progressDesc.raw(),
5294 TRUE /* aCancelable */,
5295 3 /* cOperations */,
5296 10 /* ulTotalOperationsWeight */,
5297 Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
5298 1 /* ulFirstOperationWeight */,
5299 NULL);
5300
5301 if (FAILED(rc))
5302 return rc;
5303
5304 /* Tell VBoxSVC and Machine about the progress object so they can combine
5305 proxy it to any openRemoteSession caller. */
5306 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
5307 rc = mControl->BeginPowerUp(powerupProgress);
5308 if (FAILED(rc))
5309 {
5310 LogFlowThisFunc(("BeginPowerUp failed\n"));
5311 return rc;
5312 }
5313
5314 LogFlowThisFunc(("Checking if canceled...\n"));
5315 BOOL fCanceled;
5316 rc = powerupProgress->COMGETTER(Canceled)(&fCanceled);
5317 if (FAILED(rc))
5318 return rc;
5319 if (fCanceled)
5320 {
5321 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5322 return setError(E_FAIL, tr("Powerup was canceled"));
5323 }
5324 LogFlowThisFunc(("Not canceled yet.\n"));
5325
5326 /* setup task object and thread to carry out the operation
5327 * asynchronously */
5328
5329 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
5330 ComAssertComRCRetRC(task->rc());
5331
5332 task->mConfigConstructor = configConstructor;
5333 task->mSharedFolders = sharedFolders;
5334 task->mStartPaused = aPaused;
5335 if (mMachineState == MachineState_Saved)
5336 task->mSavedStateFile = savedStateFile;
5337 task->mTeleporterEnabled = fTeleporterEnabled;
5338 task->mEnmFaultToleranceState = enmFaultToleranceState;
5339
5340 /* Reset differencing hard disks for which autoReset is true,
5341 * but only if the machine has no snapshots OR the current snapshot
5342 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5343 * image of an ONLINE snapshot which contains the disk state of the machine
5344 * while it was previously running, but without the corresponding machine
5345 * state, which is equivalent to powering off a running machine and not
5346 * good idea
5347 */
5348 ComPtr<ISnapshot> pCurrentSnapshot;
5349 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5350 if (FAILED(rc)) return rc;
5351
5352 BOOL fCurrentSnapshotIsOnline = false;
5353 if (pCurrentSnapshot)
5354 {
5355 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5356 if (FAILED(rc)) return rc;
5357 }
5358
5359 if (!fCurrentSnapshotIsOnline)
5360 {
5361 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5362
5363 com::SafeIfaceArray<IMediumAttachment> atts;
5364 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5365 if (FAILED(rc)) return rc;
5366
5367 for (size_t i = 0;
5368 i < atts.size();
5369 ++i)
5370 {
5371 DeviceType_T devType;
5372 rc = atts[i]->COMGETTER(Type)(&devType);
5373 /** @todo later applies to floppies as well */
5374 if (devType == DeviceType_HardDisk)
5375 {
5376 ComPtr<IMedium> medium;
5377 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
5378 if (FAILED(rc)) return rc;
5379
5380 /* needs autoreset? */
5381 BOOL autoReset = FALSE;
5382 rc = medium->COMGETTER(AutoReset)(&autoReset);
5383 if (FAILED(rc)) return rc;
5384
5385 if (autoReset)
5386 {
5387 ComPtr<IProgress> resetProgress;
5388 rc = medium->Reset(resetProgress.asOutParam());
5389 if (FAILED(rc)) return rc;
5390
5391 /* save for later use on the powerup thread */
5392 task->hardDiskProgresses.push_back(resetProgress);
5393 }
5394 }
5395 }
5396 }
5397 else
5398 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5399
5400 rc = consoleInitReleaseLog(mMachine);
5401 if (FAILED(rc)) return rc;
5402
5403#ifdef RT_OS_SOLARIS
5404 /* setup host core dumper for the VM */
5405 Bstr value;
5406 HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
5407 if (SUCCEEDED(hrc) && value == "1")
5408 {
5409 Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
5410 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
5411 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
5412 mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
5413
5414 uint32_t fCoreFlags = 0;
5415 if ( coreDumpReplaceSys.isEmpty() == false
5416 && Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
5417 {
5418 fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
5419 }
5420
5421 if ( coreDumpLive.isEmpty() == false
5422 && Utf8Str(coreDumpLive).toUInt32() == 1)
5423 {
5424 fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
5425 }
5426
5427 const char *pszDumpDir = Utf8Str(coreDumpDir).c_str();
5428 if ( pszDumpDir
5429 && *pszDumpDir == '\0')
5430 pszDumpDir = NULL;
5431
5432 int vrc;
5433 if ( pszDumpDir
5434 && !RTDirExists(pszDumpDir))
5435 {
5436 /*
5437 * Try create the directory.
5438 */
5439 vrc = RTDirCreateFullPath(pszDumpDir, 0777);
5440 if (RT_FAILURE(vrc))
5441 return setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
5442 }
5443
5444 vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
5445 if (RT_FAILURE(vrc))
5446 return setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
5447 else
5448 LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
5449 }
5450#endif
5451
5452 /* pass the progress object to the caller if requested */
5453 if (aProgress)
5454 {
5455 if (task->hardDiskProgresses.size() == 0)
5456 {
5457 /* there are no other operations to track, return the powerup
5458 * progress only */
5459 powerupProgress.queryInterfaceTo(aProgress);
5460 }
5461 else
5462 {
5463 /* create a combined progress object */
5464 ComObjPtr<CombinedProgress> progress;
5465 progress.createObject();
5466 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5467 progresses.push_back(ComPtr<IProgress> (powerupProgress));
5468 rc = progress->init(static_cast<IConsole *>(this),
5469 progressDesc.raw(), progresses.begin(),
5470 progresses.end());
5471 AssertComRCReturnRC(rc);
5472 progress.queryInterfaceTo(aProgress);
5473 }
5474 }
5475
5476 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5477 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5478 if (RT_FAILURE(vrc))
5479 return setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
5480
5481 /* task is now owned by powerUpThread(), so release it */
5482 task.release();
5483
5484 /* finally, set the state: no right to fail in this method afterwards
5485 * since we've already started the thread and it is now responsible for
5486 * any error reporting and appropriate state change! */
5487
5488 if (mMachineState == MachineState_Saved)
5489 setMachineState(MachineState_Restoring);
5490 else if (fTeleporterEnabled)
5491 setMachineState(MachineState_TeleportingIn);
5492 else if (enmFaultToleranceState == FaultToleranceState_Standby)
5493 setMachineState(MachineState_FaultTolerantSyncing);
5494 else
5495 setMachineState(MachineState_Starting);
5496
5497 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5498 LogFlowThisFuncLeave();
5499 return S_OK;
5500}
5501
5502/**
5503 * Internal power off worker routine.
5504 *
5505 * This method may be called only at certain places with the following meaning
5506 * as shown below:
5507 *
5508 * - if the machine state is either Running or Paused, a normal
5509 * Console-initiated powerdown takes place (e.g. PowerDown());
5510 * - if the machine state is Saving, saveStateThread() has successfully done its
5511 * job;
5512 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5513 * to start/load the VM;
5514 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5515 * as a result of the powerDown() call).
5516 *
5517 * Calling it in situations other than the above will cause unexpected behavior.
5518 *
5519 * Note that this method should be the only one that destroys mpVM and sets it
5520 * to NULL.
5521 *
5522 * @param aProgress Progress object to run (may be NULL).
5523 *
5524 * @note Locks this object for writing.
5525 *
5526 * @note Never call this method from a thread that called addVMCaller() or
5527 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5528 * release(). Otherwise it will deadlock.
5529 */
5530HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5531{
5532 LogFlowThisFuncEnter();
5533
5534 AutoCaller autoCaller(this);
5535 AssertComRCReturnRC(autoCaller.rc());
5536
5537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5538
5539 /* Total # of steps for the progress object. Must correspond to the
5540 * number of "advance percent count" comments in this method! */
5541 enum { StepCount = 7 };
5542 /* current step */
5543 ULONG step = 0;
5544
5545 HRESULT rc = S_OK;
5546 int vrc = VINF_SUCCESS;
5547
5548 /* sanity */
5549 Assert(mVMDestroying == false);
5550
5551 Assert(mpVM != NULL);
5552
5553 AssertMsg( mMachineState == MachineState_Running
5554 || mMachineState == MachineState_Paused
5555 || mMachineState == MachineState_Stuck
5556 || mMachineState == MachineState_Starting
5557 || mMachineState == MachineState_Stopping
5558 || mMachineState == MachineState_Saving
5559 || mMachineState == MachineState_Restoring
5560 || mMachineState == MachineState_TeleportingPausedVM
5561 || mMachineState == MachineState_FaultTolerantSyncing
5562 || mMachineState == MachineState_TeleportingIn
5563 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5564
5565 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5566 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5567
5568 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5569 * VM has already powered itself off in vmstateChangeCallback() and is just
5570 * notifying Console about that. In case of Starting or Restoring,
5571 * powerUpThread() is calling us on failure, so the VM is already off at
5572 * that point. */
5573 if ( !mVMPoweredOff
5574 && ( mMachineState == MachineState_Starting
5575 || mMachineState == MachineState_Restoring
5576 || mMachineState == MachineState_FaultTolerantSyncing
5577 || mMachineState == MachineState_TeleportingIn)
5578 )
5579 mVMPoweredOff = true;
5580
5581 /*
5582 * Go to Stopping state if not already there.
5583 *
5584 * Note that we don't go from Saving/Restoring to Stopping because
5585 * vmstateChangeCallback() needs it to set the state to Saved on
5586 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5587 * while leaving the lock below, Saving or Restoring should be fine too.
5588 * Ditto for TeleportingPausedVM -> Teleported.
5589 */
5590 if ( mMachineState != MachineState_Saving
5591 && mMachineState != MachineState_Restoring
5592 && mMachineState != MachineState_Stopping
5593 && mMachineState != MachineState_TeleportingIn
5594 && mMachineState != MachineState_TeleportingPausedVM
5595 && mMachineState != MachineState_FaultTolerantSyncing
5596 )
5597 setMachineState(MachineState_Stopping);
5598
5599 /* ----------------------------------------------------------------------
5600 * DONE with necessary state changes, perform the power down actions (it's
5601 * safe to leave the object lock now if needed)
5602 * ---------------------------------------------------------------------- */
5603
5604 /* Stop the VRDP server to prevent new clients connection while VM is being
5605 * powered off. */
5606 if (mConsoleVRDPServer)
5607 {
5608 LogFlowThisFunc(("Stopping VRDP server...\n"));
5609
5610 /* Leave the lock since EMT will call us back as addVMCaller()
5611 * in updateDisplayData(). */
5612 alock.leave();
5613
5614 mConsoleVRDPServer->Stop();
5615
5616 alock.enter();
5617 }
5618
5619 /* advance percent count */
5620 if (aProgress)
5621 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5622
5623
5624 /* ----------------------------------------------------------------------
5625 * Now, wait for all mpVM callers to finish their work if there are still
5626 * some on other threads. NO methods that need mpVM (or initiate other calls
5627 * that need it) may be called after this point
5628 * ---------------------------------------------------------------------- */
5629
5630 /* go to the destroying state to prevent from adding new callers */
5631 mVMDestroying = true;
5632
5633 if (mVMCallers > 0)
5634 {
5635 /* lazy creation */
5636 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5637 RTSemEventCreate(&mVMZeroCallersSem);
5638
5639 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5640 mVMCallers));
5641
5642 alock.leave();
5643
5644 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5645
5646 alock.enter();
5647 }
5648
5649 /* advance percent count */
5650 if (aProgress)
5651 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5652
5653 vrc = VINF_SUCCESS;
5654
5655 /*
5656 * Power off the VM if not already done that.
5657 * Leave the lock since EMT will call vmstateChangeCallback.
5658 *
5659 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5660 * VM-(guest-)initiated power off happened in parallel a ms before this
5661 * call. So far, we let this error pop up on the user's side.
5662 */
5663 if (!mVMPoweredOff)
5664 {
5665 LogFlowThisFunc(("Powering off the VM...\n"));
5666 alock.leave();
5667 vrc = VMR3PowerOff(mpVM);
5668 alock.enter();
5669 }
5670 else
5671 {
5672 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5673 * off. */
5674 /* reset the flag for future re-use */
5675 mVMPoweredOff = false;
5676 }
5677
5678 /* advance percent count */
5679 if (aProgress)
5680 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5681
5682#ifdef VBOX_WITH_HGCM
5683 /* Shutdown HGCM services before destroying the VM. */
5684 if (mVMMDev)
5685 {
5686 LogFlowThisFunc(("Shutdown HGCM...\n"));
5687
5688 /* Leave the lock since EMT will call us back as addVMCaller() */
5689 alock.leave();
5690
5691 mVMMDev->hgcmShutdown();
5692
5693 alock.enter();
5694 }
5695
5696 /* advance percent count */
5697 if (aProgress)
5698 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5699
5700#endif /* VBOX_WITH_HGCM */
5701
5702 LogFlowThisFunc(("Ready for VM destruction.\n"));
5703
5704 /* If we are called from Console::uninit(), then try to destroy the VM even
5705 * on failure (this will most likely fail too, but what to do?..) */
5706 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5707 {
5708 /* If the machine has an USB controller, release all USB devices
5709 * (symmetric to the code in captureUSBDevices()) */
5710 bool fHasUSBController = false;
5711 {
5712 PPDMIBASE pBase;
5713 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5714 if (RT_SUCCESS(vrc))
5715 {
5716 fHasUSBController = true;
5717 detachAllUSBDevices(false /* aDone */);
5718 }
5719 }
5720
5721 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5722 * this point). We leave the lock before calling VMR3Destroy() because
5723 * it will result into calling destructors of drivers associated with
5724 * Console children which may in turn try to lock Console (e.g. by
5725 * instantiating SafeVMPtr to access mpVM). It's safe here because
5726 * mVMDestroying is set which should prevent any activity. */
5727
5728 /* Set mpVM to NULL early just in case if some old code is not using
5729 * addVMCaller()/releaseVMCaller(). */
5730 PVM pVM = mpVM;
5731 mpVM = NULL;
5732
5733 LogFlowThisFunc(("Destroying the VM...\n"));
5734
5735 alock.leave();
5736
5737 vrc = VMR3Destroy(pVM);
5738
5739 /* take the lock again */
5740 alock.enter();
5741
5742 /* advance percent count */
5743 if (aProgress)
5744 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5745
5746 if (RT_SUCCESS(vrc))
5747 {
5748 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5749 mMachineState));
5750 /* Note: the Console-level machine state change happens on the
5751 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5752 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5753 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5754 * occurred yet. This is okay, because mMachineState is already
5755 * Stopping in this case, so any other attempt to call PowerDown()
5756 * will be rejected. */
5757 }
5758 else
5759 {
5760 /* bad bad bad, but what to do? */
5761 mpVM = pVM;
5762 rc = setError(VBOX_E_VM_ERROR,
5763 tr("Could not destroy the machine. (Error: %Rrc)"),
5764 vrc);
5765 }
5766
5767 /* Complete the detaching of the USB devices. */
5768 if (fHasUSBController)
5769 detachAllUSBDevices(true /* aDone */);
5770
5771 /* advance percent count */
5772 if (aProgress)
5773 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5774 }
5775 else
5776 {
5777 rc = setError(VBOX_E_VM_ERROR,
5778 tr("Could not power off the machine. (Error: %Rrc)"),
5779 vrc);
5780 }
5781
5782 /* Finished with destruction. Note that if something impossible happened and
5783 * we've failed to destroy the VM, mVMDestroying will remain true and
5784 * mMachineState will be something like Stopping, so most Console methods
5785 * will return an error to the caller. */
5786 if (mpVM == NULL)
5787 mVMDestroying = false;
5788
5789 if (SUCCEEDED(rc))
5790 mCallbackData.clear();
5791
5792 /* complete the progress */
5793 if (aProgress)
5794 aProgress->notifyComplete(rc);
5795
5796 LogFlowThisFuncLeave();
5797 return rc;
5798}
5799
5800/**
5801 * @note Locks this object for writing.
5802 */
5803HRESULT Console::setMachineState(MachineState_T aMachineState,
5804 bool aUpdateServer /* = true */)
5805{
5806 AutoCaller autoCaller(this);
5807 AssertComRCReturnRC(autoCaller.rc());
5808
5809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5810
5811 HRESULT rc = S_OK;
5812
5813 if (mMachineState != aMachineState)
5814 {
5815 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5816 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5817 mMachineState = aMachineState;
5818
5819 /// @todo (dmik)
5820 // possibly, we need to redo onStateChange() using the dedicated
5821 // Event thread, like it is done in VirtualBox. This will make it
5822 // much safer (no deadlocks possible if someone tries to use the
5823 // console from the callback), however, listeners will lose the
5824 // ability to synchronously react to state changes (is it really
5825 // necessary??)
5826 LogFlowThisFunc(("Doing onStateChange()...\n"));
5827 onStateChange(aMachineState);
5828 LogFlowThisFunc(("Done onStateChange()\n"));
5829
5830 if (aUpdateServer)
5831 {
5832 /* Server notification MUST be done from under the lock; otherwise
5833 * the machine state here and on the server might go out of sync
5834 * which can lead to various unexpected results (like the machine
5835 * state being >= MachineState_Running on the server, while the
5836 * session state is already SessionState_Unlocked at the same time
5837 * there).
5838 *
5839 * Cross-lock conditions should be carefully watched out: calling
5840 * UpdateState we will require Machine and SessionMachine locks
5841 * (remember that here we're holding the Console lock here, and also
5842 * all locks that have been entered by the thread before calling
5843 * this method).
5844 */
5845 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5846 rc = mControl->UpdateState(aMachineState);
5847 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5848 }
5849 }
5850
5851 return rc;
5852}
5853
5854/**
5855 * Searches for a shared folder with the given logical name
5856 * in the collection of shared folders.
5857 *
5858 * @param aName logical name of the shared folder
5859 * @param aSharedFolder where to return the found object
5860 * @param aSetError whether to set the error info if the folder is
5861 * not found
5862 * @return
5863 * S_OK when found or E_INVALIDARG when not found
5864 *
5865 * @note The caller must lock this object for writing.
5866 */
5867HRESULT Console::findSharedFolder(CBSTR aName,
5868 ComObjPtr<SharedFolder> &aSharedFolder,
5869 bool aSetError /* = false */)
5870{
5871 /* sanity check */
5872 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5873
5874 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5875 if (it != mSharedFolders.end())
5876 {
5877 aSharedFolder = it->second;
5878 return S_OK;
5879 }
5880
5881 if (aSetError)
5882 setError(VBOX_E_FILE_ERROR,
5883 tr("Could not find a shared folder named '%ls'."),
5884 aName);
5885
5886 return VBOX_E_FILE_ERROR;
5887}
5888
5889/**
5890 * Fetches the list of global or machine shared folders from the server.
5891 *
5892 * @param aGlobal true to fetch global folders.
5893 *
5894 * @note The caller must lock this object for writing.
5895 */
5896HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5897{
5898 /* sanity check */
5899 AssertReturn(AutoCaller(this).state() == InInit ||
5900 isWriteLockOnCurrentThread(), E_FAIL);
5901
5902 /* protect mpVM (if not NULL) */
5903 AutoVMCallerQuietWeak autoVMCaller(this);
5904
5905 HRESULT rc = S_OK;
5906
5907 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5908
5909 if (aGlobal)
5910 {
5911 /// @todo grab & process global folders when they are done
5912 }
5913 else
5914 {
5915 SharedFolderDataMap oldFolders;
5916 if (online)
5917 oldFolders = mMachineSharedFolders;
5918
5919 mMachineSharedFolders.clear();
5920
5921 SafeIfaceArray<ISharedFolder> folders;
5922 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
5923 AssertComRCReturnRC(rc);
5924
5925 for (size_t i = 0; i < folders.size(); ++i)
5926 {
5927 ComPtr<ISharedFolder> folder = folders[i];
5928
5929 Bstr name;
5930 Bstr hostPath;
5931 BOOL writable;
5932 BOOL autoMount;
5933
5934 rc = folder->COMGETTER(Name)(name.asOutParam());
5935 if (FAILED(rc)) break;
5936 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
5937 if (FAILED(rc)) break;
5938 rc = folder->COMGETTER(Writable)(&writable);
5939 if (FAILED(rc)) break;
5940 rc = folder->COMGETTER(AutoMount)(&autoMount);
5941 if (FAILED(rc)) break;
5942
5943 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable, autoMount)));
5944
5945 /* send changes to HGCM if the VM is running */
5946 /// @todo report errors as runtime warnings through VMSetError
5947 if (online)
5948 {
5949 SharedFolderDataMap::iterator it = oldFolders.find(name);
5950 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5951 {
5952 /* a new machine folder is added or
5953 * the existing machine folder is changed */
5954 if (mSharedFolders.find(name) != mSharedFolders.end())
5955 ; /* the console folder exists, nothing to do */
5956 else
5957 {
5958 /* remove the old machine folder (when changed)
5959 * or the global folder if any (when new) */
5960 if (it != oldFolders.end() ||
5961 mGlobalSharedFolders.find(name) !=
5962 mGlobalSharedFolders.end())
5963 rc = removeSharedFolder(name.raw());
5964 /* create the new machine folder */
5965 rc = createSharedFolder(name.raw(),
5966 SharedFolderData(hostPath,
5967 writable,
5968 autoMount));
5969 }
5970 }
5971 /* forget the processed (or identical) folder */
5972 if (it != oldFolders.end())
5973 oldFolders.erase(it);
5974
5975 rc = S_OK;
5976 }
5977 }
5978
5979 AssertComRCReturnRC(rc);
5980
5981 /* process outdated (removed) folders */
5982 /// @todo report errors as runtime warnings through VMSetError
5983 if (online)
5984 {
5985 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5986 it != oldFolders.end(); ++ it)
5987 {
5988 if (mSharedFolders.find(it->first) != mSharedFolders.end())
5989 ; /* the console folder exists, nothing to do */
5990 else
5991 {
5992 /* remove the outdated machine folder */
5993 rc = removeSharedFolder(it->first.raw());
5994 /* create the global folder if there is any */
5995 SharedFolderDataMap::const_iterator git =
5996 mGlobalSharedFolders.find(it->first);
5997 if (git != mGlobalSharedFolders.end())
5998 rc = createSharedFolder(git->first.raw(), git->second);
5999 }
6000 }
6001
6002 rc = S_OK;
6003 }
6004 }
6005
6006 return rc;
6007}
6008
6009/**
6010 * Searches for a shared folder with the given name in the list of machine
6011 * shared folders and then in the list of the global shared folders.
6012 *
6013 * @param aName Name of the folder to search for.
6014 * @param aIt Where to store the pointer to the found folder.
6015 * @return @c true if the folder was found and @c false otherwise.
6016 *
6017 * @note The caller must lock this object for reading.
6018 */
6019bool Console::findOtherSharedFolder(IN_BSTR aName,
6020 SharedFolderDataMap::const_iterator &aIt)
6021{
6022 /* sanity check */
6023 AssertReturn(isWriteLockOnCurrentThread(), false);
6024
6025 /* first, search machine folders */
6026 aIt = mMachineSharedFolders.find(aName);
6027 if (aIt != mMachineSharedFolders.end())
6028 return true;
6029
6030 /* second, search machine folders */
6031 aIt = mGlobalSharedFolders.find(aName);
6032 if (aIt != mGlobalSharedFolders.end())
6033 return true;
6034
6035 return false;
6036}
6037
6038/**
6039 * Calls the HGCM service to add a shared folder definition.
6040 *
6041 * @param aName Shared folder name.
6042 * @param aHostPath Shared folder path.
6043 *
6044 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6045 * @note Doesn't lock anything.
6046 */
6047HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
6048{
6049 ComAssertRet(aName && *aName, E_FAIL);
6050 ComAssertRet(!aData.mHostPath.isEmpty(), E_FAIL);
6051
6052 /* sanity checks */
6053 AssertReturn(mpVM, E_FAIL);
6054 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6055
6056 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
6057 SHFLSTRING *pFolderName, *pMapName;
6058 size_t cbString;
6059
6060 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
6061
6062 cbString = (RTUtf16Len(aData.mHostPath.raw()) + 1) * sizeof(RTUTF16);
6063 if (cbString >= UINT16_MAX)
6064 return setError(E_INVALIDARG, tr("The name is too long"));
6065 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6066 Assert(pFolderName);
6067 memcpy(pFolderName->String.ucs2, aData.mHostPath.raw(), cbString);
6068
6069 pFolderName->u16Size = (uint16_t)cbString;
6070 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6071
6072 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
6073 parms[0].u.pointer.addr = pFolderName;
6074 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6075
6076 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6077 if (cbString >= UINT16_MAX)
6078 {
6079 RTMemFree(pFolderName);
6080 return setError(E_INVALIDARG, tr("The host path is too long"));
6081 }
6082 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6083 Assert(pMapName);
6084 memcpy(pMapName->String.ucs2, aName, cbString);
6085
6086 pMapName->u16Size = (uint16_t)cbString;
6087 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6088
6089 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
6090 parms[1].u.pointer.addr = pMapName;
6091 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6092
6093 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
6094 parms[2].u.uint32 = aData.mWritable;
6095
6096 /*
6097 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
6098 * define below. This shows the host service that we have supplied
6099 * an additional parameter (auto-mount) and keeps the actual command
6100 * backwards compatible.
6101 */
6102 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
6103 parms[3].u.uint32 = aData.mAutoMount;
6104
6105 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6106 SHFL_FN_ADD_MAPPING,
6107 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
6108 RTMemFree(pFolderName);
6109 RTMemFree(pMapName);
6110
6111 if (RT_FAILURE(vrc))
6112 return setError(E_FAIL,
6113 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
6114 aName, aData.mHostPath.raw(), vrc);
6115
6116 return S_OK;
6117}
6118
6119/**
6120 * Calls the HGCM service to remove the shared folder definition.
6121 *
6122 * @param aName Shared folder name.
6123 *
6124 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
6125 * @note Doesn't lock anything.
6126 */
6127HRESULT Console::removeSharedFolder(CBSTR aName)
6128{
6129 ComAssertRet(aName && *aName, E_FAIL);
6130
6131 /* sanity checks */
6132 AssertReturn(mpVM, E_FAIL);
6133 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6134
6135 VBOXHGCMSVCPARM parms;
6136 SHFLSTRING *pMapName;
6137 size_t cbString;
6138
6139 Log(("Removing shared folder '%ls'\n", aName));
6140
6141 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6142 if (cbString >= UINT16_MAX)
6143 return setError(E_INVALIDARG, tr("The name is too long"));
6144 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6145 Assert(pMapName);
6146 memcpy(pMapName->String.ucs2, aName, cbString);
6147
6148 pMapName->u16Size = (uint16_t)cbString;
6149 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6150
6151 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6152 parms.u.pointer.addr = pMapName;
6153 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6154
6155 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6156 SHFL_FN_REMOVE_MAPPING,
6157 1, &parms);
6158 RTMemFree(pMapName);
6159 if (RT_FAILURE(vrc))
6160 return setError(E_FAIL,
6161 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6162 aName, vrc);
6163
6164 return S_OK;
6165}
6166
6167/**
6168 * VM state callback function. Called by the VMM
6169 * using its state machine states.
6170 *
6171 * Primarily used to handle VM initiated power off, suspend and state saving,
6172 * but also for doing termination completed work (VMSTATE_TERMINATE).
6173 *
6174 * In general this function is called in the context of the EMT.
6175 *
6176 * @param aVM The VM handle.
6177 * @param aState The new state.
6178 * @param aOldState The old state.
6179 * @param aUser The user argument (pointer to the Console object).
6180 *
6181 * @note Locks the Console object for writing.
6182 */
6183DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6184 VMSTATE aState,
6185 VMSTATE aOldState,
6186 void *aUser)
6187{
6188 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6189 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6190
6191 Console *that = static_cast<Console *>(aUser);
6192 AssertReturnVoid(that);
6193
6194 AutoCaller autoCaller(that);
6195
6196 /* Note that we must let this method proceed even if Console::uninit() has
6197 * been already called. In such case this VMSTATE change is a result of:
6198 * 1) powerDown() called from uninit() itself, or
6199 * 2) VM-(guest-)initiated power off. */
6200 AssertReturnVoid( autoCaller.isOk()
6201 || autoCaller.state() == InUninit);
6202
6203 switch (aState)
6204 {
6205 /*
6206 * The VM has terminated
6207 */
6208 case VMSTATE_OFF:
6209 {
6210 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6211
6212 if (that->mVMStateChangeCallbackDisabled)
6213 break;
6214
6215 /* Do we still think that it is running? It may happen if this is a
6216 * VM-(guest-)initiated shutdown/poweroff.
6217 */
6218 if ( that->mMachineState != MachineState_Stopping
6219 && that->mMachineState != MachineState_Saving
6220 && that->mMachineState != MachineState_Restoring
6221 && that->mMachineState != MachineState_TeleportingIn
6222 && that->mMachineState != MachineState_FaultTolerantSyncing
6223 && that->mMachineState != MachineState_TeleportingPausedVM
6224 && !that->mVMIsAlreadyPoweringOff
6225 )
6226 {
6227 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6228
6229 /* prevent powerDown() from calling VMR3PowerOff() again */
6230 Assert(that->mVMPoweredOff == false);
6231 that->mVMPoweredOff = true;
6232
6233 /* we are stopping now */
6234 that->setMachineState(MachineState_Stopping);
6235
6236 /* Setup task object and thread to carry out the operation
6237 * asynchronously (if we call powerDown() right here but there
6238 * is one or more mpVM callers (added with addVMCaller()) we'll
6239 * deadlock).
6240 */
6241 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6242 true /* aUsesVMPtr */));
6243
6244 /* If creating a task is falied, this can currently mean one of
6245 * two: either Console::uninit() has been called just a ms
6246 * before (so a powerDown() call is already on the way), or
6247 * powerDown() itself is being already executed. Just do
6248 * nothing.
6249 */
6250 if (!task->isOk())
6251 {
6252 LogFlowFunc(("Console is already being uninitialized.\n"));
6253 break;
6254 }
6255
6256 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6257 (void *) task.get(), 0,
6258 RTTHREADTYPE_MAIN_WORKER, 0,
6259 "VMPowerDown");
6260 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6261
6262 /* task is now owned by powerDownThread(), so release it */
6263 task.release();
6264 }
6265 break;
6266 }
6267
6268 /* The VM has been completely destroyed.
6269 *
6270 * Note: This state change can happen at two points:
6271 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6272 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6273 * called by EMT.
6274 */
6275 case VMSTATE_TERMINATED:
6276 {
6277 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6278
6279 if (that->mVMStateChangeCallbackDisabled)
6280 break;
6281
6282 /* Terminate host interface networking. If aVM is NULL, we've been
6283 * manually called from powerUpThread() either before calling
6284 * VMR3Create() or after VMR3Create() failed, so no need to touch
6285 * networking.
6286 */
6287 if (aVM)
6288 that->powerDownHostInterfaces();
6289
6290 /* From now on the machine is officially powered down or remains in
6291 * the Saved state.
6292 */
6293 switch (that->mMachineState)
6294 {
6295 default:
6296 AssertFailed();
6297 /* fall through */
6298 case MachineState_Stopping:
6299 /* successfully powered down */
6300 that->setMachineState(MachineState_PoweredOff);
6301 break;
6302 case MachineState_Saving:
6303 /* successfully saved (note that the machine is already in
6304 * the Saved state on the server due to EndSavingState()
6305 * called from saveStateThread(), so only change the local
6306 * state) */
6307 that->setMachineStateLocally(MachineState_Saved);
6308 break;
6309 case MachineState_Starting:
6310 /* failed to start, but be patient: set back to PoweredOff
6311 * (for similarity with the below) */
6312 that->setMachineState(MachineState_PoweredOff);
6313 break;
6314 case MachineState_Restoring:
6315 /* failed to load the saved state file, but be patient: set
6316 * back to Saved (to preserve the saved state file) */
6317 that->setMachineState(MachineState_Saved);
6318 break;
6319 case MachineState_TeleportingIn:
6320 /* Teleportation failed or was canceled. Back to powered off. */
6321 that->setMachineState(MachineState_PoweredOff);
6322 break;
6323 case MachineState_TeleportingPausedVM:
6324 /* Successfully teleported the VM. */
6325 that->setMachineState(MachineState_Teleported);
6326 break;
6327 case MachineState_FaultTolerantSyncing:
6328 /* Fault tolerant sync failed or was canceled. Back to powered off. */
6329 that->setMachineState(MachineState_PoweredOff);
6330 break;
6331 }
6332 break;
6333 }
6334
6335 case VMSTATE_SUSPENDED:
6336 {
6337 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6338
6339 if (that->mVMStateChangeCallbackDisabled)
6340 break;
6341
6342 switch (that->mMachineState)
6343 {
6344 case MachineState_Teleporting:
6345 that->setMachineState(MachineState_TeleportingPausedVM);
6346 break;
6347
6348 case MachineState_LiveSnapshotting:
6349 that->setMachineState(MachineState_Saving);
6350 break;
6351
6352 case MachineState_TeleportingPausedVM:
6353 case MachineState_Saving:
6354 case MachineState_Restoring:
6355 case MachineState_Stopping:
6356 case MachineState_TeleportingIn:
6357 case MachineState_FaultTolerantSyncing:
6358 /* The worker thread handles the transition. */
6359 break;
6360
6361 default:
6362 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6363 case MachineState_Running:
6364 that->setMachineState(MachineState_Paused);
6365 break;
6366
6367 case MachineState_Paused:
6368 /* Nothing to do. */
6369 break;
6370 }
6371 break;
6372 }
6373
6374 case VMSTATE_SUSPENDED_LS:
6375 case VMSTATE_SUSPENDED_EXT_LS:
6376 {
6377 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6378 if (that->mVMStateChangeCallbackDisabled)
6379 break;
6380 switch (that->mMachineState)
6381 {
6382 case MachineState_Teleporting:
6383 that->setMachineState(MachineState_TeleportingPausedVM);
6384 break;
6385
6386 case MachineState_LiveSnapshotting:
6387 that->setMachineState(MachineState_Saving);
6388 break;
6389
6390 case MachineState_TeleportingPausedVM:
6391 case MachineState_Saving:
6392 /* ignore */
6393 break;
6394
6395 default:
6396 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6397 that->setMachineState(MachineState_Paused);
6398 break;
6399 }
6400 break;
6401 }
6402
6403 case VMSTATE_RUNNING:
6404 {
6405 if ( aOldState == VMSTATE_POWERING_ON
6406 || aOldState == VMSTATE_RESUMING
6407 || aOldState == VMSTATE_RUNNING_FT)
6408 {
6409 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6410
6411 if (that->mVMStateChangeCallbackDisabled)
6412 break;
6413
6414 Assert( ( ( that->mMachineState == MachineState_Starting
6415 || that->mMachineState == MachineState_Paused)
6416 && aOldState == VMSTATE_POWERING_ON)
6417 || ( ( that->mMachineState == MachineState_Restoring
6418 || that->mMachineState == MachineState_TeleportingIn
6419 || that->mMachineState == MachineState_Paused
6420 || that->mMachineState == MachineState_Saving
6421 )
6422 && aOldState == VMSTATE_RESUMING)
6423 || ( that->mMachineState == MachineState_FaultTolerantSyncing
6424 && aOldState == VMSTATE_RUNNING_FT));
6425
6426 that->setMachineState(MachineState_Running);
6427 }
6428
6429 break;
6430 }
6431
6432 case VMSTATE_RUNNING_LS:
6433 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6434 || that->mMachineState == MachineState_Teleporting,
6435 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6436 break;
6437
6438 case VMSTATE_RUNNING_FT:
6439 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
6440 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6441 break;
6442
6443 case VMSTATE_FATAL_ERROR:
6444 {
6445 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6446
6447 if (that->mVMStateChangeCallbackDisabled)
6448 break;
6449
6450 /* Fatal errors are only for running VMs. */
6451 Assert(Global::IsOnline(that->mMachineState));
6452
6453 /* Note! 'Pause' is used here in want of something better. There
6454 * are currently only two places where fatal errors might be
6455 * raised, so it is not worth adding a new externally
6456 * visible state for this yet. */
6457 that->setMachineState(MachineState_Paused);
6458 break;
6459 }
6460
6461 case VMSTATE_GURU_MEDITATION:
6462 {
6463 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6464
6465 if (that->mVMStateChangeCallbackDisabled)
6466 break;
6467
6468 /* Guru are only for running VMs */
6469 Assert(Global::IsOnline(that->mMachineState));
6470
6471 that->setMachineState(MachineState_Stuck);
6472 break;
6473 }
6474
6475 default: /* shut up gcc */
6476 break;
6477 }
6478}
6479
6480#ifdef VBOX_WITH_USB
6481
6482/**
6483 * Sends a request to VMM to attach the given host device.
6484 * After this method succeeds, the attached device will appear in the
6485 * mUSBDevices collection.
6486 *
6487 * @param aHostDevice device to attach
6488 *
6489 * @note Synchronously calls EMT.
6490 * @note Must be called from under this object's lock.
6491 */
6492HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6493{
6494 AssertReturn(aHostDevice, E_FAIL);
6495 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6496
6497 /* still want a lock object because we need to leave it */
6498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6499
6500 HRESULT hrc;
6501
6502 /*
6503 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6504 * method in EMT (using usbAttachCallback()).
6505 */
6506 Bstr BstrAddress;
6507 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6508 ComAssertComRCRetRC(hrc);
6509
6510 Utf8Str Address(BstrAddress);
6511
6512 Bstr id;
6513 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6514 ComAssertComRCRetRC(hrc);
6515 Guid uuid(id);
6516
6517 BOOL fRemote = FALSE;
6518 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6519 ComAssertComRCRetRC(hrc);
6520
6521 /* protect mpVM */
6522 AutoVMCaller autoVMCaller(this);
6523 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6524
6525 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6526 Address.c_str(), uuid.ptr()));
6527
6528 /* leave the lock before a VMR3* call (EMT will call us back)! */
6529 alock.leave();
6530
6531/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6532 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6533 (PFNRT)usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.c_str(), aMaskedIfs);
6534
6535 /* restore the lock */
6536 alock.enter();
6537
6538 /* hrc is S_OK here */
6539
6540 if (RT_FAILURE(vrc))
6541 {
6542 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6543 Address.c_str(), uuid.ptr(), vrc));
6544
6545 switch (vrc)
6546 {
6547 case VERR_VUSB_NO_PORTS:
6548 hrc = setError(E_FAIL,
6549 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6550 break;
6551 case VERR_VUSB_USBFS_PERMISSION:
6552 hrc = setError(E_FAIL,
6553 tr("Not permitted to open the USB device, check usbfs options"));
6554 break;
6555 default:
6556 hrc = setError(E_FAIL,
6557 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6558 vrc);
6559 break;
6560 }
6561 }
6562
6563 return hrc;
6564}
6565
6566/**
6567 * USB device attach callback used by AttachUSBDevice().
6568 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6569 * so we don't use AutoCaller and don't care about reference counters of
6570 * interface pointers passed in.
6571 *
6572 * @thread EMT
6573 * @note Locks the console object for writing.
6574 */
6575//static
6576DECLCALLBACK(int)
6577Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6578{
6579 LogFlowFuncEnter();
6580 LogFlowFunc(("that={%p}\n", that));
6581
6582 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6583
6584 void *pvRemoteBackend = NULL;
6585 if (aRemote)
6586 {
6587 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6588 Guid guid(*aUuid);
6589
6590 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6591 if (!pvRemoteBackend)
6592 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6593 }
6594
6595 USHORT portVersion = 1;
6596 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6597 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6598 Assert(portVersion == 1 || portVersion == 2);
6599
6600 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6601 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6602 if (RT_SUCCESS(vrc))
6603 {
6604 /* Create a OUSBDevice and add it to the device list */
6605 ComObjPtr<OUSBDevice> device;
6606 device.createObject();
6607 hrc = device->init(aHostDevice);
6608 AssertComRC(hrc);
6609
6610 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6611 that->mUSBDevices.push_back(device);
6612 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6613
6614 /* notify callbacks */
6615 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6616 }
6617
6618 LogFlowFunc(("vrc=%Rrc\n", vrc));
6619 LogFlowFuncLeave();
6620 return vrc;
6621}
6622
6623/**
6624 * Sends a request to VMM to detach the given host device. After this method
6625 * succeeds, the detached device will disappear from the mUSBDevices
6626 * collection.
6627 *
6628 * @param aIt Iterator pointing to the device to detach.
6629 *
6630 * @note Synchronously calls EMT.
6631 * @note Must be called from under this object's lock.
6632 */
6633HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6634{
6635 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6636
6637 /* still want a lock object because we need to leave it */
6638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6639
6640 /* protect mpVM */
6641 AutoVMCaller autoVMCaller(this);
6642 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6643
6644 /* if the device is attached, then there must at least one USB hub. */
6645 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6646
6647 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6648 (*aIt)->id().raw()));
6649
6650 /* leave the lock before a VMR3* call (EMT will call us back)! */
6651 alock.leave();
6652
6653/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6654 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6655 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6656 ComAssertRCRet(vrc, E_FAIL);
6657
6658 return S_OK;
6659}
6660
6661/**
6662 * USB device detach callback used by DetachUSBDevice().
6663 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6664 * so we don't use AutoCaller and don't care about reference counters of
6665 * interface pointers passed in.
6666 *
6667 * @thread EMT
6668 * @note Locks the console object for writing.
6669 */
6670//static
6671DECLCALLBACK(int)
6672Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6673{
6674 LogFlowFuncEnter();
6675 LogFlowFunc(("that={%p}\n", that));
6676
6677 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6678 ComObjPtr<OUSBDevice> device = **aIt;
6679
6680 /*
6681 * If that was a remote device, release the backend pointer.
6682 * The pointer was requested in usbAttachCallback.
6683 */
6684 BOOL fRemote = FALSE;
6685
6686 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6687 if (FAILED(hrc2))
6688 setErrorStatic(hrc2, "GetRemote() failed");
6689
6690 if (fRemote)
6691 {
6692 Guid guid(*aUuid);
6693 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6694 }
6695
6696 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6697
6698 if (RT_SUCCESS(vrc))
6699 {
6700 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6701
6702 /* Remove the device from the collection */
6703 that->mUSBDevices.erase(*aIt);
6704 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6705
6706 /* notify callbacks */
6707 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6708 }
6709
6710 LogFlowFunc(("vrc=%Rrc\n", vrc));
6711 LogFlowFuncLeave();
6712 return vrc;
6713}
6714
6715#endif /* VBOX_WITH_USB */
6716#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6717
6718/**
6719 * Helper function to handle host interface device creation and attachment.
6720 *
6721 * @param networkAdapter the network adapter which attachment should be reset
6722 * @return COM status code
6723 *
6724 * @note The caller must lock this object for writing.
6725 *
6726 * @todo Move this back into the driver!
6727 */
6728HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6729{
6730 LogFlowThisFunc(("\n"));
6731 /* sanity check */
6732 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6733
6734# ifdef VBOX_STRICT
6735 /* paranoia */
6736 NetworkAttachmentType_T attachment;
6737 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6738 Assert(attachment == NetworkAttachmentType_Bridged);
6739# endif /* VBOX_STRICT */
6740
6741 HRESULT rc = S_OK;
6742
6743 ULONG slot = 0;
6744 rc = networkAdapter->COMGETTER(Slot)(&slot);
6745 AssertComRC(rc);
6746
6747# ifdef RT_OS_LINUX
6748 /*
6749 * Allocate a host interface device
6750 */
6751 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6752 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6753 if (RT_SUCCESS(rcVBox))
6754 {
6755 /*
6756 * Set/obtain the tap interface.
6757 */
6758 struct ifreq IfReq;
6759 memset(&IfReq, 0, sizeof(IfReq));
6760 /* The name of the TAP interface we are using */
6761 Bstr tapDeviceName;
6762 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6763 if (FAILED(rc))
6764 tapDeviceName.setNull(); /* Is this necessary? */
6765 if (tapDeviceName.isEmpty())
6766 {
6767 LogRel(("No TAP device name was supplied.\n"));
6768 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6769 }
6770
6771 if (SUCCEEDED(rc))
6772 {
6773 /* If we are using a static TAP device then try to open it. */
6774 Utf8Str str(tapDeviceName);
6775 if (str.length() <= sizeof(IfReq.ifr_name))
6776 strcpy(IfReq.ifr_name, str.c_str());
6777 else
6778 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6779 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6780 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6781 if (rcVBox != 0)
6782 {
6783 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6784 rc = setError(E_FAIL,
6785 tr("Failed to open the host network interface %ls"),
6786 tapDeviceName.raw());
6787 }
6788 }
6789 if (SUCCEEDED(rc))
6790 {
6791 /*
6792 * Make it pollable.
6793 */
6794 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6795 {
6796 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6797 /*
6798 * Here is the right place to communicate the TAP file descriptor and
6799 * the host interface name to the server if/when it becomes really
6800 * necessary.
6801 */
6802 maTAPDeviceName[slot] = tapDeviceName;
6803 rcVBox = VINF_SUCCESS;
6804 }
6805 else
6806 {
6807 int iErr = errno;
6808
6809 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6810 rcVBox = VERR_HOSTIF_BLOCKING;
6811 rc = setError(E_FAIL,
6812 tr("could not set up the host networking device for non blocking access: %s"),
6813 strerror(errno));
6814 }
6815 }
6816 }
6817 else
6818 {
6819 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6820 switch (rcVBox)
6821 {
6822 case VERR_ACCESS_DENIED:
6823 /* will be handled by our caller */
6824 rc = rcVBox;
6825 break;
6826 default:
6827 rc = setError(E_FAIL,
6828 tr("Could not set up the host networking device: %Rrc"),
6829 rcVBox);
6830 break;
6831 }
6832 }
6833
6834# elif defined(RT_OS_FREEBSD)
6835 /*
6836 * Set/obtain the tap interface.
6837 */
6838 /* The name of the TAP interface we are using */
6839 Bstr tapDeviceName;
6840 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6841 if (FAILED(rc))
6842 tapDeviceName.setNull(); /* Is this necessary? */
6843 if (tapDeviceName.isEmpty())
6844 {
6845 LogRel(("No TAP device name was supplied.\n"));
6846 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6847 }
6848 char szTapdev[1024] = "/dev/";
6849 /* If we are using a static TAP device then try to open it. */
6850 Utf8Str str(tapDeviceName);
6851 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6852 strcat(szTapdev, str.c_str());
6853 else
6854 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
6855 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6856 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6857 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6858
6859 if (RT_SUCCESS(rcVBox))
6860 maTAPDeviceName[slot] = tapDeviceName;
6861 else
6862 {
6863 switch (rcVBox)
6864 {
6865 case VERR_ACCESS_DENIED:
6866 /* will be handled by our caller */
6867 rc = rcVBox;
6868 break;
6869 default:
6870 rc = setError(E_FAIL,
6871 tr("Failed to open the host network interface %ls"),
6872 tapDeviceName.raw());
6873 break;
6874 }
6875 }
6876# else
6877# error "huh?"
6878# endif
6879 /* in case of failure, cleanup. */
6880 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6881 {
6882 LogRel(("General failure attaching to host interface\n"));
6883 rc = setError(E_FAIL,
6884 tr("General failure attaching to host interface"));
6885 }
6886 LogFlowThisFunc(("rc=%d\n", rc));
6887 return rc;
6888}
6889
6890
6891/**
6892 * Helper function to handle detachment from a host interface
6893 *
6894 * @param networkAdapter the network adapter which attachment should be reset
6895 * @return COM status code
6896 *
6897 * @note The caller must lock this object for writing.
6898 *
6899 * @todo Move this back into the driver!
6900 */
6901HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6902{
6903 /* sanity check */
6904 LogFlowThisFunc(("\n"));
6905 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6906
6907 HRESULT rc = S_OK;
6908# ifdef VBOX_STRICT
6909 /* paranoia */
6910 NetworkAttachmentType_T attachment;
6911 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6912 Assert(attachment == NetworkAttachmentType_Bridged);
6913# endif /* VBOX_STRICT */
6914
6915 ULONG slot = 0;
6916 rc = networkAdapter->COMGETTER(Slot)(&slot);
6917 AssertComRC(rc);
6918
6919 /* is there an open TAP device? */
6920 if (maTapFD[slot] != NIL_RTFILE)
6921 {
6922 /*
6923 * Close the file handle.
6924 */
6925 Bstr tapDeviceName, tapTerminateApplication;
6926 bool isStatic = true;
6927 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6928 if (FAILED(rc) || tapDeviceName.isEmpty())
6929 {
6930 /* If the name is empty, this is a dynamic TAP device, so close it now,
6931 so that the termination script can remove the interface. Otherwise we still
6932 need the FD to pass to the termination script. */
6933 isStatic = false;
6934 int rcVBox = RTFileClose(maTapFD[slot]);
6935 AssertRC(rcVBox);
6936 maTapFD[slot] = NIL_RTFILE;
6937 }
6938 if (isStatic)
6939 {
6940 /* If we are using a static TAP device, we close it now, after having called the
6941 termination script. */
6942 int rcVBox = RTFileClose(maTapFD[slot]);
6943 AssertRC(rcVBox);
6944 }
6945 /* the TAP device name and handle are no longer valid */
6946 maTapFD[slot] = NIL_RTFILE;
6947 maTAPDeviceName[slot] = "";
6948 }
6949 LogFlowThisFunc(("returning %d\n", rc));
6950 return rc;
6951}
6952
6953#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6954
6955/**
6956 * Called at power down to terminate host interface networking.
6957 *
6958 * @note The caller must lock this object for writing.
6959 */
6960HRESULT Console::powerDownHostInterfaces()
6961{
6962 LogFlowThisFunc(("\n"));
6963
6964 /* sanity check */
6965 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6966
6967 /*
6968 * host interface termination handling
6969 */
6970 HRESULT rc;
6971 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6972 {
6973 ComPtr<INetworkAdapter> networkAdapter;
6974 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6975 if (FAILED(rc)) break;
6976
6977 BOOL enabled = FALSE;
6978 networkAdapter->COMGETTER(Enabled)(&enabled);
6979 if (!enabled)
6980 continue;
6981
6982 NetworkAttachmentType_T attachment;
6983 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6984 if (attachment == NetworkAttachmentType_Bridged)
6985 {
6986#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6987 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6988 if (FAILED(rc2) && SUCCEEDED(rc))
6989 rc = rc2;
6990#endif
6991 }
6992 }
6993
6994 return rc;
6995}
6996
6997
6998/**
6999 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
7000 * and VMR3Teleport.
7001 *
7002 * @param pVM The VM handle.
7003 * @param uPercent Completetion precentage (0-100).
7004 * @param pvUser Pointer to the VMProgressTask structure.
7005 * @return VINF_SUCCESS.
7006 */
7007/*static*/
7008DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
7009{
7010 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
7011 AssertReturn(task, VERR_INVALID_PARAMETER);
7012
7013 /* update the progress object */
7014 if (task->mProgress)
7015 task->mProgress->SetCurrentOperationProgress(uPercent);
7016
7017 return VINF_SUCCESS;
7018}
7019
7020/**
7021 * @copydoc FNVMATERROR
7022 *
7023 * @remarks Might be some tiny serialization concerns with access to the string
7024 * object here...
7025 */
7026/*static*/ DECLCALLBACK(void)
7027Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
7028 const char *pszErrorFmt, va_list va)
7029{
7030 Utf8Str *pErrorText = (Utf8Str *)pvUser;
7031 AssertPtr(pErrorText);
7032
7033 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
7034 va_list va2;
7035 va_copy(va2, va);
7036
7037 /* Append to any the existing error message. */
7038 if (pErrorText->length())
7039 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
7040 pszErrorFmt, &va2, rc, rc);
7041 else
7042 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
7043
7044 va_end(va2);
7045}
7046
7047/**
7048 * VM runtime error callback function.
7049 * See VMSetRuntimeError for the detailed description of parameters.
7050 *
7051 * @param pVM The VM handle.
7052 * @param pvUser The user argument.
7053 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
7054 * @param pszErrorId Error ID string.
7055 * @param pszFormat Error message format string.
7056 * @param va Error message arguments.
7057 * @thread EMT.
7058 */
7059/* static */ DECLCALLBACK(void)
7060Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
7061 const char *pszErrorId,
7062 const char *pszFormat, va_list va)
7063{
7064 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
7065 LogFlowFuncEnter();
7066
7067 Console *that = static_cast<Console *>(pvUser);
7068 AssertReturnVoid(that);
7069
7070 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
7071
7072 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
7073 fFatal, pszErrorId, message.c_str()));
7074
7075 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
7076 Bstr(message).raw());
7077
7078 LogFlowFuncLeave();
7079}
7080
7081/**
7082 * Captures USB devices that match filters of the VM.
7083 * Called at VM startup.
7084 *
7085 * @param pVM The VM handle.
7086 *
7087 * @note The caller must lock this object for writing.
7088 */
7089HRESULT Console::captureUSBDevices(PVM pVM)
7090{
7091 LogFlowThisFunc(("\n"));
7092
7093 /* sanity check */
7094 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
7095
7096 /* If the machine has an USB controller, ask the USB proxy service to
7097 * capture devices */
7098 PPDMIBASE pBase;
7099 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7100 if (RT_SUCCESS(vrc))
7101 {
7102 /* leave the lock before calling Host in VBoxSVC since Host may call
7103 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7104 * produce an inter-process dead-lock otherwise. */
7105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7106 alock.leave();
7107
7108 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7109 ComAssertComRCRetRC(hrc);
7110 }
7111 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7112 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7113 vrc = VINF_SUCCESS;
7114 else
7115 AssertRC(vrc);
7116
7117 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7118}
7119
7120
7121/**
7122 * Detach all USB device which are attached to the VM for the
7123 * purpose of clean up and such like.
7124 *
7125 * @note The caller must lock this object for writing.
7126 */
7127void Console::detachAllUSBDevices(bool aDone)
7128{
7129 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7130
7131 /* sanity check */
7132 AssertReturnVoid(isWriteLockOnCurrentThread());
7133
7134 mUSBDevices.clear();
7135
7136 /* leave the lock before calling Host in VBoxSVC since Host may call
7137 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7138 * produce an inter-process dead-lock otherwise. */
7139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7140 alock.leave();
7141
7142 mControl->DetachAllUSBDevices(aDone);
7143}
7144
7145/**
7146 * @note Locks this object for writing.
7147 */
7148void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7149{
7150 LogFlowThisFuncEnter();
7151 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7152
7153 AutoCaller autoCaller(this);
7154 if (!autoCaller.isOk())
7155 {
7156 /* Console has been already uninitialized, deny request */
7157 AssertMsgFailed(("Console is already uninitialized\n"));
7158 LogFlowThisFunc(("Console is already uninitialized\n"));
7159 LogFlowThisFuncLeave();
7160 return;
7161 }
7162
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164
7165 /*
7166 * Mark all existing remote USB devices as dirty.
7167 */
7168 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7169 it != mRemoteUSBDevices.end();
7170 ++it)
7171 {
7172 (*it)->dirty(true);
7173 }
7174
7175 /*
7176 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7177 */
7178 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7179 VRDPUSBDEVICEDESC *e = pDevList;
7180
7181 /* The cbDevList condition must be checked first, because the function can
7182 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7183 */
7184 while (cbDevList >= 2 && e->oNext)
7185 {
7186 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7187 e->idVendor, e->idProduct,
7188 e->oProduct? (char *)e + e->oProduct: ""));
7189
7190 bool fNewDevice = true;
7191
7192 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7193 it != mRemoteUSBDevices.end();
7194 ++it)
7195 {
7196 if ((*it)->devId() == e->id
7197 && (*it)->clientId() == u32ClientId)
7198 {
7199 /* The device is already in the list. */
7200 (*it)->dirty(false);
7201 fNewDevice = false;
7202 break;
7203 }
7204 }
7205
7206 if (fNewDevice)
7207 {
7208 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7209 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7210
7211 /* Create the device object and add the new device to list. */
7212 ComObjPtr<RemoteUSBDevice> device;
7213 device.createObject();
7214 device->init(u32ClientId, e);
7215
7216 mRemoteUSBDevices.push_back(device);
7217
7218 /* Check if the device is ok for current USB filters. */
7219 BOOL fMatched = FALSE;
7220 ULONG fMaskedIfs = 0;
7221
7222 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7223
7224 AssertComRC(hrc);
7225
7226 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7227
7228 if (fMatched)
7229 {
7230 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7231
7232 /// @todo (r=dmik) warning reporting subsystem
7233
7234 if (hrc == S_OK)
7235 {
7236 LogFlowThisFunc(("Device attached\n"));
7237 device->captured(true);
7238 }
7239 }
7240 }
7241
7242 if (cbDevList < e->oNext)
7243 {
7244 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7245 cbDevList, e->oNext));
7246 break;
7247 }
7248
7249 cbDevList -= e->oNext;
7250
7251 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7252 }
7253
7254 /*
7255 * Remove dirty devices, that is those which are not reported by the server anymore.
7256 */
7257 for (;;)
7258 {
7259 ComObjPtr<RemoteUSBDevice> device;
7260
7261 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7262 while (it != mRemoteUSBDevices.end())
7263 {
7264 if ((*it)->dirty())
7265 {
7266 device = *it;
7267 break;
7268 }
7269
7270 ++ it;
7271 }
7272
7273 if (!device)
7274 {
7275 break;
7276 }
7277
7278 USHORT vendorId = 0;
7279 device->COMGETTER(VendorId)(&vendorId);
7280
7281 USHORT productId = 0;
7282 device->COMGETTER(ProductId)(&productId);
7283
7284 Bstr product;
7285 device->COMGETTER(Product)(product.asOutParam());
7286
7287 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7288 vendorId, productId, product.raw()));
7289
7290 /* Detach the device from VM. */
7291 if (device->captured())
7292 {
7293 Bstr uuid;
7294 device->COMGETTER(Id)(uuid.asOutParam());
7295 onUSBDeviceDetach(uuid.raw(), NULL);
7296 }
7297
7298 /* And remove it from the list. */
7299 mRemoteUSBDevices.erase(it);
7300 }
7301
7302 LogFlowThisFuncLeave();
7303}
7304
7305/**
7306 * Progress cancelation callback for fault tolerance VM poweron
7307 */
7308static void faultToleranceProgressCancelCallback(void *pvUser)
7309{
7310 PVM pVM = (PVM)pvUser;
7311
7312 if (pVM)
7313 FTMR3CancelStandby(pVM);
7314}
7315
7316/**
7317 * Thread function which starts the VM (also from saved state) and
7318 * track progress.
7319 *
7320 * @param Thread The thread id.
7321 * @param pvUser Pointer to a VMPowerUpTask structure.
7322 * @return VINF_SUCCESS (ignored).
7323 *
7324 * @note Locks the Console object for writing.
7325 */
7326/*static*/
7327DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7328{
7329 LogFlowFuncEnter();
7330
7331 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7332 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7333
7334 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7335 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7336
7337#if defined(RT_OS_WINDOWS)
7338 {
7339 /* initialize COM */
7340 HRESULT hrc = CoInitializeEx(NULL,
7341 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7342 COINIT_SPEED_OVER_MEMORY);
7343 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7344 }
7345#endif
7346
7347 HRESULT rc = S_OK;
7348 int vrc = VINF_SUCCESS;
7349
7350 /* Set up a build identifier so that it can be seen from core dumps what
7351 * exact build was used to produce the core. */
7352 static char saBuildID[40];
7353 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7354 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7355
7356 ComObjPtr<Console> console = task->mConsole;
7357
7358 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7359
7360 /* The lock is also used as a signal from the task initiator (which
7361 * releases it only after RTThreadCreate()) that we can start the job */
7362 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7363
7364 /* sanity */
7365 Assert(console->mpVM == NULL);
7366
7367 try
7368 {
7369 /* wait for auto reset ops to complete so that we can successfully lock
7370 * the attached hard disks by calling LockMedia() below */
7371 for (VMPowerUpTask::ProgressList::const_iterator
7372 it = task->hardDiskProgresses.begin();
7373 it != task->hardDiskProgresses.end(); ++ it)
7374 {
7375 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7376 AssertComRC(rc2);
7377 }
7378
7379 /*
7380 * Lock attached media. This method will also check their accessibility.
7381 * If we're a teleporter, we'll have to postpone this action so we can
7382 * migrate between local processes.
7383 *
7384 * Note! The media will be unlocked automatically by
7385 * SessionMachine::setMachineState() when the VM is powered down.
7386 */
7387 if ( !task->mTeleporterEnabled
7388 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
7389 {
7390 rc = console->mControl->LockMedia();
7391 if (FAILED(rc)) throw rc;
7392 }
7393
7394#ifdef VBOX_WITH_VRDP
7395
7396 /* Create the VRDP server. In case of headless operation, this will
7397 * also create the framebuffer, required at VM creation.
7398 */
7399 ConsoleVRDPServer *server = console->consoleVRDPServer();
7400 Assert(server);
7401
7402 /* Does VRDP server call Console from the other thread?
7403 * Not sure (and can change), so leave the lock just in case.
7404 */
7405 alock.leave();
7406 vrc = server->Launch();
7407 alock.enter();
7408
7409 if (vrc == VERR_NET_ADDRESS_IN_USE)
7410 {
7411 Utf8Str errMsg;
7412 Bstr bstr;
7413 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7414 Utf8Str ports = bstr;
7415 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7416 ports.c_str());
7417 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7418 vrc, errMsg.c_str()));
7419 }
7420 else if (RT_FAILURE(vrc))
7421 {
7422 Utf8Str errMsg;
7423 switch (vrc)
7424 {
7425 case VERR_FILE_NOT_FOUND:
7426 {
7427 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7428 break;
7429 }
7430 default:
7431 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7432 vrc);
7433 }
7434 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7435 vrc, errMsg.c_str()));
7436 throw setErrorStatic(E_FAIL, errMsg.c_str());
7437 }
7438
7439#endif /* VBOX_WITH_VRDP */
7440
7441 ComPtr<IMachine> pMachine = console->machine();
7442 ULONG cCpus = 1;
7443 pMachine->COMGETTER(CPUCount)(&cCpus);
7444
7445 /*
7446 * Create the VM
7447 */
7448 PVM pVM;
7449 /*
7450 * leave the lock since EMT will call Console. It's safe because
7451 * mMachineState is either Starting or Restoring state here.
7452 */
7453 alock.leave();
7454
7455 vrc = VMR3Create(cCpus, console->mpVmm2UserMethods,
7456 Console::genericVMSetErrorCallback, &task->mErrorMsg,
7457 task->mConfigConstructor, static_cast<Console *>(console),
7458 &pVM);
7459
7460 alock.enter();
7461
7462#ifdef VBOX_WITH_VRDP
7463 /* Enable client connections to the server. */
7464 console->consoleVRDPServer()->EnableConnections();
7465#endif /* VBOX_WITH_VRDP */
7466
7467 if (RT_SUCCESS(vrc))
7468 {
7469 do
7470 {
7471 /*
7472 * Register our load/save state file handlers
7473 */
7474 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7475 NULL, NULL, NULL,
7476 NULL, saveStateFileExec, NULL,
7477 NULL, loadStateFileExec, NULL,
7478 static_cast<Console *>(console));
7479 AssertRCBreak(vrc);
7480
7481 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7482 AssertRC(vrc);
7483 if (RT_FAILURE(vrc))
7484 break;
7485
7486 /*
7487 * Synchronize debugger settings
7488 */
7489 MachineDebugger *machineDebugger = console->getMachineDebugger();
7490 if (machineDebugger)
7491 {
7492 machineDebugger->flushQueuedSettings();
7493 }
7494
7495 /*
7496 * Shared Folders
7497 */
7498 if (console->getVMMDev()->isShFlActive())
7499 {
7500 /* Does the code below call Console from the other thread?
7501 * Not sure, so leave the lock just in case. */
7502 alock.leave();
7503
7504 for (SharedFolderDataMap::const_iterator
7505 it = task->mSharedFolders.begin();
7506 it != task->mSharedFolders.end();
7507 ++ it)
7508 {
7509 rc = console->createSharedFolder((*it).first.raw(),
7510 (*it).second);
7511 if (FAILED(rc)) break;
7512 }
7513 if (FAILED(rc)) break;
7514
7515 /* enter the lock again */
7516 alock.enter();
7517 }
7518
7519 /*
7520 * Capture USB devices.
7521 */
7522 rc = console->captureUSBDevices(pVM);
7523 if (FAILED(rc)) break;
7524
7525 /* leave the lock before a lengthy operation */
7526 alock.leave();
7527
7528 /* Load saved state? */
7529 if (task->mSavedStateFile.length())
7530 {
7531 LogFlowFunc(("Restoring saved state from '%s'...\n",
7532 task->mSavedStateFile.c_str()));
7533
7534 vrc = VMR3LoadFromFile(pVM,
7535 task->mSavedStateFile.c_str(),
7536 Console::stateProgressCallback,
7537 static_cast<VMProgressTask*>(task.get()));
7538
7539 if (RT_SUCCESS(vrc))
7540 {
7541 if (task->mStartPaused)
7542 /* done */
7543 console->setMachineState(MachineState_Paused);
7544 else
7545 {
7546 /* Start/Resume the VM execution */
7547 vrc = VMR3Resume(pVM);
7548 AssertRC(vrc);
7549 }
7550 }
7551
7552 /* Power off in case we failed loading or resuming the VM */
7553 if (RT_FAILURE(vrc))
7554 {
7555 int vrc2 = VMR3PowerOff(pVM);
7556 AssertRC(vrc2);
7557 }
7558 }
7559 else if (task->mTeleporterEnabled)
7560 {
7561 /* -> ConsoleImplTeleporter.cpp */
7562 bool fPowerOffOnFailure;
7563 rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7564 task->mProgress, &fPowerOffOnFailure);
7565 if (FAILED(rc) && fPowerOffOnFailure)
7566 {
7567 ErrorInfoKeeper eik;
7568 int vrc2 = VMR3PowerOff(pVM);
7569 AssertRC(vrc2);
7570 }
7571 }
7572 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
7573 {
7574 /*
7575 * Get the config.
7576 */
7577 ULONG uPort;
7578 ULONG uInterval;
7579 Bstr bstrAddress, bstrPassword;
7580
7581 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
7582 if (SUCCEEDED(rc))
7583 {
7584 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
7585 if (SUCCEEDED(rc))
7586 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
7587 if (SUCCEEDED(rc))
7588 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
7589 }
7590 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
7591 {
7592 if (SUCCEEDED(rc))
7593 {
7594 Utf8Str strAddress(bstrAddress);
7595 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
7596 Utf8Str strPassword(bstrPassword);
7597 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
7598
7599 /* Power on the FT enabled VM. */
7600 vrc = FTMR3PowerOn(pVM, (task->mEnmFaultToleranceState == FaultToleranceState_Master) /* fMaster */, uInterval, pszAddress, uPort, pszPassword);
7601 AssertRC(vrc);
7602 }
7603 task->mProgress->setCancelCallback(NULL, NULL);
7604 }
7605 else
7606 rc = E_FAIL;
7607 }
7608 else if (task->mStartPaused)
7609 /* done */
7610 console->setMachineState(MachineState_Paused);
7611 else
7612 {
7613 /* Power on the VM (i.e. start executing) */
7614 vrc = VMR3PowerOn(pVM);
7615 AssertRC(vrc);
7616 }
7617
7618 /* enter the lock again */
7619 alock.enter();
7620 }
7621 while (0);
7622
7623 /* On failure, destroy the VM */
7624 if (FAILED(rc) || RT_FAILURE(vrc))
7625 {
7626 /* preserve existing error info */
7627 ErrorInfoKeeper eik;
7628
7629 /* powerDown() will call VMR3Destroy() and do all necessary
7630 * cleanup (VRDP, USB devices) */
7631 HRESULT rc2 = console->powerDown();
7632 AssertComRC(rc2);
7633 }
7634 else
7635 {
7636 /*
7637 * Deregister the VMSetError callback. This is necessary as the
7638 * pfnVMAtError() function passed to VMR3Create() is supposed to
7639 * be sticky but our error callback isn't.
7640 */
7641 alock.leave();
7642 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7643 /** @todo register another VMSetError callback? */
7644 alock.enter();
7645 }
7646 }
7647 else
7648 {
7649 /*
7650 * If VMR3Create() failed it has released the VM memory.
7651 */
7652 console->mpVM = NULL;
7653 }
7654
7655 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7656 {
7657 /* If VMR3Create() or one of the other calls in this function fail,
7658 * an appropriate error message has been set in task->mErrorMsg.
7659 * However since that happens via a callback, the rc status code in
7660 * this function is not updated.
7661 */
7662 if (!task->mErrorMsg.length())
7663 {
7664 /* If the error message is not set but we've got a failure,
7665 * convert the VBox status code into a meaningful error message.
7666 * This becomes unused once all the sources of errors set the
7667 * appropriate error message themselves.
7668 */
7669 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7670 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7671 vrc);
7672 }
7673
7674 /* Set the error message as the COM error.
7675 * Progress::notifyComplete() will pick it up later. */
7676 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
7677 }
7678 }
7679 catch (HRESULT aRC) { rc = aRC; }
7680
7681 if ( console->mMachineState == MachineState_Starting
7682 || console->mMachineState == MachineState_Restoring
7683 || console->mMachineState == MachineState_TeleportingIn
7684 )
7685 {
7686 /* We are still in the Starting/Restoring state. This means one of:
7687 *
7688 * 1) we failed before VMR3Create() was called;
7689 * 2) VMR3Create() failed.
7690 *
7691 * In both cases, there is no need to call powerDown(), but we still
7692 * need to go back to the PoweredOff/Saved state. Reuse
7693 * vmstateChangeCallback() for that purpose.
7694 */
7695
7696 /* preserve existing error info */
7697 ErrorInfoKeeper eik;
7698
7699 Assert(console->mpVM == NULL);
7700 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7701 console);
7702 }
7703
7704 /*
7705 * Evaluate the final result. Note that the appropriate mMachineState value
7706 * is already set by vmstateChangeCallback() in all cases.
7707 */
7708
7709 /* leave the lock, don't need it any more */
7710 alock.leave();
7711
7712 if (SUCCEEDED(rc))
7713 {
7714 /* Notify the progress object of the success */
7715 task->mProgress->notifyComplete(S_OK);
7716 }
7717 else
7718 {
7719 /* The progress object will fetch the current error info */
7720 task->mProgress->notifyComplete(rc);
7721 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7722 }
7723
7724 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7725 console->mControl->EndPowerUp(rc);
7726
7727#if defined(RT_OS_WINDOWS)
7728 /* uninitialize COM */
7729 CoUninitialize();
7730#endif
7731
7732 LogFlowFuncLeave();
7733
7734 return VINF_SUCCESS;
7735}
7736
7737
7738/**
7739 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7740 *
7741 * @param pConsole Reference to the console object.
7742 * @param pVM The VM handle.
7743 * @param lInstance The instance of the controller.
7744 * @param pcszDevice The name of the controller type.
7745 * @param enmBus The storage bus type of the controller.
7746 * @param fSetupMerge Whether to set up a medium merge
7747 * @param uMergeSource Merge source image index
7748 * @param uMergeTarget Merge target image index
7749 * @param aMediumAtt The medium attachment.
7750 * @param aMachineState The current machine state.
7751 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7752 * @return VBox status code.
7753 */
7754/* static */
7755DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7756 PVM pVM,
7757 const char *pcszDevice,
7758 unsigned uInstance,
7759 StorageBus_T enmBus,
7760 bool fUseHostIOCache,
7761 bool fSetupMerge,
7762 unsigned uMergeSource,
7763 unsigned uMergeTarget,
7764 IMediumAttachment *aMediumAtt,
7765 MachineState_T aMachineState,
7766 HRESULT *phrc)
7767{
7768 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7769
7770 int rc;
7771 HRESULT hrc;
7772 Bstr bstr;
7773 *phrc = S_OK;
7774#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7775#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7776
7777 /* Ignore attachments other than hard disks, since at the moment they are
7778 * not subject to snapshotting in general. */
7779 DeviceType_T lType;
7780 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7781 if (lType != DeviceType_HardDisk)
7782 return VINF_SUCCESS;
7783
7784 /* Determine the base path for the device instance. */
7785 PCFGMNODE pCtlInst;
7786 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7787 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7788
7789 /* Update the device instance configuration. */
7790 rc = pConsole->configMediumAttachment(pCtlInst,
7791 pcszDevice,
7792 uInstance,
7793 enmBus,
7794 fUseHostIOCache,
7795 fSetupMerge,
7796 uMergeSource,
7797 uMergeTarget,
7798 aMediumAtt,
7799 aMachineState,
7800 phrc,
7801 true /* fAttachDetach */,
7802 false /* fForceUnmount */,
7803 pVM,
7804 NULL /* paLedDevType */);
7805 /** @todo this dumps everything attached to this device instance, which
7806 * is more than necessary. Dumping the changed LUN would be enough. */
7807 CFGMR3Dump(pCtlInst);
7808 RC_CHECK();
7809
7810#undef RC_CHECK
7811#undef H
7812
7813 LogFlowFunc(("Returns success\n"));
7814 return VINF_SUCCESS;
7815}
7816
7817/**
7818 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7819 */
7820static void takesnapshotProgressCancelCallback(void *pvUser)
7821{
7822 PVM pVM = (PVM)pvUser;
7823 SSMR3Cancel(pVM);
7824}
7825
7826/**
7827 * Worker thread created by Console::TakeSnapshot.
7828 * @param Thread The current thread (ignored).
7829 * @param pvUser The task.
7830 * @return VINF_SUCCESS (ignored).
7831 */
7832/*static*/
7833DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7834{
7835 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7836
7837 // taking a snapshot consists of the following:
7838
7839 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7840 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7841 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7842 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7843 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7844
7845 Console *that = pTask->mConsole;
7846 bool fBeganTakingSnapshot = false;
7847 bool fSuspenededBySave = false;
7848
7849 AutoCaller autoCaller(that);
7850 if (FAILED(autoCaller.rc()))
7851 {
7852 that->mptrCancelableProgress.setNull();
7853 return autoCaller.rc();
7854 }
7855
7856 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7857
7858 HRESULT rc = S_OK;
7859
7860 try
7861 {
7862 /* STEP 1 + 2:
7863 * request creating the diff images on the server and create the snapshot object
7864 * (this will set the machine state to Saving on the server to block
7865 * others from accessing this machine)
7866 */
7867 rc = that->mControl->BeginTakingSnapshot(that,
7868 pTask->bstrName.raw(),
7869 pTask->bstrDescription.raw(),
7870 pTask->mProgress,
7871 pTask->fTakingSnapshotOnline,
7872 pTask->bstrSavedStateFile.asOutParam());
7873 if (FAILED(rc))
7874 throw rc;
7875
7876 fBeganTakingSnapshot = true;
7877
7878 /*
7879 * state file is non-null only when the VM is paused
7880 * (i.e. creating a snapshot online)
7881 */
7882 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7883 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
7884 if (!f)
7885 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
7886
7887 /* sync the state with the server */
7888 if (pTask->lastMachineState == MachineState_Running)
7889 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7890 else
7891 that->setMachineStateLocally(MachineState_Saving);
7892
7893 // STEP 3: save the VM state (if online)
7894 if (pTask->fTakingSnapshotOnline)
7895 {
7896 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7897
7898 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
7899 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7900 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7901
7902 alock.leave();
7903 LogFlowFunc(("VMR3Save...\n"));
7904 int vrc = VMR3Save(that->mpVM,
7905 strSavedStateFile.c_str(),
7906 true /*fContinueAfterwards*/,
7907 Console::stateProgressCallback,
7908 (void*)pTask,
7909 &fSuspenededBySave);
7910 alock.enter();
7911 if (RT_FAILURE(vrc))
7912 throw setErrorStatic(E_FAIL,
7913 tr("Failed to save the machine state to '%s' (%Rrc)"),
7914 strSavedStateFile.c_str(), vrc);
7915
7916 pTask->mProgress->setCancelCallback(NULL, NULL);
7917 if (!pTask->mProgress->notifyPointOfNoReturn())
7918 throw setErrorStatic(E_FAIL, tr("Canceled"));
7919 that->mptrCancelableProgress.setNull();
7920
7921 // STEP 4: reattach hard disks
7922 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7923
7924 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
7925 1); // operation weight, same as computed when setting up progress object
7926
7927 com::SafeIfaceArray<IMediumAttachment> atts;
7928 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7929 if (FAILED(rc))
7930 throw rc;
7931
7932 for (size_t i = 0;
7933 i < atts.size();
7934 ++i)
7935 {
7936 ComPtr<IStorageController> controller;
7937 Bstr controllerName;
7938 ULONG lInstance;
7939 StorageControllerType_T enmController;
7940 StorageBus_T enmBus;
7941 BOOL fUseHostIOCache;
7942
7943 /*
7944 * We can't pass a storage controller object directly
7945 * (g++ complains about not being able to pass non POD types through '...')
7946 * so we have to query needed values here and pass them.
7947 */
7948 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
7949 if (FAILED(rc))
7950 throw rc;
7951
7952 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
7953 controller.asOutParam());
7954 if (FAILED(rc))
7955 throw rc;
7956
7957 rc = controller->COMGETTER(ControllerType)(&enmController);
7958 if (FAILED(rc))
7959 throw rc;
7960 rc = controller->COMGETTER(Instance)(&lInstance);
7961 if (FAILED(rc))
7962 throw rc;
7963 rc = controller->COMGETTER(Bus)(&enmBus);
7964 if (FAILED(rc))
7965 throw rc;
7966 rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
7967 if (FAILED(rc))
7968 throw rc;
7969
7970 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7971
7972 /*
7973 * don't leave the lock since reconfigureMediumAttachment
7974 * isn't going to need the Console lock.
7975 */
7976 vrc = VMR3ReqCallWait(that->mpVM,
7977 VMCPUID_ANY,
7978 (PFNRT)reconfigureMediumAttachment,
7979 12,
7980 that,
7981 that->mpVM,
7982 pcszDevice,
7983 lInstance,
7984 enmBus,
7985 fUseHostIOCache,
7986 false /* fSetupMerge */,
7987 0 /* uMergeSource */,
7988 0 /* uMergeTarget */,
7989 atts[i],
7990 that->mMachineState,
7991 &rc);
7992 if (RT_FAILURE(vrc))
7993 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
7994 if (FAILED(rc))
7995 throw rc;
7996 }
7997 }
7998
7999 /*
8000 * finalize the requested snapshot object.
8001 * This will reset the machine state to the state it had right
8002 * before calling mControl->BeginTakingSnapshot().
8003 */
8004 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
8005 // do not throw rc here because we can't call EndTakingSnapshot() twice
8006 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8007 }
8008 catch (HRESULT rcThrown)
8009 {
8010 /* preserve existing error info */
8011 ErrorInfoKeeper eik;
8012
8013 if (fBeganTakingSnapshot)
8014 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
8015
8016 rc = rcThrown;
8017 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
8018 }
8019 Assert(alock.isWriteLockOnCurrentThread());
8020
8021 if (FAILED(rc)) /* Must come before calling setMachineState. */
8022 pTask->mProgress->notifyComplete(rc);
8023
8024 /*
8025 * Fix up the machine state.
8026 *
8027 * For live snapshots we do all the work, for the two other variantions we
8028 * just update the local copy.
8029 */
8030 MachineState_T enmMachineState;
8031 that->mMachine->COMGETTER(State)(&enmMachineState);
8032 if ( that->mMachineState == MachineState_LiveSnapshotting
8033 || that->mMachineState == MachineState_Saving)
8034 {
8035
8036 if (!pTask->fTakingSnapshotOnline)
8037 that->setMachineStateLocally(pTask->lastMachineState);
8038 else if (SUCCEEDED(rc))
8039 {
8040 Assert( pTask->lastMachineState == MachineState_Running
8041 || pTask->lastMachineState == MachineState_Paused);
8042 Assert(that->mMachineState == MachineState_Saving);
8043 if (pTask->lastMachineState == MachineState_Running)
8044 {
8045 LogFlowFunc(("VMR3Resume...\n"));
8046 alock.leave();
8047 int vrc = VMR3Resume(that->mpVM);
8048 alock.enter();
8049 if (RT_FAILURE(vrc))
8050 {
8051 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
8052 pTask->mProgress->notifyComplete(rc);
8053 if (that->mMachineState == MachineState_Saving)
8054 that->setMachineStateLocally(MachineState_Paused);
8055 }
8056 }
8057 else
8058 that->setMachineStateLocally(MachineState_Paused);
8059 }
8060 else
8061 {
8062 /** @todo this could probably be made more generic and reused elsewhere. */
8063 /* paranoid cleanup on for a failed online snapshot. */
8064 VMSTATE enmVMState = VMR3GetState(that->mpVM);
8065 switch (enmVMState)
8066 {
8067 case VMSTATE_RUNNING:
8068 case VMSTATE_RUNNING_LS:
8069 case VMSTATE_DEBUGGING:
8070 case VMSTATE_DEBUGGING_LS:
8071 case VMSTATE_POWERING_OFF:
8072 case VMSTATE_POWERING_OFF_LS:
8073 case VMSTATE_RESETTING:
8074 case VMSTATE_RESETTING_LS:
8075 Assert(!fSuspenededBySave);
8076 that->setMachineState(MachineState_Running);
8077 break;
8078
8079 case VMSTATE_GURU_MEDITATION:
8080 case VMSTATE_GURU_MEDITATION_LS:
8081 that->setMachineState(MachineState_Stuck);
8082 break;
8083
8084 case VMSTATE_FATAL_ERROR:
8085 case VMSTATE_FATAL_ERROR_LS:
8086 if (pTask->lastMachineState == MachineState_Paused)
8087 that->setMachineStateLocally(pTask->lastMachineState);
8088 else
8089 that->setMachineState(MachineState_Paused);
8090 break;
8091
8092 default:
8093 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
8094 case VMSTATE_SUSPENDED:
8095 case VMSTATE_SUSPENDED_LS:
8096 case VMSTATE_SUSPENDING:
8097 case VMSTATE_SUSPENDING_LS:
8098 case VMSTATE_SUSPENDING_EXT_LS:
8099 if (fSuspenededBySave)
8100 {
8101 Assert(pTask->lastMachineState == MachineState_Running);
8102 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8103 alock.leave();
8104 int vrc = VMR3Resume(that->mpVM);
8105 alock.enter();
8106 AssertLogRelRC(vrc);
8107 if (RT_FAILURE(vrc))
8108 that->setMachineState(MachineState_Paused);
8109 }
8110 else if (pTask->lastMachineState == MachineState_Paused)
8111 that->setMachineStateLocally(pTask->lastMachineState);
8112 else
8113 that->setMachineState(MachineState_Paused);
8114 break;
8115 }
8116
8117 }
8118 }
8119 /*else: somebody else has change the state... Leave it. */
8120
8121 /* check the remote state to see that we got it right. */
8122 that->mMachine->COMGETTER(State)(&enmMachineState);
8123 AssertLogRelMsg(that->mMachineState == enmMachineState,
8124 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8125 Global::stringifyMachineState(enmMachineState) ));
8126
8127
8128 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8129 pTask->mProgress->notifyComplete(rc);
8130
8131 delete pTask;
8132
8133 LogFlowFuncLeave();
8134 return VINF_SUCCESS;
8135}
8136
8137/**
8138 * Thread for executing the saved state operation.
8139 *
8140 * @param Thread The thread handle.
8141 * @param pvUser Pointer to a VMSaveTask structure.
8142 * @return VINF_SUCCESS (ignored).
8143 *
8144 * @note Locks the Console object for writing.
8145 */
8146/*static*/
8147DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8148{
8149 LogFlowFuncEnter();
8150
8151 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8152 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8153
8154 Assert(task->mSavedStateFile.length());
8155 Assert(!task->mProgress.isNull());
8156
8157 const ComObjPtr<Console> &that = task->mConsole;
8158 Utf8Str errMsg;
8159 HRESULT rc = S_OK;
8160
8161 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
8162
8163 bool fSuspenededBySave;
8164 int vrc = VMR3Save(that->mpVM,
8165 task->mSavedStateFile.c_str(),
8166 false, /*fContinueAfterwards*/
8167 Console::stateProgressCallback,
8168 static_cast<VMProgressTask*>(task.get()),
8169 &fSuspenededBySave);
8170 if (RT_FAILURE(vrc))
8171 {
8172 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8173 task->mSavedStateFile.c_str(), vrc);
8174 rc = E_FAIL;
8175 }
8176 Assert(!fSuspenededBySave);
8177
8178 /* lock the console once we're going to access it */
8179 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8180
8181 /*
8182 * finalize the requested save state procedure.
8183 * In case of success, the server will set the machine state to Saved;
8184 * in case of failure it will reset the it to the state it had right
8185 * before calling mControl->BeginSavingState().
8186 */
8187 that->mControl->EndSavingState(SUCCEEDED(rc));
8188
8189 /* synchronize the state with the server */
8190 if (!FAILED(rc))
8191 {
8192 /*
8193 * The machine has been successfully saved, so power it down
8194 * (vmstateChangeCallback() will set state to Saved on success).
8195 * Note: we release the task's VM caller, otherwise it will
8196 * deadlock.
8197 */
8198 task->releaseVMCaller();
8199
8200 rc = that->powerDown();
8201 }
8202
8203 /* notify the progress object about operation completion */
8204 if (SUCCEEDED(rc))
8205 task->mProgress->notifyComplete(S_OK);
8206 else
8207 {
8208 if (errMsg.length())
8209 task->mProgress->notifyComplete(rc,
8210 COM_IIDOF(IConsole),
8211 Console::getStaticComponentName(),
8212 errMsg.c_str());
8213 else
8214 task->mProgress->notifyComplete(rc);
8215 }
8216
8217 LogFlowFuncLeave();
8218 return VINF_SUCCESS;
8219}
8220
8221/**
8222 * Thread for powering down the Console.
8223 *
8224 * @param Thread The thread handle.
8225 * @param pvUser Pointer to the VMTask structure.
8226 * @return VINF_SUCCESS (ignored).
8227 *
8228 * @note Locks the Console object for writing.
8229 */
8230/*static*/
8231DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8232{
8233 LogFlowFuncEnter();
8234
8235 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8236 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8237
8238 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8239
8240 const ComObjPtr<Console> &that = task->mConsole;
8241
8242 /* Note: no need to use addCaller() to protect Console because VMTask does
8243 * that */
8244
8245 /* wait until the method tat started us returns */
8246 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8247
8248 /* release VM caller to avoid the powerDown() deadlock */
8249 task->releaseVMCaller();
8250
8251 that->powerDown(task->mProgress);
8252
8253 LogFlowFuncLeave();
8254 return VINF_SUCCESS;
8255}
8256
8257
8258/**
8259 * @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
8260 */
8261/*static*/
8262DECLCALLBACK(int) Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PVM pVM)
8263{
8264 Console *pConsole = *(Console **)(pThis + 1); /* lazy bird */
8265
8266 /*
8267 * For now, just call SaveState. We should probably try notify the GUI so
8268 * it can pop up a progress object and stuff.
8269 */
8270 HRESULT hrc = pConsole->SaveState(NULL);
8271 return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
8272}
8273
8274
8275
8276/**
8277 * The Main status driver instance data.
8278 */
8279typedef struct DRVMAINSTATUS
8280{
8281 /** The LED connectors. */
8282 PDMILEDCONNECTORS ILedConnectors;
8283 /** Pointer to the LED ports interface above us. */
8284 PPDMILEDPORTS pLedPorts;
8285 /** Pointer to the array of LED pointers. */
8286 PPDMLED *papLeds;
8287 /** The unit number corresponding to the first entry in the LED array. */
8288 RTUINT iFirstLUN;
8289 /** The unit number corresponding to the last entry in the LED array.
8290 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8291 RTUINT iLastLUN;
8292} DRVMAINSTATUS, *PDRVMAINSTATUS;
8293
8294
8295/**
8296 * Notification about a unit which have been changed.
8297 *
8298 * The driver must discard any pointers to data owned by
8299 * the unit and requery it.
8300 *
8301 * @param pInterface Pointer to the interface structure containing the called function pointer.
8302 * @param iLUN The unit number.
8303 */
8304DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8305{
8306 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8307 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8308 {
8309 PPDMLED pLed;
8310 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8311 if (RT_FAILURE(rc))
8312 pLed = NULL;
8313 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8314 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8315 }
8316}
8317
8318
8319/**
8320 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8321 */
8322DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8323{
8324 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8325 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8326 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8327 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8328 return NULL;
8329}
8330
8331
8332/**
8333 * Destruct a status driver instance.
8334 *
8335 * @returns VBox status.
8336 * @param pDrvIns The driver instance data.
8337 */
8338DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8339{
8340 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8341 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8342 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8343
8344 if (pData->papLeds)
8345 {
8346 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8347 while (iLed-- > 0)
8348 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8349 }
8350}
8351
8352
8353/**
8354 * Construct a status driver instance.
8355 *
8356 * @copydoc FNPDMDRVCONSTRUCT
8357 */
8358DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8359{
8360 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8361 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8362 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8363
8364 /*
8365 * Validate configuration.
8366 */
8367 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8368 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8369 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8370 ("Configuration error: Not possible to attach anything to this driver!\n"),
8371 VERR_PDM_DRVINS_NO_ATTACH);
8372
8373 /*
8374 * Data.
8375 */
8376 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8377 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8378
8379 /*
8380 * Read config.
8381 */
8382 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8383 if (RT_FAILURE(rc))
8384 {
8385 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8386 return rc;
8387 }
8388
8389 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8390 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8391 pData->iFirstLUN = 0;
8392 else if (RT_FAILURE(rc))
8393 {
8394 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8395 return rc;
8396 }
8397
8398 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8399 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8400 pData->iLastLUN = 0;
8401 else if (RT_FAILURE(rc))
8402 {
8403 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8404 return rc;
8405 }
8406 if (pData->iFirstLUN > pData->iLastLUN)
8407 {
8408 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8409 return VERR_GENERAL_FAILURE;
8410 }
8411
8412 /*
8413 * Get the ILedPorts interface of the above driver/device and
8414 * query the LEDs we want.
8415 */
8416 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8417 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8418 VERR_PDM_MISSING_INTERFACE_ABOVE);
8419
8420 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8421 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8422
8423 return VINF_SUCCESS;
8424}
8425
8426
8427/**
8428 * Keyboard driver registration record.
8429 */
8430const PDMDRVREG Console::DrvStatusReg =
8431{
8432 /* u32Version */
8433 PDM_DRVREG_VERSION,
8434 /* szName */
8435 "MainStatus",
8436 /* szRCMod */
8437 "",
8438 /* szR0Mod */
8439 "",
8440 /* pszDescription */
8441 "Main status driver (Main as in the API).",
8442 /* fFlags */
8443 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8444 /* fClass. */
8445 PDM_DRVREG_CLASS_STATUS,
8446 /* cMaxInstances */
8447 ~0,
8448 /* cbInstance */
8449 sizeof(DRVMAINSTATUS),
8450 /* pfnConstruct */
8451 Console::drvStatus_Construct,
8452 /* pfnDestruct */
8453 Console::drvStatus_Destruct,
8454 /* pfnRelocate */
8455 NULL,
8456 /* pfnIOCtl */
8457 NULL,
8458 /* pfnPowerOn */
8459 NULL,
8460 /* pfnReset */
8461 NULL,
8462 /* pfnSuspend */
8463 NULL,
8464 /* pfnResume */
8465 NULL,
8466 /* pfnAttach */
8467 NULL,
8468 /* pfnDetach */
8469 NULL,
8470 /* pfnPowerOff */
8471 NULL,
8472 /* pfnSoftReset */
8473 NULL,
8474 /* u32EndVersion */
8475 PDM_DRVREG_VERSION
8476};
8477
8478/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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