VirtualBox

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

Last change on this file since 32718 was 32718, checked in by vboxsync, 15 years ago

com/string: Remove bool conversion operator and other convenience error operators. They are hiding programming errors (like incorrect empty string checks, and in one case a free of the wrong pointer).

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

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