VirtualBox

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

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

FT updates

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