VirtualBox

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

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

Applied a simple memory leak detector to libjpeg and VBoxC (Windows host only), the code completely disabled by default.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 271.6 KB
Line 
1/* $Id: ConsoleImpl.cpp 32056 2010-08-27 16:04:23Z 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 break;
6277 }
6278
6279 case VMSTATE_SUSPENDED_LS:
6280 case VMSTATE_SUSPENDED_EXT_LS:
6281 {
6282 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6283 if (that->mVMStateChangeCallbackDisabled)
6284 break;
6285 switch (that->mMachineState)
6286 {
6287 case MachineState_Teleporting:
6288 that->setMachineState(MachineState_TeleportingPausedVM);
6289 break;
6290
6291 case MachineState_LiveSnapshotting:
6292 that->setMachineState(MachineState_Saving);
6293 break;
6294
6295 case MachineState_TeleportingPausedVM:
6296 case MachineState_Saving:
6297 /* ignore */
6298 break;
6299
6300 default:
6301 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6302 that->setMachineState(MachineState_Paused);
6303 break;
6304 }
6305 break;
6306 }
6307
6308 case VMSTATE_RUNNING:
6309 {
6310 if ( aOldState == VMSTATE_POWERING_ON
6311 || aOldState == VMSTATE_RESUMING
6312 || aOldState == VMSTATE_RUNNING_FT)
6313 {
6314 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6315
6316 if (that->mVMStateChangeCallbackDisabled)
6317 break;
6318
6319 Assert( ( ( that->mMachineState == MachineState_Starting
6320 || that->mMachineState == MachineState_Paused)
6321 && aOldState == VMSTATE_POWERING_ON)
6322 || ( ( that->mMachineState == MachineState_Restoring
6323 || that->mMachineState == MachineState_TeleportingIn
6324 || that->mMachineState == MachineState_Paused
6325 || that->mMachineState == MachineState_Saving
6326 )
6327 && aOldState == VMSTATE_RESUMING)
6328 || ( that->mMachineState == MachineState_FaultTolerantSyncing
6329 && aOldState == VMSTATE_RUNNING_FT));
6330
6331 that->setMachineState(MachineState_Running);
6332 }
6333
6334 break;
6335 }
6336
6337 case VMSTATE_RUNNING_LS:
6338 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6339 || that->mMachineState == MachineState_Teleporting,
6340 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6341 break;
6342
6343 case VMSTATE_RUNNING_FT:
6344 AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
6345 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6346 break;
6347
6348 case VMSTATE_FATAL_ERROR:
6349 {
6350 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6351
6352 if (that->mVMStateChangeCallbackDisabled)
6353 break;
6354
6355 /* Fatal errors are only for running VMs. */
6356 Assert(Global::IsOnline(that->mMachineState));
6357
6358 /* Note! 'Pause' is used here in want of something better. There
6359 * are currently only two places where fatal errors might be
6360 * raised, so it is not worth adding a new externally
6361 * visible state for this yet. */
6362 that->setMachineState(MachineState_Paused);
6363 break;
6364 }
6365
6366 case VMSTATE_GURU_MEDITATION:
6367 {
6368 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6369
6370 if (that->mVMStateChangeCallbackDisabled)
6371 break;
6372
6373 /* Guru are only for running VMs */
6374 Assert(Global::IsOnline(that->mMachineState));
6375
6376 that->setMachineState(MachineState_Stuck);
6377 break;
6378 }
6379
6380 default: /* shut up gcc */
6381 break;
6382 }
6383}
6384
6385#ifdef VBOX_WITH_USB
6386
6387/**
6388 * Sends a request to VMM to attach the given host device.
6389 * After this method succeeds, the attached device will appear in the
6390 * mUSBDevices collection.
6391 *
6392 * @param aHostDevice device to attach
6393 *
6394 * @note Synchronously calls EMT.
6395 * @note Must be called from under this object's lock.
6396 */
6397HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6398{
6399 AssertReturn(aHostDevice, E_FAIL);
6400 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6401
6402 /* still want a lock object because we need to leave it */
6403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6404
6405 HRESULT hrc;
6406
6407 /*
6408 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6409 * method in EMT (using usbAttachCallback()).
6410 */
6411 Bstr BstrAddress;
6412 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6413 ComAssertComRCRetRC(hrc);
6414
6415 Utf8Str Address(BstrAddress);
6416
6417 Bstr id;
6418 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6419 ComAssertComRCRetRC(hrc);
6420 Guid uuid(id);
6421
6422 BOOL fRemote = FALSE;
6423 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6424 ComAssertComRCRetRC(hrc);
6425
6426 /* protect mpVM */
6427 AutoVMCaller autoVMCaller(this);
6428 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6429
6430 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6431 Address.c_str(), uuid.ptr()));
6432
6433 /* leave the lock before a VMR3* call (EMT will call us back)! */
6434 alock.leave();
6435
6436/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6437 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6438 (PFNRT)usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.c_str(), aMaskedIfs);
6439
6440 /* restore the lock */
6441 alock.enter();
6442
6443 /* hrc is S_OK here */
6444
6445 if (RT_FAILURE(vrc))
6446 {
6447 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6448 Address.c_str(), uuid.ptr(), vrc));
6449
6450 switch (vrc)
6451 {
6452 case VERR_VUSB_NO_PORTS:
6453 hrc = setError(E_FAIL,
6454 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6455 break;
6456 case VERR_VUSB_USBFS_PERMISSION:
6457 hrc = setError(E_FAIL,
6458 tr("Not permitted to open the USB device, check usbfs options"));
6459 break;
6460 default:
6461 hrc = setError(E_FAIL,
6462 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6463 vrc);
6464 break;
6465 }
6466 }
6467
6468 return hrc;
6469}
6470
6471/**
6472 * USB device attach callback used by AttachUSBDevice().
6473 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6474 * so we don't use AutoCaller and don't care about reference counters of
6475 * interface pointers passed in.
6476 *
6477 * @thread EMT
6478 * @note Locks the console object for writing.
6479 */
6480//static
6481DECLCALLBACK(int)
6482Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6483{
6484 LogFlowFuncEnter();
6485 LogFlowFunc(("that={%p}\n", that));
6486
6487 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6488
6489 void *pvRemoteBackend = NULL;
6490 if (aRemote)
6491 {
6492 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6493 Guid guid(*aUuid);
6494
6495 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6496 if (!pvRemoteBackend)
6497 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6498 }
6499
6500 USHORT portVersion = 1;
6501 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6502 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6503 Assert(portVersion == 1 || portVersion == 2);
6504
6505 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6506 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6507 if (RT_SUCCESS(vrc))
6508 {
6509 /* Create a OUSBDevice and add it to the device list */
6510 ComObjPtr<OUSBDevice> device;
6511 device.createObject();
6512 hrc = device->init(aHostDevice);
6513 AssertComRC(hrc);
6514
6515 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6516 that->mUSBDevices.push_back(device);
6517 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6518
6519 /* notify callbacks */
6520 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6521 }
6522
6523 LogFlowFunc(("vrc=%Rrc\n", vrc));
6524 LogFlowFuncLeave();
6525 return vrc;
6526}
6527
6528/**
6529 * Sends a request to VMM to detach the given host device. After this method
6530 * succeeds, the detached device will disappear from the mUSBDevices
6531 * collection.
6532 *
6533 * @param aIt Iterator pointing to the device to detach.
6534 *
6535 * @note Synchronously calls EMT.
6536 * @note Must be called from under this object's lock.
6537 */
6538HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6539{
6540 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6541
6542 /* still want a lock object because we need to leave it */
6543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6544
6545 /* protect mpVM */
6546 AutoVMCaller autoVMCaller(this);
6547 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6548
6549 /* if the device is attached, then there must at least one USB hub. */
6550 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6551
6552 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6553 (*aIt)->id().raw()));
6554
6555 /* leave the lock before a VMR3* call (EMT will call us back)! */
6556 alock.leave();
6557
6558/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6559 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6560 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6561 ComAssertRCRet(vrc, E_FAIL);
6562
6563 return S_OK;
6564}
6565
6566/**
6567 * USB device detach callback used by DetachUSBDevice().
6568 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6569 * so we don't use AutoCaller and don't care about reference counters of
6570 * interface pointers passed in.
6571 *
6572 * @thread EMT
6573 * @note Locks the console object for writing.
6574 */
6575//static
6576DECLCALLBACK(int)
6577Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6578{
6579 LogFlowFuncEnter();
6580 LogFlowFunc(("that={%p}\n", that));
6581
6582 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6583 ComObjPtr<OUSBDevice> device = **aIt;
6584
6585 /*
6586 * If that was a remote device, release the backend pointer.
6587 * The pointer was requested in usbAttachCallback.
6588 */
6589 BOOL fRemote = FALSE;
6590
6591 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6592 if (FAILED(hrc2))
6593 setErrorStatic(hrc2, "GetRemote() failed");
6594
6595 if (fRemote)
6596 {
6597 Guid guid(*aUuid);
6598 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6599 }
6600
6601 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6602
6603 if (RT_SUCCESS(vrc))
6604 {
6605 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6606
6607 /* Remove the device from the collection */
6608 that->mUSBDevices.erase(*aIt);
6609 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6610
6611 /* notify callbacks */
6612 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6613 }
6614
6615 LogFlowFunc(("vrc=%Rrc\n", vrc));
6616 LogFlowFuncLeave();
6617 return vrc;
6618}
6619
6620#endif /* VBOX_WITH_USB */
6621#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6622
6623/**
6624 * Helper function to handle host interface device creation and attachment.
6625 *
6626 * @param networkAdapter the network adapter which attachment should be reset
6627 * @return COM status code
6628 *
6629 * @note The caller must lock this object for writing.
6630 *
6631 * @todo Move this back into the driver!
6632 */
6633HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6634{
6635 LogFlowThisFunc(("\n"));
6636 /* sanity check */
6637 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6638
6639# ifdef VBOX_STRICT
6640 /* paranoia */
6641 NetworkAttachmentType_T attachment;
6642 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6643 Assert(attachment == NetworkAttachmentType_Bridged);
6644# endif /* VBOX_STRICT */
6645
6646 HRESULT rc = S_OK;
6647
6648 ULONG slot = 0;
6649 rc = networkAdapter->COMGETTER(Slot)(&slot);
6650 AssertComRC(rc);
6651
6652# ifdef RT_OS_LINUX
6653 /*
6654 * Allocate a host interface device
6655 */
6656 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6657 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6658 if (RT_SUCCESS(rcVBox))
6659 {
6660 /*
6661 * Set/obtain the tap interface.
6662 */
6663 struct ifreq IfReq;
6664 memset(&IfReq, 0, sizeof(IfReq));
6665 /* The name of the TAP interface we are using */
6666 Bstr tapDeviceName;
6667 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6668 if (FAILED(rc))
6669 tapDeviceName.setNull(); /* Is this necessary? */
6670 if (tapDeviceName.isEmpty())
6671 {
6672 LogRel(("No TAP device name was supplied.\n"));
6673 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6674 }
6675
6676 if (SUCCEEDED(rc))
6677 {
6678 /* If we are using a static TAP device then try to open it. */
6679 Utf8Str str(tapDeviceName);
6680 if (str.length() <= sizeof(IfReq.ifr_name))
6681 strcpy(IfReq.ifr_name, str.c_str());
6682 else
6683 memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6684 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6685 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6686 if (rcVBox != 0)
6687 {
6688 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6689 rc = setError(E_FAIL,
6690 tr("Failed to open the host network interface %ls"),
6691 tapDeviceName.raw());
6692 }
6693 }
6694 if (SUCCEEDED(rc))
6695 {
6696 /*
6697 * Make it pollable.
6698 */
6699 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6700 {
6701 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6702 /*
6703 * Here is the right place to communicate the TAP file descriptor and
6704 * the host interface name to the server if/when it becomes really
6705 * necessary.
6706 */
6707 maTAPDeviceName[slot] = tapDeviceName;
6708 rcVBox = VINF_SUCCESS;
6709 }
6710 else
6711 {
6712 int iErr = errno;
6713
6714 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6715 rcVBox = VERR_HOSTIF_BLOCKING;
6716 rc = setError(E_FAIL,
6717 tr("could not set up the host networking device for non blocking access: %s"),
6718 strerror(errno));
6719 }
6720 }
6721 }
6722 else
6723 {
6724 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6725 switch (rcVBox)
6726 {
6727 case VERR_ACCESS_DENIED:
6728 /* will be handled by our caller */
6729 rc = rcVBox;
6730 break;
6731 default:
6732 rc = setError(E_FAIL,
6733 tr("Could not set up the host networking device: %Rrc"),
6734 rcVBox);
6735 break;
6736 }
6737 }
6738
6739# elif defined(RT_OS_FREEBSD)
6740 /*
6741 * Set/obtain the tap interface.
6742 */
6743 /* The name of the TAP interface we are using */
6744 Bstr tapDeviceName;
6745 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6746 if (FAILED(rc))
6747 tapDeviceName.setNull(); /* Is this necessary? */
6748 if (tapDeviceName.isEmpty())
6749 {
6750 LogRel(("No TAP device name was supplied.\n"));
6751 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6752 }
6753 char szTapdev[1024] = "/dev/";
6754 /* If we are using a static TAP device then try to open it. */
6755 Utf8Str str(tapDeviceName);
6756 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6757 strcat(szTapdev, str.c_str());
6758 else
6759 memcpy(szTapdev + strlen(szTapdev), str.c_str(),
6760 sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6761 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6762 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6763
6764 if (RT_SUCCESS(rcVBox))
6765 maTAPDeviceName[slot] = tapDeviceName;
6766 else
6767 {
6768 switch (rcVBox)
6769 {
6770 case VERR_ACCESS_DENIED:
6771 /* will be handled by our caller */
6772 rc = rcVBox;
6773 break;
6774 default:
6775 rc = setError(E_FAIL,
6776 tr("Failed to open the host network interface %ls"),
6777 tapDeviceName.raw());
6778 break;
6779 }
6780 }
6781# else
6782# error "huh?"
6783# endif
6784 /* in case of failure, cleanup. */
6785 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6786 {
6787 LogRel(("General failure attaching to host interface\n"));
6788 rc = setError(E_FAIL,
6789 tr("General failure attaching to host interface"));
6790 }
6791 LogFlowThisFunc(("rc=%d\n", rc));
6792 return rc;
6793}
6794
6795
6796/**
6797 * Helper function to handle detachment from a host interface
6798 *
6799 * @param networkAdapter the network adapter which attachment should be reset
6800 * @return COM status code
6801 *
6802 * @note The caller must lock this object for writing.
6803 *
6804 * @todo Move this back into the driver!
6805 */
6806HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6807{
6808 /* sanity check */
6809 LogFlowThisFunc(("\n"));
6810 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6811
6812 HRESULT rc = S_OK;
6813# ifdef VBOX_STRICT
6814 /* paranoia */
6815 NetworkAttachmentType_T attachment;
6816 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6817 Assert(attachment == NetworkAttachmentType_Bridged);
6818# endif /* VBOX_STRICT */
6819
6820 ULONG slot = 0;
6821 rc = networkAdapter->COMGETTER(Slot)(&slot);
6822 AssertComRC(rc);
6823
6824 /* is there an open TAP device? */
6825 if (maTapFD[slot] != NIL_RTFILE)
6826 {
6827 /*
6828 * Close the file handle.
6829 */
6830 Bstr tapDeviceName, tapTerminateApplication;
6831 bool isStatic = true;
6832 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6833 if (FAILED(rc) || tapDeviceName.isEmpty())
6834 {
6835 /* If the name is empty, this is a dynamic TAP device, so close it now,
6836 so that the termination script can remove the interface. Otherwise we still
6837 need the FD to pass to the termination script. */
6838 isStatic = false;
6839 int rcVBox = RTFileClose(maTapFD[slot]);
6840 AssertRC(rcVBox);
6841 maTapFD[slot] = NIL_RTFILE;
6842 }
6843 if (isStatic)
6844 {
6845 /* If we are using a static TAP device, we close it now, after having called the
6846 termination script. */
6847 int rcVBox = RTFileClose(maTapFD[slot]);
6848 AssertRC(rcVBox);
6849 }
6850 /* the TAP device name and handle are no longer valid */
6851 maTapFD[slot] = NIL_RTFILE;
6852 maTAPDeviceName[slot] = "";
6853 }
6854 LogFlowThisFunc(("returning %d\n", rc));
6855 return rc;
6856}
6857
6858#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6859
6860/**
6861 * Called at power down to terminate host interface networking.
6862 *
6863 * @note The caller must lock this object for writing.
6864 */
6865HRESULT Console::powerDownHostInterfaces()
6866{
6867 LogFlowThisFunc(("\n"));
6868
6869 /* sanity check */
6870 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6871
6872 /*
6873 * host interface termination handling
6874 */
6875 HRESULT rc;
6876 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6877 {
6878 ComPtr<INetworkAdapter> networkAdapter;
6879 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6880 if (FAILED(rc)) break;
6881
6882 BOOL enabled = FALSE;
6883 networkAdapter->COMGETTER(Enabled)(&enabled);
6884 if (!enabled)
6885 continue;
6886
6887 NetworkAttachmentType_T attachment;
6888 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6889 if (attachment == NetworkAttachmentType_Bridged)
6890 {
6891#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6892 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6893 if (FAILED(rc2) && SUCCEEDED(rc))
6894 rc = rc2;
6895#endif
6896 }
6897 }
6898
6899 return rc;
6900}
6901
6902
6903/**
6904 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
6905 * and VMR3Teleport.
6906 *
6907 * @param pVM The VM handle.
6908 * @param uPercent Completetion precentage (0-100).
6909 * @param pvUser Pointer to the VMProgressTask structure.
6910 * @return VINF_SUCCESS.
6911 */
6912/*static*/
6913DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6914{
6915 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6916 AssertReturn(task, VERR_INVALID_PARAMETER);
6917
6918 /* update the progress object */
6919 if (task->mProgress)
6920 task->mProgress->SetCurrentOperationProgress(uPercent);
6921
6922 return VINF_SUCCESS;
6923}
6924
6925/**
6926 * @copydoc FNVMATERROR
6927 *
6928 * @remarks Might be some tiny serialization concerns with access to the string
6929 * object here...
6930 */
6931/*static*/ DECLCALLBACK(void)
6932Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6933 const char *pszErrorFmt, va_list va)
6934{
6935 Utf8Str *pErrorText = (Utf8Str *)pvUser;
6936 AssertPtr(pErrorText);
6937
6938 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
6939 va_list va2;
6940 va_copy(va2, va);
6941
6942 /* Append to any the existing error message. */
6943 if (pErrorText->length())
6944 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
6945 pszErrorFmt, &va2, rc, rc);
6946 else
6947 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
6948
6949 va_end(va2);
6950}
6951
6952/**
6953 * VM runtime error callback function.
6954 * See VMSetRuntimeError for the detailed description of parameters.
6955 *
6956 * @param pVM The VM handle.
6957 * @param pvUser The user argument.
6958 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6959 * @param pszErrorId Error ID string.
6960 * @param pszFormat Error message format string.
6961 * @param va Error message arguments.
6962 * @thread EMT.
6963 */
6964/* static */ DECLCALLBACK(void)
6965Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
6966 const char *pszErrorId,
6967 const char *pszFormat, va_list va)
6968{
6969 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6970 LogFlowFuncEnter();
6971
6972 Console *that = static_cast<Console *>(pvUser);
6973 AssertReturnVoid(that);
6974
6975 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
6976
6977 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
6978 fFatal, pszErrorId, message.c_str()));
6979
6980 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
6981
6982 LogFlowFuncLeave();
6983}
6984
6985/**
6986 * Captures USB devices that match filters of the VM.
6987 * Called at VM startup.
6988 *
6989 * @param pVM The VM handle.
6990 *
6991 * @note The caller must lock this object for writing.
6992 */
6993HRESULT Console::captureUSBDevices(PVM pVM)
6994{
6995 LogFlowThisFunc(("\n"));
6996
6997 /* sanity check */
6998 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6999
7000 /* If the machine has an USB controller, ask the USB proxy service to
7001 * capture devices */
7002 PPDMIBASE pBase;
7003 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
7004 if (RT_SUCCESS(vrc))
7005 {
7006 /* leave the lock before calling Host in VBoxSVC since Host may call
7007 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7008 * produce an inter-process dead-lock otherwise. */
7009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7010 alock.leave();
7011
7012 HRESULT hrc = mControl->AutoCaptureUSBDevices();
7013 ComAssertComRCRetRC(hrc);
7014 }
7015 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
7016 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
7017 vrc = VINF_SUCCESS;
7018 else
7019 AssertRC(vrc);
7020
7021 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
7022}
7023
7024
7025/**
7026 * Detach all USB device which are attached to the VM for the
7027 * purpose of clean up and such like.
7028 *
7029 * @note The caller must lock this object for writing.
7030 */
7031void Console::detachAllUSBDevices(bool aDone)
7032{
7033 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
7034
7035 /* sanity check */
7036 AssertReturnVoid(isWriteLockOnCurrentThread());
7037
7038 mUSBDevices.clear();
7039
7040 /* leave the lock before calling Host in VBoxSVC since Host may call
7041 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
7042 * produce an inter-process dead-lock otherwise. */
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044 alock.leave();
7045
7046 mControl->DetachAllUSBDevices(aDone);
7047}
7048
7049/**
7050 * @note Locks this object for writing.
7051 */
7052void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
7053{
7054 LogFlowThisFuncEnter();
7055 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7056
7057 AutoCaller autoCaller(this);
7058 if (!autoCaller.isOk())
7059 {
7060 /* Console has been already uninitialized, deny request */
7061 AssertMsgFailed(("Console is already uninitialized\n"));
7062 LogFlowThisFunc(("Console is already uninitialized\n"));
7063 LogFlowThisFuncLeave();
7064 return;
7065 }
7066
7067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7068
7069 /*
7070 * Mark all existing remote USB devices as dirty.
7071 */
7072 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7073 it != mRemoteUSBDevices.end();
7074 ++it)
7075 {
7076 (*it)->dirty(true);
7077 }
7078
7079 /*
7080 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7081 */
7082 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7083 VRDPUSBDEVICEDESC *e = pDevList;
7084
7085 /* The cbDevList condition must be checked first, because the function can
7086 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7087 */
7088 while (cbDevList >= 2 && e->oNext)
7089 {
7090 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7091 e->idVendor, e->idProduct,
7092 e->oProduct? (char *)e + e->oProduct: ""));
7093
7094 bool fNewDevice = true;
7095
7096 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7097 it != mRemoteUSBDevices.end();
7098 ++it)
7099 {
7100 if ((*it)->devId() == e->id
7101 && (*it)->clientId() == u32ClientId)
7102 {
7103 /* The device is already in the list. */
7104 (*it)->dirty(false);
7105 fNewDevice = false;
7106 break;
7107 }
7108 }
7109
7110 if (fNewDevice)
7111 {
7112 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7113 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7114
7115 /* Create the device object and add the new device to list. */
7116 ComObjPtr<RemoteUSBDevice> device;
7117 device.createObject();
7118 device->init(u32ClientId, e);
7119
7120 mRemoteUSBDevices.push_back(device);
7121
7122 /* Check if the device is ok for current USB filters. */
7123 BOOL fMatched = FALSE;
7124 ULONG fMaskedIfs = 0;
7125
7126 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7127
7128 AssertComRC(hrc);
7129
7130 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7131
7132 if (fMatched)
7133 {
7134 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7135
7136 /// @todo (r=dmik) warning reporting subsystem
7137
7138 if (hrc == S_OK)
7139 {
7140 LogFlowThisFunc(("Device attached\n"));
7141 device->captured(true);
7142 }
7143 }
7144 }
7145
7146 if (cbDevList < e->oNext)
7147 {
7148 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7149 cbDevList, e->oNext));
7150 break;
7151 }
7152
7153 cbDevList -= e->oNext;
7154
7155 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7156 }
7157
7158 /*
7159 * Remove dirty devices, that is those which are not reported by the server anymore.
7160 */
7161 for (;;)
7162 {
7163 ComObjPtr<RemoteUSBDevice> device;
7164
7165 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7166 while (it != mRemoteUSBDevices.end())
7167 {
7168 if ((*it)->dirty())
7169 {
7170 device = *it;
7171 break;
7172 }
7173
7174 ++ it;
7175 }
7176
7177 if (!device)
7178 {
7179 break;
7180 }
7181
7182 USHORT vendorId = 0;
7183 device->COMGETTER(VendorId)(&vendorId);
7184
7185 USHORT productId = 0;
7186 device->COMGETTER(ProductId)(&productId);
7187
7188 Bstr product;
7189 device->COMGETTER(Product)(product.asOutParam());
7190
7191 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7192 vendorId, productId, product.raw()));
7193
7194 /* Detach the device from VM. */
7195 if (device->captured())
7196 {
7197 Bstr uuid;
7198 device->COMGETTER(Id)(uuid.asOutParam());
7199 onUSBDeviceDetach(uuid, NULL);
7200 }
7201
7202 /* And remove it from the list. */
7203 mRemoteUSBDevices.erase(it);
7204 }
7205
7206 LogFlowThisFuncLeave();
7207}
7208
7209/**
7210 * Progress cancelation callback for fault tolerance VM poweron
7211 */
7212static void faultToleranceProgressCancelCallback(void *pvUser)
7213{
7214 PVM pVM = (PVM)pvUser;
7215
7216 if (pVM)
7217 FTMR3CancelStandby(pVM);
7218}
7219
7220/**
7221 * Thread function which starts the VM (also from saved state) and
7222 * track progress.
7223 *
7224 * @param Thread The thread id.
7225 * @param pvUser Pointer to a VMPowerUpTask structure.
7226 * @return VINF_SUCCESS (ignored).
7227 *
7228 * @note Locks the Console object for writing.
7229 */
7230/*static*/
7231DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7232{
7233 LogFlowFuncEnter();
7234
7235 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7236 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7237
7238 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7239 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7240
7241#if defined(RT_OS_WINDOWS)
7242 {
7243 /* initialize COM */
7244 HRESULT hrc = CoInitializeEx(NULL,
7245 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7246 COINIT_SPEED_OVER_MEMORY);
7247 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7248 }
7249#endif
7250
7251 HRESULT rc = S_OK;
7252 int vrc = VINF_SUCCESS;
7253
7254 /* Set up a build identifier so that it can be seen from core dumps what
7255 * exact build was used to produce the core. */
7256 static char saBuildID[40];
7257 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7258 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7259
7260 ComObjPtr<Console> console = task->mConsole;
7261
7262 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7263
7264 /* The lock is also used as a signal from the task initiator (which
7265 * releases it only after RTThreadCreate()) that we can start the job */
7266 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7267
7268 /* sanity */
7269 Assert(console->mpVM == NULL);
7270
7271 try
7272 {
7273 /* wait for auto reset ops to complete so that we can successfully lock
7274 * the attached hard disks by calling LockMedia() below */
7275 for (VMPowerUpTask::ProgressList::const_iterator
7276 it = task->hardDiskProgresses.begin();
7277 it != task->hardDiskProgresses.end(); ++ it)
7278 {
7279 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7280 AssertComRC(rc2);
7281 }
7282
7283 /*
7284 * Lock attached media. This method will also check their accessibility.
7285 * If we're a teleporter, we'll have to postpone this action so we can
7286 * migrate between local processes.
7287 *
7288 * Note! The media will be unlocked automatically by
7289 * SessionMachine::setMachineState() when the VM is powered down.
7290 */
7291 if ( !task->mTeleporterEnabled
7292 && task->mEnmFaultToleranceState != FaultToleranceState_Standby)
7293 {
7294 rc = console->mControl->LockMedia();
7295 if (FAILED(rc)) throw rc;
7296 }
7297
7298#ifdef VBOX_WITH_VRDP
7299
7300 /* Create the VRDP server. In case of headless operation, this will
7301 * also create the framebuffer, required at VM creation.
7302 */
7303 ConsoleVRDPServer *server = console->consoleVRDPServer();
7304 Assert(server);
7305
7306 /* Does VRDP server call Console from the other thread?
7307 * Not sure (and can change), so leave the lock just in case.
7308 */
7309 alock.leave();
7310 vrc = server->Launch();
7311 alock.enter();
7312
7313 if (vrc == VERR_NET_ADDRESS_IN_USE)
7314 {
7315 Utf8Str errMsg;
7316 Bstr bstr;
7317 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7318 Utf8Str ports = bstr;
7319 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7320 ports.c_str());
7321 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7322 vrc, errMsg.c_str()));
7323 }
7324 else if (RT_FAILURE(vrc))
7325 {
7326 Utf8Str errMsg;
7327 switch (vrc)
7328 {
7329 case VERR_FILE_NOT_FOUND:
7330 {
7331 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7332 break;
7333 }
7334 default:
7335 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7336 vrc);
7337 }
7338 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7339 vrc, errMsg.c_str()));
7340 throw setErrorStatic(E_FAIL, errMsg.c_str());
7341 }
7342
7343#endif /* VBOX_WITH_VRDP */
7344
7345 ComPtr<IMachine> pMachine = console->machine();
7346 ULONG cCpus = 1;
7347 pMachine->COMGETTER(CPUCount)(&cCpus);
7348
7349 /*
7350 * Create the VM
7351 */
7352 PVM pVM;
7353 /*
7354 * leave the lock since EMT will call Console. It's safe because
7355 * mMachineState is either Starting or Restoring state here.
7356 */
7357 alock.leave();
7358
7359 vrc = VMR3Create(cCpus, Console::genericVMSetErrorCallback, &task->mErrorMsg,
7360 task->mConfigConstructor, static_cast<Console *>(console),
7361 &pVM);
7362
7363 alock.enter();
7364
7365#ifdef VBOX_WITH_VRDP
7366 /* Enable client connections to the server. */
7367 console->consoleVRDPServer()->EnableConnections();
7368#endif /* VBOX_WITH_VRDP */
7369
7370 if (RT_SUCCESS(vrc))
7371 {
7372 do
7373 {
7374 /*
7375 * Register our load/save state file handlers
7376 */
7377 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7378 NULL, NULL, NULL,
7379 NULL, saveStateFileExec, NULL,
7380 NULL, loadStateFileExec, NULL,
7381 static_cast<Console *>(console));
7382 AssertRCBreak(vrc);
7383
7384 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7385 AssertRC(vrc);
7386 if (RT_FAILURE(vrc))
7387 break;
7388
7389 /*
7390 * Synchronize debugger settings
7391 */
7392 MachineDebugger *machineDebugger = console->getMachineDebugger();
7393 if (machineDebugger)
7394 {
7395 machineDebugger->flushQueuedSettings();
7396 }
7397
7398 /*
7399 * Shared Folders
7400 */
7401 if (console->getVMMDev()->isShFlActive())
7402 {
7403 /* Does the code below call Console from the other thread?
7404 * Not sure, so leave the lock just in case. */
7405 alock.leave();
7406
7407 for (SharedFolderDataMap::const_iterator
7408 it = task->mSharedFolders.begin();
7409 it != task->mSharedFolders.end();
7410 ++ it)
7411 {
7412 rc = console->createSharedFolder((*it).first, (*it).second);
7413 if (FAILED(rc)) break;
7414 }
7415 if (FAILED(rc)) break;
7416
7417 /* enter the lock again */
7418 alock.enter();
7419 }
7420
7421 /*
7422 * Capture USB devices.
7423 */
7424 rc = console->captureUSBDevices(pVM);
7425 if (FAILED(rc)) break;
7426
7427 /* leave the lock before a lengthy operation */
7428 alock.leave();
7429
7430 /* Load saved state? */
7431 if (task->mSavedStateFile.length())
7432 {
7433 LogFlowFunc(("Restoring saved state from '%s'...\n",
7434 task->mSavedStateFile.c_str()));
7435
7436 vrc = VMR3LoadFromFile(pVM,
7437 task->mSavedStateFile.c_str(),
7438 Console::stateProgressCallback,
7439 static_cast<VMProgressTask*>(task.get()));
7440
7441 if (RT_SUCCESS(vrc))
7442 {
7443 if (task->mStartPaused)
7444 /* done */
7445 console->setMachineState(MachineState_Paused);
7446 else
7447 {
7448 /* Start/Resume the VM execution */
7449 vrc = VMR3Resume(pVM);
7450 AssertRC(vrc);
7451 }
7452 }
7453
7454 /* Power off in case we failed loading or resuming the VM */
7455 if (RT_FAILURE(vrc))
7456 {
7457 int vrc2 = VMR3PowerOff(pVM);
7458 AssertRC(vrc2);
7459 }
7460 }
7461 else if (task->mTeleporterEnabled)
7462 {
7463 /* -> ConsoleImplTeleporter.cpp */
7464 bool fPowerOffOnFailure;
7465 rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7466 task->mProgress, &fPowerOffOnFailure);
7467 if (FAILED(rc) && fPowerOffOnFailure)
7468 {
7469 ErrorInfoKeeper eik;
7470 int vrc2 = VMR3PowerOff(pVM);
7471 AssertRC(vrc2);
7472 }
7473 }
7474 else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
7475 {
7476 /*
7477 * Get the config.
7478 */
7479 ULONG uPort;
7480 ULONG uInterval;
7481 Bstr bstrAddress, bstrPassword;
7482
7483 rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
7484 if (SUCCEEDED(rc))
7485 {
7486 rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
7487 if (SUCCEEDED(rc))
7488 rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
7489 if (SUCCEEDED(rc))
7490 rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
7491 }
7492 if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
7493 {
7494 if (SUCCEEDED(rc))
7495 {
7496 Utf8Str strAddress(bstrAddress);
7497 const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
7498 Utf8Str strPassword(bstrPassword);
7499 const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
7500
7501 /* Power on the FT enabled VM. */
7502 vrc = FTMR3PowerOn(pVM, (task->mEnmFaultToleranceState == FaultToleranceState_Master) /* fMaster */, uInterval, pszAddress, uPort, pszPassword);
7503 AssertRC(vrc);
7504 }
7505 task->mProgress->setCancelCallback(NULL, NULL);
7506 }
7507 else
7508 rc = E_FAIL;
7509 }
7510 else if (task->mStartPaused)
7511 /* done */
7512 console->setMachineState(MachineState_Paused);
7513 else
7514 {
7515 /* Power on the VM (i.e. start executing) */
7516 vrc = VMR3PowerOn(pVM);
7517 AssertRC(vrc);
7518 }
7519
7520 /* enter the lock again */
7521 alock.enter();
7522 }
7523 while (0);
7524
7525 /* On failure, destroy the VM */
7526 if (FAILED(rc) || RT_FAILURE(vrc))
7527 {
7528 /* preserve existing error info */
7529 ErrorInfoKeeper eik;
7530
7531 /* powerDown() will call VMR3Destroy() and do all necessary
7532 * cleanup (VRDP, USB devices) */
7533 HRESULT rc2 = console->powerDown();
7534 AssertComRC(rc2);
7535 }
7536 else
7537 {
7538 /*
7539 * Deregister the VMSetError callback. This is necessary as the
7540 * pfnVMAtError() function passed to VMR3Create() is supposed to
7541 * be sticky but our error callback isn't.
7542 */
7543 alock.leave();
7544 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7545 /** @todo register another VMSetError callback? */
7546 alock.enter();
7547 }
7548 }
7549 else
7550 {
7551 /*
7552 * If VMR3Create() failed it has released the VM memory.
7553 */
7554 console->mpVM = NULL;
7555 }
7556
7557 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7558 {
7559 /* If VMR3Create() or one of the other calls in this function fail,
7560 * an appropriate error message has been set in task->mErrorMsg.
7561 * However since that happens via a callback, the rc status code in
7562 * this function is not updated.
7563 */
7564 if (!task->mErrorMsg.length())
7565 {
7566 /* If the error message is not set but we've got a failure,
7567 * convert the VBox status code into a meaningful error message.
7568 * This becomes unused once all the sources of errors set the
7569 * appropriate error message themselves.
7570 */
7571 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7572 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7573 vrc);
7574 }
7575
7576 /* Set the error message as the COM error.
7577 * Progress::notifyComplete() will pick it up later. */
7578 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
7579 }
7580 }
7581 catch (HRESULT aRC) { rc = aRC; }
7582
7583 if ( console->mMachineState == MachineState_Starting
7584 || console->mMachineState == MachineState_Restoring
7585 || console->mMachineState == MachineState_TeleportingIn
7586 )
7587 {
7588 /* We are still in the Starting/Restoring state. This means one of:
7589 *
7590 * 1) we failed before VMR3Create() was called;
7591 * 2) VMR3Create() failed.
7592 *
7593 * In both cases, there is no need to call powerDown(), but we still
7594 * need to go back to the PoweredOff/Saved state. Reuse
7595 * vmstateChangeCallback() for that purpose.
7596 */
7597
7598 /* preserve existing error info */
7599 ErrorInfoKeeper eik;
7600
7601 Assert(console->mpVM == NULL);
7602 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7603 console);
7604 }
7605
7606 /*
7607 * Evaluate the final result. Note that the appropriate mMachineState value
7608 * is already set by vmstateChangeCallback() in all cases.
7609 */
7610
7611 /* leave the lock, don't need it any more */
7612 alock.leave();
7613
7614 if (SUCCEEDED(rc))
7615 {
7616 /* Notify the progress object of the success */
7617 task->mProgress->notifyComplete(S_OK);
7618 }
7619 else
7620 {
7621 /* The progress object will fetch the current error info */
7622 task->mProgress->notifyComplete(rc);
7623 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7624 }
7625
7626 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7627 console->mControl->EndPowerUp(rc);
7628
7629#if defined(RT_OS_WINDOWS)
7630 /* uninitialize COM */
7631 CoUninitialize();
7632#endif
7633
7634 LogFlowFuncLeave();
7635
7636 return VINF_SUCCESS;
7637}
7638
7639
7640/**
7641 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7642 *
7643 * @param pConsole Reference to the console object.
7644 * @param pVM The VM handle.
7645 * @param lInstance The instance of the controller.
7646 * @param pcszDevice The name of the controller type.
7647 * @param enmBus The storage bus type of the controller.
7648 * @param fSetupMerge Whether to set up a medium merge
7649 * @param uMergeSource Merge source image index
7650 * @param uMergeTarget Merge target image index
7651 * @param aMediumAtt The medium attachment.
7652 * @param aMachineState The current machine state.
7653 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7654 * @return VBox status code.
7655 */
7656/* static */
7657DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7658 PVM pVM,
7659 const char *pcszDevice,
7660 unsigned uInstance,
7661 StorageBus_T enmBus,
7662 bool fUseHostIOCache,
7663 bool fSetupMerge,
7664 unsigned uMergeSource,
7665 unsigned uMergeTarget,
7666 IMediumAttachment *aMediumAtt,
7667 MachineState_T aMachineState,
7668 HRESULT *phrc)
7669{
7670 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7671
7672 int rc;
7673 HRESULT hrc;
7674 Bstr bstr;
7675 *phrc = S_OK;
7676#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7677#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7678
7679 /* Ignore attachments other than hard disks, since at the moment they are
7680 * not subject to snapshotting in general. */
7681 DeviceType_T lType;
7682 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7683 if (lType != DeviceType_HardDisk)
7684 return VINF_SUCCESS;
7685
7686 /* Determine the base path for the device instance. */
7687 PCFGMNODE pCtlInst;
7688 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7689 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7690
7691 /* Update the device instance configuration. */
7692 rc = pConsole->configMediumAttachment(pCtlInst,
7693 pcszDevice,
7694 uInstance,
7695 enmBus,
7696 fUseHostIOCache,
7697 fSetupMerge,
7698 uMergeSource,
7699 uMergeTarget,
7700 aMediumAtt,
7701 aMachineState,
7702 phrc,
7703 true /* fAttachDetach */,
7704 false /* fForceUnmount */,
7705 pVM,
7706 NULL /* paLedDevType */);
7707 /** @todo this dumps everything attached to this device instance, which
7708 * is more than necessary. Dumping the changed LUN would be enough. */
7709 CFGMR3Dump(pCtlInst);
7710 RC_CHECK();
7711
7712#undef RC_CHECK
7713#undef H
7714
7715 LogFlowFunc(("Returns success\n"));
7716 return VINF_SUCCESS;
7717}
7718
7719/**
7720 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7721 */
7722static void takesnapshotProgressCancelCallback(void *pvUser)
7723{
7724 PVM pVM = (PVM)pvUser;
7725 SSMR3Cancel(pVM);
7726}
7727
7728/**
7729 * Worker thread created by Console::TakeSnapshot.
7730 * @param Thread The current thread (ignored).
7731 * @param pvUser The task.
7732 * @return VINF_SUCCESS (ignored).
7733 */
7734/*static*/
7735DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7736{
7737 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7738
7739 // taking a snapshot consists of the following:
7740
7741 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7742 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7743 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7744 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7745 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7746
7747 Console *that = pTask->mConsole;
7748 bool fBeganTakingSnapshot = false;
7749 bool fSuspenededBySave = false;
7750
7751 AutoCaller autoCaller(that);
7752 if (FAILED(autoCaller.rc()))
7753 {
7754 that->mptrCancelableProgress.setNull();
7755 return autoCaller.rc();
7756 }
7757
7758 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7759
7760 HRESULT rc = S_OK;
7761
7762 try
7763 {
7764 /* STEP 1 + 2:
7765 * request creating the diff images on the server and create the snapshot object
7766 * (this will set the machine state to Saving on the server to block
7767 * others from accessing this machine)
7768 */
7769 rc = that->mControl->BeginTakingSnapshot(that,
7770 pTask->bstrName,
7771 pTask->bstrDescription,
7772 pTask->mProgress,
7773 pTask->fTakingSnapshotOnline,
7774 pTask->bstrSavedStateFile.asOutParam());
7775 if (FAILED(rc))
7776 throw rc;
7777
7778 fBeganTakingSnapshot = true;
7779
7780 /*
7781 * state file is non-null only when the VM is paused
7782 * (i.e. creating a snapshot online)
7783 */
7784 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7785 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
7786 if (!f)
7787 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
7788
7789 /* sync the state with the server */
7790 if (pTask->lastMachineState == MachineState_Running)
7791 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7792 else
7793 that->setMachineStateLocally(MachineState_Saving);
7794
7795 // STEP 3: save the VM state (if online)
7796 if (pTask->fTakingSnapshotOnline)
7797 {
7798 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7799
7800 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7801 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7802 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7803
7804 alock.leave();
7805 LogFlowFunc(("VMR3Save...\n"));
7806 int vrc = VMR3Save(that->mpVM,
7807 strSavedStateFile.c_str(),
7808 NULL /* pStreamOps */,
7809 NULL /* pvStreamOpsUser */,
7810 true /*fContinueAfterwards*/,
7811 Console::stateProgressCallback,
7812 (void*)pTask,
7813 &fSuspenededBySave);
7814 alock.enter();
7815 if (RT_FAILURE(vrc))
7816 throw setErrorStatic(E_FAIL,
7817 tr("Failed to save the machine state to '%s' (%Rrc)"),
7818 strSavedStateFile.c_str(), vrc);
7819
7820 pTask->mProgress->setCancelCallback(NULL, NULL);
7821 if (!pTask->mProgress->notifyPointOfNoReturn())
7822 throw setErrorStatic(E_FAIL, tr("Canceled"));
7823 that->mptrCancelableProgress.setNull();
7824
7825 // STEP 4: reattach hard disks
7826 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7827
7828 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7829 1); // operation weight, same as computed when setting up progress object
7830
7831 com::SafeIfaceArray<IMediumAttachment> atts;
7832 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7833 if (FAILED(rc))
7834 throw rc;
7835
7836 for (size_t i = 0;
7837 i < atts.size();
7838 ++i)
7839 {
7840 ComPtr<IStorageController> controller;
7841 Bstr controllerName;
7842 ULONG lInstance;
7843 StorageControllerType_T enmController;
7844 StorageBus_T enmBus;
7845 BOOL fUseHostIOCache;
7846
7847 /*
7848 * We can't pass a storage controller object directly
7849 * (g++ complains about not being able to pass non POD types through '...')
7850 * so we have to query needed values here and pass them.
7851 */
7852 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
7853 if (FAILED(rc))
7854 throw rc;
7855
7856 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7857 if (FAILED(rc))
7858 throw rc;
7859
7860 rc = controller->COMGETTER(ControllerType)(&enmController);
7861 if (FAILED(rc))
7862 throw rc;
7863 rc = controller->COMGETTER(Instance)(&lInstance);
7864 if (FAILED(rc))
7865 throw rc;
7866 rc = controller->COMGETTER(Bus)(&enmBus);
7867 if (FAILED(rc))
7868 throw rc;
7869 rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
7870 if (FAILED(rc))
7871 throw rc;
7872
7873 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7874
7875 /*
7876 * don't leave the lock since reconfigureMediumAttachment
7877 * isn't going to need the Console lock.
7878 */
7879 vrc = VMR3ReqCallWait(that->mpVM,
7880 VMCPUID_ANY,
7881 (PFNRT)reconfigureMediumAttachment,
7882 12,
7883 that,
7884 that->mpVM,
7885 pcszDevice,
7886 lInstance,
7887 enmBus,
7888 fUseHostIOCache,
7889 false /* fSetupMerge */,
7890 0 /* uMergeSource */,
7891 0 /* uMergeTarget */,
7892 atts[i],
7893 that->mMachineState,
7894 &rc);
7895 if (RT_FAILURE(vrc))
7896 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
7897 if (FAILED(rc))
7898 throw rc;
7899 }
7900 }
7901
7902 /*
7903 * finalize the requested snapshot object.
7904 * This will reset the machine state to the state it had right
7905 * before calling mControl->BeginTakingSnapshot().
7906 */
7907 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
7908 // do not throw rc here because we can't call EndTakingSnapshot() twice
7909 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7910 }
7911 catch (HRESULT rcThrown)
7912 {
7913 /* preserve existing error info */
7914 ErrorInfoKeeper eik;
7915
7916 if (fBeganTakingSnapshot)
7917 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
7918
7919 rc = rcThrown;
7920 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7921 }
7922 Assert(alock.isWriteLockOnCurrentThread());
7923
7924 if (FAILED(rc)) /* Must come before calling setMachineState. */
7925 pTask->mProgress->notifyComplete(rc);
7926
7927 /*
7928 * Fix up the machine state.
7929 *
7930 * For live snapshots we do all the work, for the two other variantions we
7931 * just update the local copy.
7932 */
7933 MachineState_T enmMachineState;
7934 that->mMachine->COMGETTER(State)(&enmMachineState);
7935 if ( that->mMachineState == MachineState_LiveSnapshotting
7936 || that->mMachineState == MachineState_Saving)
7937 {
7938
7939 if (!pTask->fTakingSnapshotOnline)
7940 that->setMachineStateLocally(pTask->lastMachineState);
7941 else if (SUCCEEDED(rc))
7942 {
7943 Assert( pTask->lastMachineState == MachineState_Running
7944 || pTask->lastMachineState == MachineState_Paused);
7945 Assert(that->mMachineState == MachineState_Saving);
7946 if (pTask->lastMachineState == MachineState_Running)
7947 {
7948 LogFlowFunc(("VMR3Resume...\n"));
7949 alock.leave();
7950 int vrc = VMR3Resume(that->mpVM);
7951 alock.enter();
7952 if (RT_FAILURE(vrc))
7953 {
7954 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
7955 pTask->mProgress->notifyComplete(rc);
7956 if (that->mMachineState == MachineState_Saving)
7957 that->setMachineStateLocally(MachineState_Paused);
7958 }
7959 }
7960 else
7961 that->setMachineStateLocally(MachineState_Paused);
7962 }
7963 else
7964 {
7965 /** @todo this could probably be made more generic and reused elsewhere. */
7966 /* paranoid cleanup on for a failed online snapshot. */
7967 VMSTATE enmVMState = VMR3GetState(that->mpVM);
7968 switch (enmVMState)
7969 {
7970 case VMSTATE_RUNNING:
7971 case VMSTATE_RUNNING_LS:
7972 case VMSTATE_DEBUGGING:
7973 case VMSTATE_DEBUGGING_LS:
7974 case VMSTATE_POWERING_OFF:
7975 case VMSTATE_POWERING_OFF_LS:
7976 case VMSTATE_RESETTING:
7977 case VMSTATE_RESETTING_LS:
7978 Assert(!fSuspenededBySave);
7979 that->setMachineState(MachineState_Running);
7980 break;
7981
7982 case VMSTATE_GURU_MEDITATION:
7983 case VMSTATE_GURU_MEDITATION_LS:
7984 that->setMachineState(MachineState_Stuck);
7985 break;
7986
7987 case VMSTATE_FATAL_ERROR:
7988 case VMSTATE_FATAL_ERROR_LS:
7989 if (pTask->lastMachineState == MachineState_Paused)
7990 that->setMachineStateLocally(pTask->lastMachineState);
7991 else
7992 that->setMachineState(MachineState_Paused);
7993 break;
7994
7995 default:
7996 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
7997 case VMSTATE_SUSPENDED:
7998 case VMSTATE_SUSPENDED_LS:
7999 case VMSTATE_SUSPENDING:
8000 case VMSTATE_SUSPENDING_LS:
8001 case VMSTATE_SUSPENDING_EXT_LS:
8002 if (fSuspenededBySave)
8003 {
8004 Assert(pTask->lastMachineState == MachineState_Running);
8005 LogFlowFunc(("VMR3Resume (on failure)...\n"));
8006 alock.leave();
8007 int vrc = VMR3Resume(that->mpVM);
8008 alock.enter();
8009 AssertLogRelRC(vrc);
8010 if (RT_FAILURE(vrc))
8011 that->setMachineState(MachineState_Paused);
8012 }
8013 else if (pTask->lastMachineState == MachineState_Paused)
8014 that->setMachineStateLocally(pTask->lastMachineState);
8015 else
8016 that->setMachineState(MachineState_Paused);
8017 break;
8018 }
8019
8020 }
8021 }
8022 /*else: somebody else has change the state... Leave it. */
8023
8024 /* check the remote state to see that we got it right. */
8025 that->mMachine->COMGETTER(State)(&enmMachineState);
8026 AssertLogRelMsg(that->mMachineState == enmMachineState,
8027 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
8028 Global::stringifyMachineState(enmMachineState) ));
8029
8030
8031 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
8032 pTask->mProgress->notifyComplete(rc);
8033
8034 delete pTask;
8035
8036 LogFlowFuncLeave();
8037 return VINF_SUCCESS;
8038}
8039
8040/**
8041 * Thread for executing the saved state operation.
8042 *
8043 * @param Thread The thread handle.
8044 * @param pvUser Pointer to a VMSaveTask structure.
8045 * @return VINF_SUCCESS (ignored).
8046 *
8047 * @note Locks the Console object for writing.
8048 */
8049/*static*/
8050DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
8051{
8052 LogFlowFuncEnter();
8053
8054 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
8055 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8056
8057 Assert(task->mSavedStateFile.length());
8058 Assert(!task->mProgress.isNull());
8059
8060 const ComObjPtr<Console> &that = task->mConsole;
8061 Utf8Str errMsg;
8062 HRESULT rc = S_OK;
8063
8064 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
8065
8066 bool fSuspenededBySave;
8067 int vrc = VMR3Save(that->mpVM,
8068 task->mSavedStateFile.c_str(),
8069 NULL /* pStreamOps */,
8070 NULL /* pvStreamOpsUser */,
8071 false, /*fContinueAfterwards*/
8072 Console::stateProgressCallback,
8073 static_cast<VMProgressTask*>(task.get()),
8074 &fSuspenededBySave);
8075 if (RT_FAILURE(vrc))
8076 {
8077 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
8078 task->mSavedStateFile.c_str(), vrc);
8079 rc = E_FAIL;
8080 }
8081 Assert(!fSuspenededBySave);
8082
8083 /* lock the console once we're going to access it */
8084 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8085
8086 /*
8087 * finalize the requested save state procedure.
8088 * In case of success, the server will set the machine state to Saved;
8089 * in case of failure it will reset the it to the state it had right
8090 * before calling mControl->BeginSavingState().
8091 */
8092 that->mControl->EndSavingState(SUCCEEDED(rc));
8093
8094 /* synchronize the state with the server */
8095 if (!FAILED(rc))
8096 {
8097 /*
8098 * The machine has been successfully saved, so power it down
8099 * (vmstateChangeCallback() will set state to Saved on success).
8100 * Note: we release the task's VM caller, otherwise it will
8101 * deadlock.
8102 */
8103 task->releaseVMCaller();
8104
8105 rc = that->powerDown();
8106 }
8107
8108 /* notify the progress object about operation completion */
8109 if (SUCCEEDED(rc))
8110 task->mProgress->notifyComplete(S_OK);
8111 else
8112 {
8113 if (errMsg.length())
8114 task->mProgress->notifyComplete(rc,
8115 COM_IIDOF(IConsole),
8116 Console::getStaticComponentName(),
8117 errMsg.c_str());
8118 else
8119 task->mProgress->notifyComplete(rc);
8120 }
8121
8122 LogFlowFuncLeave();
8123 return VINF_SUCCESS;
8124}
8125
8126/**
8127 * Thread for powering down the Console.
8128 *
8129 * @param Thread The thread handle.
8130 * @param pvUser Pointer to the VMTask structure.
8131 * @return VINF_SUCCESS (ignored).
8132 *
8133 * @note Locks the Console object for writing.
8134 */
8135/*static*/
8136DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8137{
8138 LogFlowFuncEnter();
8139
8140 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8141 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8142
8143 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8144
8145 const ComObjPtr<Console> &that = task->mConsole;
8146
8147 /* Note: no need to use addCaller() to protect Console because VMTask does
8148 * that */
8149
8150 /* wait until the method tat started us returns */
8151 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8152
8153 /* release VM caller to avoid the powerDown() deadlock */
8154 task->releaseVMCaller();
8155
8156 that->powerDown(task->mProgress);
8157
8158 LogFlowFuncLeave();
8159 return VINF_SUCCESS;
8160}
8161
8162/**
8163 * The Main status driver instance data.
8164 */
8165typedef struct DRVMAINSTATUS
8166{
8167 /** The LED connectors. */
8168 PDMILEDCONNECTORS ILedConnectors;
8169 /** Pointer to the LED ports interface above us. */
8170 PPDMILEDPORTS pLedPorts;
8171 /** Pointer to the array of LED pointers. */
8172 PPDMLED *papLeds;
8173 /** The unit number corresponding to the first entry in the LED array. */
8174 RTUINT iFirstLUN;
8175 /** The unit number corresponding to the last entry in the LED array.
8176 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8177 RTUINT iLastLUN;
8178} DRVMAINSTATUS, *PDRVMAINSTATUS;
8179
8180
8181/**
8182 * Notification about a unit which have been changed.
8183 *
8184 * The driver must discard any pointers to data owned by
8185 * the unit and requery it.
8186 *
8187 * @param pInterface Pointer to the interface structure containing the called function pointer.
8188 * @param iLUN The unit number.
8189 */
8190DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8191{
8192 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8193 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8194 {
8195 PPDMLED pLed;
8196 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8197 if (RT_FAILURE(rc))
8198 pLed = NULL;
8199 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8200 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8201 }
8202}
8203
8204
8205/**
8206 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8207 */
8208DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8209{
8210 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8211 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8212 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8213 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8214 return NULL;
8215}
8216
8217
8218/**
8219 * Destruct a status driver instance.
8220 *
8221 * @returns VBox status.
8222 * @param pDrvIns The driver instance data.
8223 */
8224DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8225{
8226 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8227 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8228 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8229
8230 if (pData->papLeds)
8231 {
8232 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8233 while (iLed-- > 0)
8234 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8235 }
8236}
8237
8238
8239/**
8240 * Construct a status driver instance.
8241 *
8242 * @copydoc FNPDMDRVCONSTRUCT
8243 */
8244DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8245{
8246 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8247 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8248 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8249
8250 /*
8251 * Validate configuration.
8252 */
8253 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8254 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8255 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8256 ("Configuration error: Not possible to attach anything to this driver!\n"),
8257 VERR_PDM_DRVINS_NO_ATTACH);
8258
8259 /*
8260 * Data.
8261 */
8262 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8263 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8264
8265 /*
8266 * Read config.
8267 */
8268 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8269 if (RT_FAILURE(rc))
8270 {
8271 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8272 return rc;
8273 }
8274
8275 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8276 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8277 pData->iFirstLUN = 0;
8278 else if (RT_FAILURE(rc))
8279 {
8280 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8281 return rc;
8282 }
8283
8284 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8285 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8286 pData->iLastLUN = 0;
8287 else if (RT_FAILURE(rc))
8288 {
8289 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8290 return rc;
8291 }
8292 if (pData->iFirstLUN > pData->iLastLUN)
8293 {
8294 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8295 return VERR_GENERAL_FAILURE;
8296 }
8297
8298 /*
8299 * Get the ILedPorts interface of the above driver/device and
8300 * query the LEDs we want.
8301 */
8302 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8303 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8304 VERR_PDM_MISSING_INTERFACE_ABOVE);
8305
8306 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8307 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8308
8309 return VINF_SUCCESS;
8310}
8311
8312
8313/**
8314 * Keyboard driver registration record.
8315 */
8316const PDMDRVREG Console::DrvStatusReg =
8317{
8318 /* u32Version */
8319 PDM_DRVREG_VERSION,
8320 /* szName */
8321 "MainStatus",
8322 /* szRCMod */
8323 "",
8324 /* szR0Mod */
8325 "",
8326 /* pszDescription */
8327 "Main status driver (Main as in the API).",
8328 /* fFlags */
8329 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8330 /* fClass. */
8331 PDM_DRVREG_CLASS_STATUS,
8332 /* cMaxInstances */
8333 ~0,
8334 /* cbInstance */
8335 sizeof(DRVMAINSTATUS),
8336 /* pfnConstruct */
8337 Console::drvStatus_Construct,
8338 /* pfnDestruct */
8339 Console::drvStatus_Destruct,
8340 /* pfnRelocate */
8341 NULL,
8342 /* pfnIOCtl */
8343 NULL,
8344 /* pfnPowerOn */
8345 NULL,
8346 /* pfnReset */
8347 NULL,
8348 /* pfnSuspend */
8349 NULL,
8350 /* pfnResume */
8351 NULL,
8352 /* pfnAttach */
8353 NULL,
8354 /* pfnDetach */
8355 NULL,
8356 /* pfnPowerOff */
8357 NULL,
8358 /* pfnSoftReset */
8359 NULL,
8360 /* u32EndVersion */
8361 PDM_DRVREG_VERSION
8362};
8363
8364/* 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