VirtualBox

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

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

The paused->suspended transition is valid

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

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