VirtualBox

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

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

Todo

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