VirtualBox

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

Last change on this file since 31558 was 31543, checked in by vboxsync, 14 years ago

build fix

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