VirtualBox

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

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

Main/ConsoleImpl: Setup CoreDumper (Solaris only for now).

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