VirtualBox

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

Last change on this file since 31403 was 31332, checked in by vboxsync, 14 years ago

Support passing cpu priority change to a running guest.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 266.5 KB
Line 
1/* $Id: ConsoleImpl.cpp 31332 2010-08-03 12:38:49Z 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.raw(), 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::setAuthLibraryError(const char *filename, int rc)
2985{
2986 return setError(E_FAIL, tr("Could not load the external authentication library '%s' (%Rrc)"), filename, rc);
2987}
2988
2989HRESULT Console::setInvalidMachineStateError()
2990{
2991 return setError(VBOX_E_INVALID_VM_STATE,
2992 tr("Invalid machine state: %s"),
2993 Global::stringifyMachineState(mMachineState));
2994}
2995
2996
2997/**
2998 * @copydoc VirtualBox::handleUnexpectedExceptions
2999 */
3000/* static */
3001HRESULT Console::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3002{
3003 try
3004 {
3005 /* re-throw the current exception */
3006 throw;
3007 }
3008 catch (const std::exception &err)
3009 {
3010 return setErrorStatic(E_FAIL,
3011 tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3012 err.what(), typeid(err).name(),
3013 pszFile, iLine, pszFunction);
3014 }
3015 catch (...)
3016 {
3017 return setErrorStatic(E_FAIL,
3018 tr("Unknown exception\n%s[%d] (%s)"),
3019 pszFile, iLine, pszFunction);
3020 }
3021
3022 /* should not get here */
3023 AssertFailed();
3024 return E_FAIL;
3025}
3026
3027/* static */
3028const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
3029{
3030 switch (enmCtrlType)
3031 {
3032 case StorageControllerType_LsiLogic:
3033 return "lsilogicscsi";
3034 case StorageControllerType_BusLogic:
3035 return "buslogic";
3036 case StorageControllerType_LsiLogicSas:
3037 return "lsilogicsas";
3038 case StorageControllerType_IntelAhci:
3039 return "ahci";
3040 case StorageControllerType_PIIX3:
3041 case StorageControllerType_PIIX4:
3042 case StorageControllerType_ICH6:
3043 return "piix3ide";
3044 case StorageControllerType_I82078:
3045 return "i82078";
3046 default:
3047 return NULL;
3048 }
3049}
3050
3051HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
3052{
3053 switch (enmBus)
3054 {
3055 case StorageBus_IDE:
3056 case StorageBus_Floppy:
3057 {
3058 AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
3059 AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
3060 uLun = 2 * port + device;
3061 return S_OK;
3062 }
3063 case StorageBus_SATA:
3064 case StorageBus_SCSI:
3065 case StorageBus_SAS:
3066 {
3067 uLun = port;
3068 return S_OK;
3069 }
3070 default:
3071 uLun = 0;
3072 AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
3073 }
3074}
3075
3076// private methods
3077/////////////////////////////////////////////////////////////////////////////
3078
3079/**
3080 * Process a medium change.
3081 *
3082 * @param aMediumAttachment The medium attachment with the new medium state.
3083 * @param fForce Force medium chance, if it is locked or not.
3084 *
3085 * @note Locks this object for writing.
3086 */
3087HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce)
3088{
3089 AutoCaller autoCaller(this);
3090 AssertComRCReturnRC(autoCaller.rc());
3091
3092 /* We will need to release the write lock before calling EMT */
3093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 HRESULT rc = S_OK;
3096 const char *pszDevice = NULL;
3097
3098 SafeIfaceArray<IStorageController> ctrls;
3099 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
3100 AssertComRC(rc);
3101 IMedium *pMedium;
3102 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
3103 AssertComRC(rc);
3104 Bstr mediumLocation;
3105 if (pMedium)
3106 {
3107 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
3108 AssertComRC(rc);
3109 }
3110
3111 Bstr attCtrlName;
3112 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
3113 AssertComRC(rc);
3114 ComPtr<IStorageController> ctrl;
3115 for (size_t i = 0; i < ctrls.size(); ++i)
3116 {
3117 Bstr ctrlName;
3118 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
3119 AssertComRC(rc);
3120 if (attCtrlName == ctrlName)
3121 {
3122 ctrl = ctrls[i];
3123 break;
3124 }
3125 }
3126 if (ctrl.isNull())
3127 {
3128 return setError(E_FAIL,
3129 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
3130 }
3131 StorageControllerType_T enmCtrlType;
3132 rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
3133 AssertComRC(rc);
3134 pszDevice = convertControllerTypeToDev(enmCtrlType);
3135
3136 StorageBus_T enmBus;
3137 rc = ctrl->COMGETTER(Bus)(&enmBus);
3138 AssertComRC(rc);
3139 ULONG uInstance;
3140 rc = ctrl->COMGETTER(Instance)(&uInstance);
3141 AssertComRC(rc);
3142 BOOL fUseHostIOCache;
3143 rc = ctrl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
3144 AssertComRC(rc);
3145
3146 /* protect mpVM */
3147 AutoVMCaller autoVMCaller(this);
3148 AssertComRCReturnRC(autoVMCaller.rc());
3149
3150 /*
3151 * Call worker in EMT, that's faster and safer than doing everything
3152 * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3153 * here to make requests from under the lock in order to serialize them.
3154 */
3155 PVMREQ pReq;
3156 int vrc = VMR3ReqCall(mpVM,
3157 VMCPUID_ANY,
3158&pReq,
3159 0 /* no wait! */,
3160 VMREQFLAGS_VBOX_STATUS,
3161 (PFNRT)Console::changeRemovableMedium,
3162 7,
3163 this,
3164 pszDevice,
3165 uInstance,
3166 enmBus,
3167 fUseHostIOCache,
3168 aMediumAttachment,
3169 fForce);
3170
3171 /* leave the lock before waiting for a result (EMT will call us back!) */
3172 alock.leave();
3173
3174 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3175 {
3176 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3177 AssertRC(vrc);
3178 if (RT_SUCCESS(vrc))
3179 vrc = pReq->iStatus;
3180 }
3181 VMR3ReqFree(pReq);
3182
3183 if (RT_SUCCESS(vrc))
3184 {
3185 LogFlowThisFunc(("Returns S_OK\n"));
3186 return S_OK;
3187 }
3188
3189 if (!pMedium)
3190 return setError(E_FAIL,
3191 tr("Could not mount the media/drive '%ls' (%Rrc)"),
3192 mediumLocation.raw(), vrc);
3193
3194 return setError(E_FAIL,
3195 tr("Could not unmount the currently mounted media/drive (%Rrc)"),
3196 vrc);
3197}
3198
3199/**
3200 * Performs the medium change in EMT.
3201 *
3202 * @returns VBox status code.
3203 *
3204 * @param pThis Pointer to the Console object.
3205 * @param pcszDevice The PDM device name.
3206 * @param uInstance The PDM device instance.
3207 * @param uLun The PDM LUN number of the drive.
3208 * @param fHostDrive True if this is a host drive attachment.
3209 * @param pszPath The path to the media / drive which is now being mounted / captured.
3210 * If NULL no media or drive is attached and the LUN will be configured with
3211 * the default block driver with no media. This will also be the state if
3212 * mounting / capturing the specified media / drive fails.
3213 * @param pszFormat Medium format string, usually "RAW".
3214 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
3215 *
3216 * @thread EMT
3217 */
3218DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
3219 const char *pcszDevice,
3220 unsigned uInstance,
3221 StorageBus_T enmBus,
3222 bool fUseHostIOCache,
3223 IMediumAttachment *aMediumAtt,
3224 bool fForce)
3225{
3226 LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
3227 pConsole, uInstance, pcszDevice, enmBus, fForce));
3228
3229 AssertReturn(pConsole, VERR_INVALID_PARAMETER);
3230
3231 AutoCaller autoCaller(pConsole);
3232 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3233
3234 PVM pVM = pConsole->mpVM;
3235
3236 /*
3237 * Suspend the VM first.
3238 *
3239 * The VM must not be running since it might have pending I/O to
3240 * the drive which is being changed.
3241 */
3242 bool fResume;
3243 VMSTATE enmVMState = VMR3GetState(pVM);
3244 switch (enmVMState)
3245 {
3246 case VMSTATE_RESETTING:
3247 case VMSTATE_RUNNING:
3248 {
3249 LogFlowFunc(("Suspending the VM...\n"));
3250 /* disable the callback to prevent Console-level state change */
3251 pConsole->mVMStateChangeCallbackDisabled = true;
3252 int rc = VMR3Suspend(pVM);
3253 pConsole->mVMStateChangeCallbackDisabled = false;
3254 AssertRCReturn(rc, rc);
3255 fResume = true;
3256 break;
3257 }
3258
3259 case VMSTATE_SUSPENDED:
3260 case VMSTATE_CREATED:
3261 case VMSTATE_OFF:
3262 fResume = false;
3263 break;
3264
3265 case VMSTATE_RUNNING_LS:
3266 return setErrorInternal(VBOX_E_INVALID_VM_STATE,
3267 COM_IIDOF(IConsole),
3268 getStaticComponentName(),
3269 Utf8Str(tr("Cannot change drive during live migration")),
3270 false /*aWarning*/,
3271 true /*aLogIt*/);
3272
3273 default:
3274 AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3275 }
3276
3277 /* Determine the base path for the device instance. */
3278 PCFGMNODE pCtlInst;
3279 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice,
3280 uInstance);
3281 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
3282
3283 int rc = VINF_SUCCESS;
3284 int rcRet = VINF_SUCCESS;
3285
3286 rcRet = pConsole->configMediumAttachment(pCtlInst,
3287 pcszDevice,
3288 uInstance,
3289 enmBus,
3290 fUseHostIOCache,
3291 false /* fSetupMerge */,
3292 0 /* uMergeSource */,
3293 0 /* uMergeTarget */,
3294 aMediumAtt,
3295 pConsole->mMachineState,
3296 NULL /* phrc */,
3297 true /* fAttachDetach */,
3298 fForce /* fForceUnmount */,
3299 pVM,
3300 NULL /* paLedDevType */);
3301 /** @todo this dumps everything attached to this device instance, which
3302 * is more than necessary. Dumping the changed LUN would be enough. */
3303 CFGMR3Dump(pCtlInst);
3304
3305 /*
3306 * Resume the VM if necessary.
3307 */
3308 if (fResume)
3309 {
3310 LogFlowFunc(("Resuming the VM...\n"));
3311 /* disable the callback to prevent Console-level state change */
3312 pConsole->mVMStateChangeCallbackDisabled = true;
3313 rc = VMR3Resume(pVM);
3314 pConsole->mVMStateChangeCallbackDisabled = false;
3315 AssertRC(rc);
3316 if (RT_FAILURE(rc))
3317 {
3318 /* too bad, we failed. try to sync the console state with the VMM state */
3319 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
3320 }
3321 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3322 // error (if any) will be hidden from the caller. For proper reporting
3323 // of such multiple errors to the caller we need to enhance the
3324 // IVirtualBoxError interface. For now, give the first error the higher
3325 // priority.
3326 if (RT_SUCCESS(rcRet))
3327 rcRet = rc;
3328 }
3329
3330 LogFlowFunc(("Returning %Rrc\n", rcRet));
3331 return rcRet;
3332}
3333
3334
3335/**
3336 * Called by IInternalSessionControl::OnNetworkAdapterChange().
3337 *
3338 * @note Locks this object for writing.
3339 */
3340HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
3341{
3342 LogFlowThisFunc(("\n"));
3343
3344 AutoCaller autoCaller(this);
3345 AssertComRCReturnRC(autoCaller.rc());
3346
3347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 HRESULT rc = S_OK;
3350
3351 /* don't trigger network change if the VM isn't running */
3352 if (mpVM)
3353 {
3354 /* protect mpVM */
3355 AutoVMCaller autoVMCaller(this);
3356 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3357
3358 /* Get the properties we need from the adapter */
3359 BOOL fCableConnected, fTraceEnabled;
3360 rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
3361 AssertComRC(rc);
3362 if (SUCCEEDED(rc))
3363 {
3364 rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
3365 AssertComRC(rc);
3366 }
3367 if (SUCCEEDED(rc))
3368 {
3369 ULONG ulInstance;
3370 rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
3371 AssertComRC(rc);
3372 if (SUCCEEDED(rc))
3373 {
3374 /*
3375 * Find the pcnet instance, get the config interface and update
3376 * the link state.
3377 */
3378 NetworkAdapterType_T adapterType;
3379 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3380 AssertComRC(rc);
3381 const char *pszAdapterName = NULL;
3382 switch (adapterType)
3383 {
3384 case NetworkAdapterType_Am79C970A:
3385 case NetworkAdapterType_Am79C973:
3386 pszAdapterName = "pcnet";
3387 break;
3388#ifdef VBOX_WITH_E1000
3389 case NetworkAdapterType_I82540EM:
3390 case NetworkAdapterType_I82543GC:
3391 case NetworkAdapterType_I82545EM:
3392 pszAdapterName = "e1000";
3393 break;
3394#endif
3395#ifdef VBOX_WITH_VIRTIO
3396 case NetworkAdapterType_Virtio:
3397 pszAdapterName = "virtio-net";
3398 break;
3399#endif
3400 default:
3401 AssertFailed();
3402 pszAdapterName = "unknown";
3403 break;
3404 }
3405
3406 PPDMIBASE pBase;
3407 int vrc = PDMR3QueryDeviceLun(mpVM, pszAdapterName, ulInstance, 0, &pBase);
3408 ComAssertRC(vrc);
3409 if (RT_SUCCESS(vrc))
3410 {
3411 Assert(pBase);
3412 PPDMINETWORKCONFIG pINetCfg;
3413 pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
3414 if (pINetCfg)
3415 {
3416 Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
3417 fCableConnected));
3418 vrc = pINetCfg->pfnSetLinkState(pINetCfg,
3419 fCableConnected ? PDMNETWORKLINKSTATE_UP
3420 : PDMNETWORKLINKSTATE_DOWN);
3421 ComAssertRC(vrc);
3422 }
3423#ifdef VBOX_DYNAMIC_NET_ATTACH
3424 if (RT_SUCCESS(vrc) && changeAdapter)
3425 {
3426 VMSTATE enmVMState = VMR3GetState(mpVM);
3427 if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
3428 || enmVMState == VMSTATE_SUSPENDED)
3429 {
3430 if (fTraceEnabled && fCableConnected && pINetCfg)
3431 {
3432 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
3433 ComAssertRC(vrc);
3434 }
3435
3436 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3437
3438 if (fTraceEnabled && fCableConnected && pINetCfg)
3439 {
3440 vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
3441 ComAssertRC(vrc);
3442 }
3443 }
3444 }
3445#endif /* VBOX_DYNAMIC_NET_ATTACH */
3446 }
3447
3448 if (RT_FAILURE(vrc))
3449 rc = E_FAIL;
3450 }
3451 }
3452 }
3453
3454 /* notify console callbacks on success */
3455 if (SUCCEEDED(rc))
3456 CONSOLE_DO_CALLBACKS1(OnNetworkAdapterChanged, aNetworkAdapter);
3457
3458 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3459 return rc;
3460}
3461
3462
3463#ifdef VBOX_DYNAMIC_NET_ATTACH
3464/**
3465 * Process a network adaptor change.
3466 *
3467 * @returns COM status code.
3468 *
3469 * @param pszDevice The PDM device name.
3470 * @param uInstance The PDM device instance.
3471 * @param uLun The PDM LUN number of the drive.
3472 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3473 *
3474 * @note Locks this object for writing.
3475 */
3476HRESULT Console::doNetworkAdapterChange(const char *pszDevice,
3477 unsigned uInstance,
3478 unsigned uLun,
3479 INetworkAdapter *aNetworkAdapter)
3480{
3481 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3482 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3483
3484 AutoCaller autoCaller(this);
3485 AssertComRCReturnRC(autoCaller.rc());
3486
3487 /* We will need to release the write lock before calling EMT */
3488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3489
3490 /* protect mpVM */
3491 AutoVMCaller autoVMCaller(this);
3492 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3493
3494 /*
3495 * Call worker in EMT, that's faster and safer than doing everything
3496 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3497 * here to make requests from under the lock in order to serialize them.
3498 */
3499 PVMREQ pReq;
3500 int vrc = VMR3ReqCall(mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3501 (PFNRT) Console::changeNetworkAttachment, 5,
3502 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3503
3504 /* leave the lock before waiting for a result (EMT will call us back!) */
3505 alock.leave();
3506
3507 if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
3508 {
3509 vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
3510 AssertRC(vrc);
3511 if (RT_SUCCESS(vrc))
3512 vrc = pReq->iStatus;
3513 }
3514 VMR3ReqFree(pReq);
3515
3516 if (RT_SUCCESS(vrc))
3517 {
3518 LogFlowThisFunc(("Returns S_OK\n"));
3519 return S_OK;
3520 }
3521
3522 return setError(E_FAIL,
3523 tr("Could not change the network adaptor attachement type (%Rrc)"),
3524 vrc);
3525}
3526
3527
3528/**
3529 * Performs the Network Adaptor change in EMT.
3530 *
3531 * @returns VBox status code.
3532 *
3533 * @param pThis Pointer to the Console object.
3534 * @param pszDevice The PDM device name.
3535 * @param uInstance The PDM device instance.
3536 * @param uLun The PDM LUN number of the drive.
3537 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3538 *
3539 * @thread EMT
3540 * @note Locks the Console object for writing.
3541 */
3542DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
3543 const char *pszDevice,
3544 unsigned uInstance,
3545 unsigned uLun,
3546 INetworkAdapter *aNetworkAdapter)
3547{
3548 LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3549 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3550
3551 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3552
3553 AssertMsg( ( !strcmp(pszDevice, "pcnet")
3554 || !strcmp(pszDevice, "e1000")
3555 || !strcmp(pszDevice, "virtio-net"))
3556 && (uLun == 0)
3557 && (uInstance < SchemaDefs::NetworkAdapterCount),
3558 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3559 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3560
3561 AutoCaller autoCaller(pThis);
3562 AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
3563
3564 /* protect mpVM */
3565 AutoVMCaller autoVMCaller(pThis);
3566 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3567
3568 PVM pVM = pThis->mpVM;
3569
3570 /*
3571 * Suspend the VM first.
3572 *
3573 * The VM must not be running since it might have pending I/O to
3574 * the drive which is being changed.
3575 */
3576 bool fResume;
3577 VMSTATE enmVMState = VMR3GetState(pVM);
3578 switch (enmVMState)
3579 {
3580 case VMSTATE_RESETTING:
3581 case VMSTATE_RUNNING:
3582 {
3583 LogFlowFunc(("Suspending the VM...\n"));
3584 /* disable the callback to prevent Console-level state change */
3585 pThis->mVMStateChangeCallbackDisabled = true;
3586 int rc = VMR3Suspend(pVM);
3587 pThis->mVMStateChangeCallbackDisabled = false;
3588 AssertRCReturn(rc, rc);
3589 fResume = true;
3590 break;
3591 }
3592
3593 case VMSTATE_SUSPENDED:
3594 case VMSTATE_CREATED:
3595 case VMSTATE_OFF:
3596 fResume = false;
3597 break;
3598
3599 default:
3600 AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3601 }
3602
3603 int rc = VINF_SUCCESS;
3604 int rcRet = VINF_SUCCESS;
3605
3606 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3607 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3608 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
3609 AssertRelease(pInst);
3610
3611 rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
3612
3613 /*
3614 * Resume the VM if necessary.
3615 */
3616 if (fResume)
3617 {
3618 LogFlowFunc(("Resuming the VM...\n"));
3619 /* disable the callback to prevent Console-level state change */
3620 pThis->mVMStateChangeCallbackDisabled = true;
3621 rc = VMR3Resume(pVM);
3622 pThis->mVMStateChangeCallbackDisabled = false;
3623 AssertRC(rc);
3624 if (RT_FAILURE(rc))
3625 {
3626 /* too bad, we failed. try to sync the console state with the VMM state */
3627 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3628 }
3629 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3630 // error (if any) will be hidden from the caller. For proper reporting
3631 // of such multiple errors to the caller we need to enhance the
3632 // IVirtualBoxError interface. For now, give the first error the higher
3633 // priority.
3634 if (RT_SUCCESS(rcRet))
3635 rcRet = rc;
3636 }
3637
3638 LogFlowFunc(("Returning %Rrc\n", rcRet));
3639 return rcRet;
3640}
3641#endif /* VBOX_DYNAMIC_NET_ATTACH */
3642
3643
3644/**
3645 * Called by IInternalSessionControl::OnSerialPortChange().
3646 *
3647 * @note Locks this object for writing.
3648 */
3649HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
3650{
3651 LogFlowThisFunc(("\n"));
3652
3653 AutoCaller autoCaller(this);
3654 AssertComRCReturnRC(autoCaller.rc());
3655
3656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3657
3658 HRESULT rc = S_OK;
3659
3660 /* don't trigger serial port change if the VM isn't running */
3661 if (mpVM)
3662 {
3663 /* protect mpVM */
3664 AutoVMCaller autoVMCaller(this);
3665 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3666
3667 /* nothing to do so far */
3668 }
3669
3670 /* notify console callbacks on success */
3671 if (SUCCEEDED(rc))
3672 CONSOLE_DO_CALLBACKS1(OnSerialPortChanged, aSerialPort);
3673
3674 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3675 return rc;
3676}
3677
3678/**
3679 * Called by IInternalSessionControl::OnParallelPortChange().
3680 *
3681 * @note Locks this object for writing.
3682 */
3683HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
3684{
3685 LogFlowThisFunc(("\n"));
3686
3687 AutoCaller autoCaller(this);
3688 AssertComRCReturnRC(autoCaller.rc());
3689
3690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3691
3692 HRESULT rc = S_OK;
3693
3694 /* don't trigger parallel port change if the VM isn't running */
3695 if (mpVM)
3696 {
3697 /* protect mpVM */
3698 AutoVMCaller autoVMCaller(this);
3699 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3700
3701 /* nothing to do so far */
3702 }
3703
3704 /* notify console callbacks on success */
3705 if (SUCCEEDED(rc))
3706 CONSOLE_DO_CALLBACKS1(OnParallelPortChanged, aParallelPort);
3707
3708 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3709 return rc;
3710}
3711
3712/**
3713 * Called by IInternalSessionControl::OnStorageControllerChange().
3714 *
3715 * @note Locks this object for writing.
3716 */
3717HRESULT Console::onStorageControllerChange()
3718{
3719 LogFlowThisFunc(("\n"));
3720
3721 AutoCaller autoCaller(this);
3722 AssertComRCReturnRC(autoCaller.rc());
3723
3724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3725
3726 HRESULT rc = S_OK;
3727
3728 /* don't trigger storage controller change if the VM isn't running */
3729 if (mpVM)
3730 {
3731 /* protect mpVM */
3732 AutoVMCaller autoVMCaller(this);
3733 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3734
3735 /* nothing to do so far */
3736 }
3737
3738 /* notify console callbacks on success */
3739 if (SUCCEEDED(rc))
3740 CONSOLE_DO_CALLBACKS0(OnStorageControllerChanged);
3741
3742 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3743 return rc;
3744}
3745
3746/**
3747 * Called by IInternalSessionControl::OnMediumChange().
3748 *
3749 * @note Locks this object for writing.
3750 */
3751HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
3752{
3753 LogFlowThisFunc(("\n"));
3754
3755 AutoCaller autoCaller(this);
3756 AssertComRCReturnRC(autoCaller.rc());
3757
3758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3759
3760 HRESULT rc = S_OK;
3761
3762 /* don't trigger medium change if the VM isn't running */
3763 if (mpVM)
3764 {
3765 /* protect mpVM */
3766 AutoVMCaller autoVMCaller(this);
3767 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3768
3769 rc = doMediumChange(aMediumAttachment, !!aForce);
3770 }
3771
3772 /* notify console callbacks on success */
3773 if (SUCCEEDED(rc))
3774 CONSOLE_DO_CALLBACKS1(OnMediumChanged, aMediumAttachment);
3775
3776 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3777 return rc;
3778}
3779
3780/**
3781 * Called by IInternalSessionControl::OnCPUChange().
3782 *
3783 * @note Locks this object for writing.
3784 */
3785HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
3786{
3787 LogFlowThisFunc(("\n"));
3788
3789 AutoCaller autoCaller(this);
3790 AssertComRCReturnRC(autoCaller.rc());
3791
3792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3793
3794 HRESULT rc = S_OK;
3795
3796 /* don't trigger CPU change if the VM isn't running */
3797 if (mpVM)
3798 {
3799 /* protect mpVM */
3800 AutoVMCaller autoVMCaller(this);
3801 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3802
3803 if (aRemove)
3804 rc = doCPURemove(aCPU);
3805 else
3806 rc = doCPUAdd(aCPU);
3807 }
3808
3809 /* notify console callbacks on success */
3810 if (SUCCEEDED(rc))
3811 CONSOLE_DO_CALLBACKS2(OnCPUChanged, aCPU, aRemove);
3812
3813 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3814 return rc;
3815}
3816
3817/**
3818 * Called by IInternalSessionControl::OnCPUPriorityChange().
3819 *
3820 * @note Locks this object for writing.
3821 */
3822HRESULT Console::onCPUPriorityChange(ULONG aCpuPriority)
3823{
3824 LogFlowThisFunc(("\n"));
3825
3826 AutoCaller autoCaller(this);
3827 AssertComRCReturnRC(autoCaller.rc());
3828
3829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3830
3831 HRESULT rc = S_OK;
3832
3833 /* don't trigger the CPU priority change if the VM isn't running */
3834 if (mpVM)
3835 {
3836 /* protect mpVM */
3837 AutoVMCaller autoVMCaller(this);
3838 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3839
3840 if ( mMachineState == MachineState_Running
3841 || mMachineState == MachineState_Teleporting
3842 || mMachineState == MachineState_LiveSnapshotting
3843 )
3844 {
3845 /* No need to call in the EMT thread. */
3846 rc = VMR3SetCpuPriority(mpVM, aCpuPriority);
3847 }
3848 else
3849 rc = setInvalidMachineStateError();
3850 }
3851
3852 /* notify console callbacks on success */
3853 if (SUCCEEDED(rc))
3854 CONSOLE_DO_CALLBACKS1(OnCPUPriorityChanged, aCpuPriority);
3855
3856 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3857 return rc;
3858}
3859
3860/**
3861 * Called by IInternalSessionControl::OnVRDPServerChange().
3862 *
3863 * @note Locks this object for writing.
3864 */
3865HRESULT Console::onVRDPServerChange(BOOL aRestart)
3866{
3867 AutoCaller autoCaller(this);
3868 AssertComRCReturnRC(autoCaller.rc());
3869
3870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3871
3872 HRESULT rc = S_OK;
3873
3874 if ( mVRDPServer
3875 && ( mMachineState == MachineState_Running
3876 || mMachineState == MachineState_Teleporting
3877 || mMachineState == MachineState_LiveSnapshotting
3878 )
3879 )
3880 {
3881 BOOL vrdpEnabled = FALSE;
3882
3883 rc = mVRDPServer->COMGETTER(Enabled)(&vrdpEnabled);
3884 ComAssertComRCRetRC(rc);
3885
3886 if (aRestart)
3887 {
3888 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
3889 alock.leave();
3890
3891 if (vrdpEnabled)
3892 {
3893 // If there was no VRDP server started the 'stop' will do nothing.
3894 // However if a server was started and this notification was called,
3895 // we have to restart the server.
3896 mConsoleVRDPServer->Stop();
3897
3898 if (RT_FAILURE(mConsoleVRDPServer->Launch()))
3899 {
3900 rc = E_FAIL;
3901 }
3902 else
3903 {
3904 mConsoleVRDPServer->EnableConnections();
3905 }
3906 }
3907 else
3908 {
3909 mConsoleVRDPServer->Stop();
3910 }
3911
3912 alock.enter();
3913 }
3914 }
3915
3916 /* notify console callbacks on success */
3917 if (SUCCEEDED(rc))
3918 CONSOLE_DO_CALLBACKS0(OnVRDPServerChanged);
3919
3920 return rc;
3921}
3922
3923/**
3924 * @note Locks this object for reading.
3925 */
3926void Console::onRemoteDisplayInfoChange()
3927{
3928 AutoCaller autoCaller(this);
3929 AssertComRCReturnVoid(autoCaller.rc());
3930
3931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3932
3933 CONSOLE_DO_CALLBACKS0(OnRemoteDisplayInfoChanged);
3934}
3935
3936
3937
3938/**
3939 * Called by IInternalSessionControl::OnUSBControllerChange().
3940 *
3941 * @note Locks this object for writing.
3942 */
3943HRESULT Console::onUSBControllerChange()
3944{
3945 LogFlowThisFunc(("\n"));
3946
3947 AutoCaller autoCaller(this);
3948 AssertComRCReturnRC(autoCaller.rc());
3949
3950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3951
3952 HRESULT rc = S_OK;
3953
3954 /* don't trigger USB controller change if the VM isn't running */
3955 if (mpVM)
3956 {
3957 /// @todo implement one day.
3958 // Anyway, if we want to query the machine's USB Controller we need
3959 // to cache it to mUSBController in #init() (similar to mDVDDrive).
3960 //
3961 // bird: While the VM supports hot-plugging, I doubt any guest can
3962 // handle it at this time... :-)
3963
3964 /* protect mpVM */
3965 AutoVMCaller autoVMCaller(this);
3966 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
3967
3968 /* nothing to do so far */
3969 }
3970
3971 /* notify console callbacks on success */
3972 if (SUCCEEDED(rc))
3973 CONSOLE_DO_CALLBACKS0(OnUSBControllerChanged);
3974
3975 return rc;
3976}
3977
3978/**
3979 * Called by IInternalSessionControl::OnSharedFolderChange().
3980 *
3981 * @note Locks this object for writing.
3982 */
3983HRESULT Console::onSharedFolderChange(BOOL aGlobal)
3984{
3985 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
3986
3987 AutoCaller autoCaller(this);
3988 AssertComRCReturnRC(autoCaller.rc());
3989
3990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3991
3992 HRESULT rc = fetchSharedFolders(aGlobal);
3993
3994 /* notify console callbacks on success */
3995 if (SUCCEEDED(rc))
3996 {
3997 if (aGlobal)
3998 CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Global);
3999 else
4000 CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Machine);
4001 }
4002
4003 return rc;
4004}
4005
4006/**
4007 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
4008 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
4009 * returns TRUE for a given remote USB device.
4010 *
4011 * @return S_OK if the device was attached to the VM.
4012 * @return failure if not attached.
4013 *
4014 * @param aDevice
4015 * The device in question.
4016 * @param aMaskedIfs
4017 * The interfaces to hide from the guest.
4018 *
4019 * @note Locks this object for writing.
4020 */
4021HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
4022{
4023#ifdef VBOX_WITH_USB
4024 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
4025
4026 AutoCaller autoCaller(this);
4027 ComAssertComRCRetRC(autoCaller.rc());
4028
4029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4030
4031 /* protect mpVM (we don't need error info, since it's a callback) */
4032 AutoVMCallerQuiet autoVMCaller(this);
4033 if (FAILED(autoVMCaller.rc()))
4034 {
4035 /* The VM may be no more operational when this message arrives
4036 * (e.g. it may be Saving or Stopping or just PoweredOff) --
4037 * autoVMCaller.rc() will return a failure in this case. */
4038 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
4039 mMachineState));
4040 return autoVMCaller.rc();
4041 }
4042
4043 if (aError != NULL)
4044 {
4045 /* notify callbacks about the error */
4046 onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
4047 return S_OK;
4048 }
4049
4050 /* Don't proceed unless there's at least one USB hub. */
4051 if (!PDMR3USBHasHub(mpVM))
4052 {
4053 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
4054 return E_FAIL;
4055 }
4056
4057 HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
4058 if (FAILED(rc))
4059 {
4060 /* take the current error info */
4061 com::ErrorInfoKeeper eik;
4062 /* the error must be a VirtualBoxErrorInfo instance */
4063 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
4064 Assert(!error.isNull());
4065 if (!error.isNull())
4066 {
4067 /* notify callbacks about the error */
4068 onUSBDeviceStateChange(aDevice, true /* aAttached */, error);
4069 }
4070 }
4071
4072 return rc;
4073
4074#else /* !VBOX_WITH_USB */
4075 return E_FAIL;
4076#endif /* !VBOX_WITH_USB */
4077}
4078
4079/**
4080 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
4081 * processRemoteUSBDevices().
4082 *
4083 * @note Locks this object for writing.
4084 */
4085HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
4086 IVirtualBoxErrorInfo *aError)
4087{
4088#ifdef VBOX_WITH_USB
4089 Guid Uuid(aId);
4090 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
4091
4092 AutoCaller autoCaller(this);
4093 AssertComRCReturnRC(autoCaller.rc());
4094
4095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4096
4097 /* Find the device. */
4098 ComObjPtr<OUSBDevice> device;
4099 USBDeviceList::iterator it = mUSBDevices.begin();
4100 while (it != mUSBDevices.end())
4101 {
4102 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
4103 if ((*it)->id() == Uuid)
4104 {
4105 device = *it;
4106 break;
4107 }
4108 ++ it;
4109 }
4110
4111
4112 if (device.isNull())
4113 {
4114 LogFlowThisFunc(("USB device not found.\n"));
4115
4116 /* The VM may be no more operational when this message arrives
4117 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
4118 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
4119 * failure in this case. */
4120
4121 AutoVMCallerQuiet autoVMCaller(this);
4122 if (FAILED(autoVMCaller.rc()))
4123 {
4124 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
4125 mMachineState));
4126 return autoVMCaller.rc();
4127 }
4128
4129 /* the device must be in the list otherwise */
4130 AssertFailedReturn(E_FAIL);
4131 }
4132
4133 if (aError != NULL)
4134 {
4135 /* notify callback about an error */
4136 onUSBDeviceStateChange(device, false /* aAttached */, aError);
4137 return S_OK;
4138 }
4139
4140 HRESULT rc = detachUSBDevice(it);
4141
4142 if (FAILED(rc))
4143 {
4144 /* take the current error info */
4145 com::ErrorInfoKeeper eik;
4146 /* the error must be a VirtualBoxErrorInfo instance */
4147 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
4148 Assert(!error.isNull());
4149 if (!error.isNull())
4150 {
4151 /* notify callbacks about the error */
4152 onUSBDeviceStateChange(device, false /* aAttached */, error);
4153 }
4154 }
4155
4156 return rc;
4157
4158#else /* !VBOX_WITH_USB */
4159 return E_FAIL;
4160#endif /* !VBOX_WITH_USB */
4161}
4162
4163/**
4164 * @note Temporarily locks this object for writing.
4165 */
4166HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
4167 ULONG64 *aTimestamp, BSTR *aFlags)
4168{
4169#ifndef VBOX_WITH_GUEST_PROPS
4170 ReturnComNotImplemented();
4171#else /* VBOX_WITH_GUEST_PROPS */
4172 if (!VALID_PTR(aName))
4173 return E_INVALIDARG;
4174 if (!VALID_PTR(aValue))
4175 return E_POINTER;
4176 if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
4177 return E_POINTER;
4178 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4179 return E_POINTER;
4180
4181 AutoCaller autoCaller(this);
4182 AssertComRCReturnRC(autoCaller.rc());
4183
4184 /* protect mpVM (if not NULL) */
4185 AutoVMCallerWeak autoVMCaller(this);
4186 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4187
4188 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4189 * autoVMCaller, so there is no need to hold a lock of this */
4190
4191 HRESULT rc = E_UNEXPECTED;
4192 using namespace guestProp;
4193
4194 try
4195 {
4196 VBOXHGCMSVCPARM parm[4];
4197 Utf8Str Utf8Name = aName;
4198 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4199
4200 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4201 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4202 /* The + 1 is the null terminator */
4203 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4204 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4205 parm[1].u.pointer.addr = pszBuffer;
4206 parm[1].u.pointer.size = sizeof(pszBuffer);
4207 int vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
4208 4, &parm[0]);
4209 /* The returned string should never be able to be greater than our buffer */
4210 AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
4211 AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4212 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4213 {
4214 rc = S_OK;
4215 if (vrc != VERR_NOT_FOUND)
4216 {
4217 Utf8Str strBuffer(pszBuffer);
4218 strBuffer.cloneTo(aValue);
4219
4220 *aTimestamp = parm[2].u.uint64;
4221
4222 size_t iFlags = strBuffer.length() + 1;
4223 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4224 }
4225 else
4226 aValue = NULL;
4227 }
4228 else
4229 rc = setError(E_UNEXPECTED,
4230 tr("The service call failed with the error %Rrc"),
4231 vrc);
4232 }
4233 catch(std::bad_alloc & /*e*/)
4234 {
4235 rc = E_OUTOFMEMORY;
4236 }
4237 return rc;
4238#endif /* VBOX_WITH_GUEST_PROPS */
4239}
4240
4241/**
4242 * @note Temporarily locks this object for writing.
4243 */
4244HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4245{
4246#ifndef VBOX_WITH_GUEST_PROPS
4247 ReturnComNotImplemented();
4248#else /* VBOX_WITH_GUEST_PROPS */
4249 if (!VALID_PTR(aName))
4250 return E_INVALIDARG;
4251 if ((aValue != NULL) && !VALID_PTR(aValue))
4252 return E_INVALIDARG;
4253 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4254 return E_INVALIDARG;
4255
4256 AutoCaller autoCaller(this);
4257 AssertComRCReturnRC(autoCaller.rc());
4258
4259 /* protect mpVM (if not NULL) */
4260 AutoVMCallerWeak autoVMCaller(this);
4261 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4262
4263 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4264 * autoVMCaller, so there is no need to hold a lock of this */
4265
4266 HRESULT rc = E_UNEXPECTED;
4267 using namespace guestProp;
4268
4269 VBOXHGCMSVCPARM parm[3];
4270 Utf8Str Utf8Name = aName;
4271 int vrc = VINF_SUCCESS;
4272
4273 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4274 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4275 /* The + 1 is the null terminator */
4276 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4277 Utf8Str Utf8Value = aValue;
4278 if (aValue != NULL)
4279 {
4280 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4281 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4282 /* The + 1 is the null terminator */
4283 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4284 }
4285 Utf8Str Utf8Flags = aFlags;
4286 if (aFlags != NULL)
4287 {
4288 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4289 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4290 /* The + 1 is the null terminator */
4291 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4292 }
4293 if ((aValue != NULL) && (aFlags != NULL))
4294 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
4295 3, &parm[0]);
4296 else if (aValue != NULL)
4297 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4298 2, &parm[0]);
4299 else
4300 vrc = mVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
4301 1, &parm[0]);
4302 if (RT_SUCCESS(vrc))
4303 rc = S_OK;
4304 else
4305 rc = setError(E_UNEXPECTED,
4306 tr("The service call failed with the error %Rrc"),
4307 vrc);
4308 return rc;
4309#endif /* VBOX_WITH_GUEST_PROPS */
4310}
4311
4312
4313/**
4314 * @note Temporarily locks this object for writing.
4315 */
4316HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
4317 ComSafeArrayOut(BSTR, aNames),
4318 ComSafeArrayOut(BSTR, aValues),
4319 ComSafeArrayOut(ULONG64, aTimestamps),
4320 ComSafeArrayOut(BSTR, aFlags))
4321{
4322#ifndef VBOX_WITH_GUEST_PROPS
4323 ReturnComNotImplemented();
4324#else /* VBOX_WITH_GUEST_PROPS */
4325 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4326 return E_POINTER;
4327 if (ComSafeArrayOutIsNull(aNames))
4328 return E_POINTER;
4329 if (ComSafeArrayOutIsNull(aValues))
4330 return E_POINTER;
4331 if (ComSafeArrayOutIsNull(aTimestamps))
4332 return E_POINTER;
4333 if (ComSafeArrayOutIsNull(aFlags))
4334 return E_POINTER;
4335
4336 AutoCaller autoCaller(this);
4337 AssertComRCReturnRC(autoCaller.rc());
4338
4339 /* protect mpVM (if not NULL) */
4340 AutoVMCallerWeak autoVMCaller(this);
4341 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
4342
4343 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4344 * autoVMCaller, so there is no need to hold a lock of this */
4345
4346 return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
4347 ComSafeArrayOutArg(aValues),
4348 ComSafeArrayOutArg(aTimestamps),
4349 ComSafeArrayOutArg(aFlags));
4350#endif /* VBOX_WITH_GUEST_PROPS */
4351}
4352
4353
4354/*
4355 * Internal: helper function for connecting progress reporting
4356 */
4357static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
4358{
4359 HRESULT rc = S_OK;
4360 IProgress *pProgress = static_cast<IProgress *>(pvUser);
4361 if (pProgress)
4362 rc = pProgress->SetCurrentOperationProgress(uPercentage);
4363 return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
4364}
4365
4366/**
4367 * @note Temporarily locks this object for writing.
4368 */
4369HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
4370 ULONG aSourceIdx, ULONG aTargetIdx,
4371 IMedium *aSource, IMedium *aTarget,
4372 BOOL aMergeForward,
4373 IMedium *aParentForTarget,
4374 ComSafeArrayIn(IMedium *, aChildrenToReparent),
4375 IProgress *aProgress)
4376{
4377 AutoCaller autoCaller(this);
4378 AssertComRCReturnRC(autoCaller.rc());
4379
4380 HRESULT rc = S_OK;
4381 int vrc = VINF_SUCCESS;
4382 PVM pVM = mpVM;
4383
4384 /* We will need to release the lock before doing the actual merge */
4385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4386
4387 /* paranoia - we don't want merges to happen while teleporting etc. */
4388 switch (mMachineState)
4389 {
4390 case MachineState_DeletingSnapshotOnline:
4391 case MachineState_DeletingSnapshotPaused:
4392 break;
4393
4394 default:
4395 return setInvalidMachineStateError();
4396 }
4397
4398 SafeIfaceArray<IStorageController> ctrls;
4399 rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
4400 AssertComRC(rc);
4401 LONG lDev;
4402 rc = aMediumAttachment->COMGETTER(Device)(&lDev);
4403 AssertComRC(rc);
4404 LONG lPort;
4405 rc = aMediumAttachment->COMGETTER(Port)(&lPort);
4406 AssertComRC(rc);
4407 IMedium *pMedium;
4408 rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
4409 AssertComRC(rc);
4410 Bstr mediumLocation;
4411 if (pMedium)
4412 {
4413 rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
4414 AssertComRC(rc);
4415 }
4416
4417 Bstr attCtrlName;
4418 rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
4419 AssertComRC(rc);
4420 ComPtr<IStorageController> ctrl;
4421 for (size_t i = 0; i < ctrls.size(); ++i)
4422 {
4423 Bstr ctrlName;
4424 rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
4425 AssertComRC(rc);
4426 if (attCtrlName == ctrlName)
4427 {
4428 ctrl = ctrls[i];
4429 break;
4430 }
4431 }
4432 if (ctrl.isNull())
4433 {
4434 return setError(E_FAIL,
4435 tr("Could not find storage controller '%ls'"), attCtrlName.raw());
4436 }
4437 StorageControllerType_T enmCtrlType;
4438 rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
4439 AssertComRC(rc);
4440 const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
4441
4442 StorageBus_T enmBus;
4443 rc = ctrl->COMGETTER(Bus)(&enmBus);
4444 AssertComRC(rc);
4445 ULONG uInstance;
4446 rc = ctrl->COMGETTER(Instance)(&uInstance);
4447 AssertComRC(rc);
4448 BOOL fUseHostIOCache;
4449 rc = ctrl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
4450 AssertComRC(rc);
4451
4452 unsigned uLUN;
4453 rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
4454 AssertComRCReturnRC(rc);
4455
4456 alock.release();
4457
4458 /* Pause the VM, as it might have pending IO on this drive */
4459 VMSTATE enmVMState = VMR3GetState(pVM);
4460 if (mMachineState == MachineState_DeletingSnapshotOnline)
4461 {
4462 LogFlowFunc(("Suspending the VM...\n"));
4463 /* disable the callback to prevent Console-level state change */
4464 mVMStateChangeCallbackDisabled = true;
4465 int vrc2 = VMR3Suspend(pVM);
4466 mVMStateChangeCallbackDisabled = false;
4467 AssertRCReturn(vrc2, E_FAIL);
4468 }
4469
4470 vrc = VMR3ReqCallWait(pVM,
4471 VMCPUID_ANY,
4472 (PFNRT)reconfigureMediumAttachment,
4473 12,
4474 this,
4475 pVM,
4476 pcszDevice,
4477 uInstance,
4478 enmBus,
4479 fUseHostIOCache,
4480 true /* fSetupMerge */,
4481 aSourceIdx,
4482 aTargetIdx,
4483 aMediumAttachment,
4484 mMachineState,
4485 &rc);
4486 /* error handling is after resuming the VM */
4487
4488 if (mMachineState == MachineState_DeletingSnapshotOnline)
4489 {
4490 LogFlowFunc(("Resuming the VM...\n"));
4491 /* disable the callback to prevent Console-level state change */
4492 mVMStateChangeCallbackDisabled = true;
4493 int vrc2 = VMR3Resume(pVM);
4494 mVMStateChangeCallbackDisabled = false;
4495 AssertRC(vrc2);
4496 if (RT_FAILURE(vrc2))
4497 {
4498 /* too bad, we failed. try to sync the console state with the VMM state */
4499 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4500 }
4501 }
4502
4503 if (RT_FAILURE(vrc))
4504 return setError(E_FAIL, tr("%Rrc"), vrc);
4505 if (FAILED(rc))
4506 return rc;
4507
4508 PPDMIBASE pIBase = NULL;
4509 PPDMIMEDIA pIMedium = NULL;
4510 vrc = PDMR3QueryDriverOnLun(pVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
4511 if (RT_SUCCESS(vrc))
4512 {
4513 if (pIBase)
4514 {
4515 pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
4516 if (!pIMedium)
4517 return setError(E_FAIL, tr("could not query medium interface of controller"));
4518 }
4519 else
4520 return setError(E_FAIL, tr("could not query base interface of controller"));
4521 }
4522
4523 /* Finally trigger the merge. */
4524 vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
4525 if (RT_FAILURE(vrc))
4526 return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
4527
4528 /* Pause the VM, as it might have pending IO on this drive */
4529 enmVMState = VMR3GetState(pVM);
4530 if (mMachineState == MachineState_DeletingSnapshotOnline)
4531 {
4532 LogFlowFunc(("Suspending the VM...\n"));
4533 /* disable the callback to prevent Console-level state change */
4534 mVMStateChangeCallbackDisabled = true;
4535 int vrc2 = VMR3Suspend(pVM);
4536 mVMStateChangeCallbackDisabled = false;
4537 AssertRCReturn(vrc2, E_FAIL);
4538 }
4539
4540 /* Update medium chain and state now, so that the VM can continue. */
4541 rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
4542 aMergeForward, aParentForTarget,
4543 ComSafeArrayInArg(aChildrenToReparent));
4544
4545 vrc = VMR3ReqCallWait(pVM,
4546 VMCPUID_ANY,
4547 (PFNRT)reconfigureMediumAttachment,
4548 12,
4549 this,
4550 pVM,
4551 pcszDevice,
4552 uInstance,
4553 enmBus,
4554 fUseHostIOCache,
4555 false /* fSetupMerge */,
4556 0 /* uMergeSource */,
4557 0 /* uMergeTarget */,
4558 aMediumAttachment,
4559 mMachineState,
4560 &rc);
4561 /* error handling is after resuming the VM */
4562
4563 if (mMachineState == MachineState_DeletingSnapshotOnline)
4564 {
4565 LogFlowFunc(("Resuming the VM...\n"));
4566 /* disable the callback to prevent Console-level state change */
4567 mVMStateChangeCallbackDisabled = true;
4568 int vrc2 = VMR3Resume(pVM);
4569 mVMStateChangeCallbackDisabled = false;
4570 AssertRC(vrc2);
4571 if (RT_FAILURE(vrc2))
4572 {
4573 /* too bad, we failed. try to sync the console state with the VMM state */
4574 vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
4575 }
4576 }
4577
4578 if (RT_FAILURE(vrc))
4579 return setError(E_FAIL, tr("%Rrc"), vrc);
4580 if (FAILED(rc))
4581 return rc;
4582
4583 return rc;
4584}
4585
4586
4587/**
4588 * Gets called by Session::UpdateMachineState()
4589 * (IInternalSessionControl::updateMachineState()).
4590 *
4591 * Must be called only in certain cases (see the implementation).
4592 *
4593 * @note Locks this object for writing.
4594 */
4595HRESULT Console::updateMachineState(MachineState_T aMachineState)
4596{
4597 AutoCaller autoCaller(this);
4598 AssertComRCReturnRC(autoCaller.rc());
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 AssertReturn( mMachineState == MachineState_Saving
4603 || mMachineState == MachineState_LiveSnapshotting
4604 || mMachineState == MachineState_RestoringSnapshot
4605 || mMachineState == MachineState_DeletingSnapshot
4606 || mMachineState == MachineState_DeletingSnapshotOnline
4607 || mMachineState == MachineState_DeletingSnapshotPaused
4608 , E_FAIL);
4609
4610 return setMachineStateLocally(aMachineState);
4611}
4612
4613/**
4614 * @note Locks this object for writing.
4615 */
4616void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4617 uint32_t xHot, uint32_t yHot,
4618 uint32_t width, uint32_t height,
4619 ComSafeArrayIn(BYTE,pShape))
4620{
4621#if 0
4622 LogFlowThisFuncEnter();
4623 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
4624 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4625#endif
4626
4627 AutoCaller autoCaller(this);
4628 AssertComRCReturnVoid(autoCaller.rc());
4629
4630 /* We need a write lock because we alter the cached callback data */
4631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 /* Save the callback arguments */
4634 mCallbackData.mpsc.visible = fVisible;
4635 mCallbackData.mpsc.alpha = fAlpha;
4636 mCallbackData.mpsc.xHot = xHot;
4637 mCallbackData.mpsc.yHot = yHot;
4638 mCallbackData.mpsc.width = width;
4639 mCallbackData.mpsc.height = height;
4640
4641 /* start with not valid */
4642 bool wasValid = mCallbackData.mpsc.valid;
4643 mCallbackData.mpsc.valid = false;
4644
4645 com::SafeArray <BYTE> aShape(ComSafeArrayInArg (pShape));
4646 if (aShape.size() != 0)
4647 mCallbackData.mpsc.shape.initFrom(aShape);
4648 else
4649 mCallbackData.mpsc.shape.resize(0);
4650 mCallbackData.mpsc.valid = true;
4651
4652 /**
4653 * Although looks stupid, this is result of fact that safearrays params in XPCOM
4654 * passed as separate pointer and length arguments.
4655 * @todo: better solution
4656 */
4657#ifdef RT_OS_WINDOWS
4658 CONSOLE_DO_CALLBACKS7(OnMousePointerShapeChanged, fVisible, fAlpha, xHot, yHot, width, height, pShape);
4659#else
4660 CONSOLE_DO_CALLBACKS8(OnMousePointerShapeChanged, fVisible, fAlpha, xHot, yHot, width, height, pShapeSize, pShape);
4661#endif
4662
4663#if 0
4664 LogFlowThisFuncLeave();
4665#endif
4666}
4667
4668/**
4669 * @note Locks this object for writing.
4670 */
4671void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
4672{
4673 LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
4674 supportsAbsolute, supportsRelative, needsHostCursor));
4675
4676 AutoCaller autoCaller(this);
4677 AssertComRCReturnVoid(autoCaller.rc());
4678
4679 /* We need a write lock because we alter the cached callback data */
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 /* save the callback arguments */
4683 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
4684 mCallbackData.mcc.supportsRelative = supportsRelative;
4685 mCallbackData.mcc.needsHostCursor = needsHostCursor;
4686 mCallbackData.mcc.valid = true;
4687
4688 CONSOLE_DO_CALLBACKS3(OnMouseCapabilityChanged, supportsAbsolute, supportsRelative, needsHostCursor);
4689}
4690
4691/**
4692 * @note Locks this object for reading.
4693 */
4694void Console::onStateChange(MachineState_T machineState)
4695{
4696 AutoCaller autoCaller(this);
4697 AssertComRCReturnVoid(autoCaller.rc());
4698
4699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4700 CONSOLE_DO_CALLBACKS1(OnStateChanged, machineState);
4701}
4702
4703/**
4704 * @note Locks this object for reading.
4705 */
4706void Console::onAdditionsStateChange()
4707{
4708 AutoCaller autoCaller(this);
4709 AssertComRCReturnVoid(autoCaller.rc());
4710
4711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4712 CONSOLE_DO_CALLBACKS0(OnAdditionsStateChanged);
4713}
4714
4715/**
4716 * @note Locks this object for reading.
4717 * This notification only is for reporting an incompatible
4718 * Guest Additions interface, *not* the Guest Additions version!
4719 *
4720 * The user will be notified inside the guest if new Guest
4721 * Additions are available (via VBoxTray/VBoxClient).
4722 */
4723void Console::onAdditionsOutdated()
4724{
4725 AutoCaller autoCaller(this);
4726 AssertComRCReturnVoid(autoCaller.rc());
4727
4728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4729}
4730
4731/**
4732 * @note Locks this object for writing.
4733 */
4734void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
4735{
4736 AutoCaller autoCaller(this);
4737 AssertComRCReturnVoid(autoCaller.rc());
4738
4739 /* We need a write lock because we alter the cached callback data */
4740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4741
4742 /* save the callback arguments */
4743 mCallbackData.klc.numLock = fNumLock;
4744 mCallbackData.klc.capsLock = fCapsLock;
4745 mCallbackData.klc.scrollLock = fScrollLock;
4746 mCallbackData.klc.valid = true;
4747
4748 CONSOLE_DO_CALLBACKS3(OnKeyboardLedsChanged, fNumLock, fCapsLock, fScrollLock);
4749}
4750
4751/**
4752 * @note Locks this object for reading.
4753 */
4754void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
4755 IVirtualBoxErrorInfo *aError)
4756{
4757 AutoCaller autoCaller(this);
4758 AssertComRCReturnVoid(autoCaller.rc());
4759
4760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4761 CONSOLE_DO_CALLBACKS3(OnUSBDeviceStateChanged, aDevice, aAttached, aError);
4762}
4763
4764/**
4765 * @note Locks this object for reading.
4766 */
4767void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
4768{
4769 AutoCaller autoCaller(this);
4770 AssertComRCReturnVoid(autoCaller.rc());
4771
4772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4773 CONSOLE_DO_CALLBACKS3(OnRuntimeError, aFatal, aErrorID, aMessage);
4774}
4775
4776/**
4777 * @note Locks this object for reading.
4778 */
4779HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
4780{
4781 AssertReturn(aCanShow, E_POINTER);
4782 AssertReturn(aWinId, E_POINTER);
4783
4784 *aCanShow = FALSE;
4785 *aWinId = 0;
4786
4787 AutoCaller autoCaller(this);
4788 AssertComRCReturnRC(autoCaller.rc());
4789
4790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4791 VBoxEventDesc evDesc;
4792
4793 if (aCheck)
4794 {
4795 evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
4796 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
4797 //Assert(fDelivered);
4798 if (fDelivered)
4799 {
4800 ComPtr<IEvent> aEvent;
4801 evDesc.getEvent(aEvent.asOutParam());
4802 // bit clumsy
4803 ComPtr<ICanShowWindowEvent> aCanShowEvent = aEvent;
4804 if (aCanShowEvent)
4805 {
4806 BOOL fVetoed = FALSE;
4807 aCanShowEvent->IsVetoed(&fVetoed);
4808 *aCanShow = !fVetoed;
4809 }
4810 else
4811 {
4812 Assert(FALSE);
4813 *aCanShow = TRUE;
4814 }
4815 }
4816 else
4817 *aCanShow = TRUE;
4818 }
4819 else
4820 {
4821 evDesc.init(mEventSource, VBoxEventType_OnShowWindow, UINT64_C(0));
4822 BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
4823 //Assert(fDelivered);
4824 if (fDelivered)
4825 {
4826 ComPtr<IEvent> aEvent;
4827 evDesc.getEvent(aEvent.asOutParam());
4828 ComPtr<IShowWindowEvent> aShowEvent = aEvent;
4829 ULONG64 aEvWinId = 0;
4830 if (aShowEvent)
4831 {
4832 aShowEvent->COMGETTER(WinId)(&aEvWinId);
4833 if ((aEvWinId != 0) && (*aWinId == 0))
4834 *aWinId = aEvWinId;
4835 }
4836 else
4837 Assert(FALSE);
4838 }
4839 }
4840
4841 return S_OK;
4842}
4843
4844// private methods
4845////////////////////////////////////////////////////////////////////////////////
4846
4847/**
4848 * Increases the usage counter of the mpVM pointer. Guarantees that
4849 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
4850 * is called.
4851 *
4852 * If this method returns a failure, the caller is not allowed to use mpVM
4853 * and may return the failed result code to the upper level. This method sets
4854 * the extended error info on failure if \a aQuiet is false.
4855 *
4856 * Setting \a aQuiet to true is useful for methods that don't want to return
4857 * the failed result code to the caller when this method fails (e.g. need to
4858 * silently check for the mpVM availability).
4859 *
4860 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
4861 * returned instead of asserting. Having it false is intended as a sanity check
4862 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
4863 *
4864 * @param aQuiet true to suppress setting error info
4865 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
4866 * (otherwise this method will assert if mpVM is NULL)
4867 *
4868 * @note Locks this object for writing.
4869 */
4870HRESULT Console::addVMCaller(bool aQuiet /* = false */,
4871 bool aAllowNullVM /* = false */)
4872{
4873 AutoCaller autoCaller(this);
4874 AssertComRCReturnRC(autoCaller.rc());
4875
4876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4877
4878 if (mVMDestroying)
4879 {
4880 /* powerDown() is waiting for all callers to finish */
4881 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
4882 tr("The virtual machine is being powered down"));
4883 }
4884
4885 if (mpVM == NULL)
4886 {
4887 Assert(aAllowNullVM == true);
4888
4889 /* The machine is not powered up */
4890 return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
4891 tr("The virtual machine is not powered up"));
4892 }
4893
4894 ++ mVMCallers;
4895
4896 return S_OK;
4897}
4898
4899/**
4900 * Decreases the usage counter of the mpVM pointer. Must always complete
4901 * the addVMCaller() call after the mpVM pointer is no more necessary.
4902 *
4903 * @note Locks this object for writing.
4904 */
4905void Console::releaseVMCaller()
4906{
4907 AutoCaller autoCaller(this);
4908 AssertComRCReturnVoid(autoCaller.rc());
4909
4910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4911
4912 AssertReturnVoid(mpVM != NULL);
4913
4914 Assert(mVMCallers > 0);
4915 --mVMCallers;
4916
4917 if (mVMCallers == 0 && mVMDestroying)
4918 {
4919 /* inform powerDown() there are no more callers */
4920 RTSemEventSignal(mVMZeroCallersSem);
4921 }
4922}
4923
4924/**
4925 * Initialize the release logging facility. In case something
4926 * goes wrong, there will be no release logging. Maybe in the future
4927 * we can add some logic to use different file names in this case.
4928 * Note that the logic must be in sync with Machine::DeleteSettings().
4929 */
4930HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
4931{
4932 HRESULT hrc = S_OK;
4933
4934 Bstr logFolder;
4935 hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
4936 if (FAILED(hrc)) return hrc;
4937
4938 Utf8Str logDir = logFolder;
4939
4940 /* make sure the Logs folder exists */
4941 Assert(logDir.length());
4942 if (!RTDirExists(logDir.c_str()))
4943 RTDirCreateFullPath(logDir.c_str(), 0777);
4944
4945 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
4946 logDir.raw(), RTPATH_DELIMITER);
4947 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
4948 logDir.raw(), RTPATH_DELIMITER);
4949
4950 /*
4951 * Age the old log files
4952 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
4953 * Overwrite target files in case they exist.
4954 */
4955 ComPtr<IVirtualBox> virtualBox;
4956 aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
4957 ComPtr<ISystemProperties> systemProperties;
4958 virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4959 ULONG cHistoryFiles = 3;
4960 systemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
4961 if (cHistoryFiles)
4962 {
4963 for (int i = cHistoryFiles-1; i >= 0; i--)
4964 {
4965 Utf8Str *files[] = { &logFile, &pngFile };
4966 Utf8Str oldName, newName;
4967
4968 for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
4969 {
4970 if (i > 0)
4971 oldName = Utf8StrFmt("%s.%d", files[j]->raw(), i);
4972 else
4973 oldName = *files[j];
4974 newName = Utf8StrFmt("%s.%d", files[j]->raw(), i + 1);
4975 /* If the old file doesn't exist, delete the new file (if it
4976 * exists) to provide correct rotation even if the sequence is
4977 * broken */
4978 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
4979 == VERR_FILE_NOT_FOUND)
4980 RTFileDelete(newName.c_str());
4981 }
4982 }
4983 }
4984
4985 PRTLOGGER loggerRelease;
4986 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
4987 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
4988#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
4989 fFlags |= RTLOGFLAGS_USECRLF;
4990#endif
4991 char szError[RTPATH_MAX + 128] = "";
4992 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
4993 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
4994 RTLOGDEST_FILE, szError, sizeof(szError), logFile.raw());
4995 if (RT_SUCCESS(vrc))
4996 {
4997 /* some introductory information */
4998 RTTIMESPEC timeSpec;
4999 char szTmp[256];
5000 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
5001 RTLogRelLogger(loggerRelease, 0, ~0U,
5002 "VirtualBox %s r%u %s (%s %s) release log\n"
5003#ifdef VBOX_BLEEDING_EDGE
5004 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
5005#endif
5006 "Log opened %s\n",
5007 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
5008 __DATE__, __TIME__, szTmp);
5009
5010 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
5011 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5012 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
5013 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
5014 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5015 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
5016 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
5017 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5018 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
5019 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
5020 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5021 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
5022 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
5023 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5024 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp);
5025 vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
5026 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
5027 RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp);
5028
5029 ComPtr<IHost> host;
5030 virtualBox->COMGETTER(Host)(host.asOutParam());
5031 ULONG cMbHostRam = 0;
5032 ULONG cMbHostRamAvail = 0;
5033 host->COMGETTER(MemorySize)(&cMbHostRam);
5034 host->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
5035 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
5036 cMbHostRam, cMbHostRamAvail);
5037
5038 /* the package type is interesting for Linux distributions */
5039 char szExecName[RTPATH_MAX];
5040 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
5041 RTLogRelLogger(loggerRelease, 0, ~0U,
5042 "Executable: %s\n"
5043 "Process ID: %u\n"
5044 "Package type: %s"
5045#ifdef VBOX_OSE
5046 " (OSE)"
5047#endif
5048 "\n",
5049 pszExecName ? pszExecName : "unknown",
5050 RTProcSelf(),
5051 VBOX_PACKAGE_STRING);
5052
5053 /* register this logger as the release logger */
5054 RTLogRelSetDefaultInstance(loggerRelease);
5055 hrc = S_OK;
5056
5057 /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
5058 RTLogFlush(loggerRelease);
5059 }
5060 else
5061 hrc = setError(E_FAIL,
5062 tr("Failed to open release log (%s, %Rrc)"),
5063 szError, vrc);
5064
5065 /* If we've made any directory changes, flush the directory to increase
5066 the likelyhood that the log file will be usable after a system panic.
5067
5068 Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
5069 is missing. Just don't have too high hopes for this to help. */
5070 if (SUCCEEDED(hrc) || cHistoryFiles)
5071 RTDirFlush(logDir.c_str());
5072
5073 return hrc;
5074}
5075
5076/**
5077 * Common worker for PowerUp and PowerUpPaused.
5078 *
5079 * @returns COM status code.
5080 *
5081 * @param aProgress Where to return the progress object.
5082 * @param aPaused true if PowerUpPaused called.
5083 *
5084 * @todo move down to powerDown();
5085 */
5086HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
5087{
5088 if (aProgress == NULL)
5089 return E_POINTER;
5090
5091 LogFlowThisFuncEnter();
5092 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5093
5094 AutoCaller autoCaller(this);
5095 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5096
5097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5098
5099 if (Global::IsOnlineOrTransient(mMachineState))
5100 return setError(VBOX_E_INVALID_VM_STATE,
5101 tr("The virtual machine is already running or busy (machine state: %s)"),
5102 Global::stringifyMachineState(mMachineState));
5103
5104 HRESULT rc = S_OK;
5105
5106 /* the network cards will undergo a quick consistency check */
5107 for (ULONG slot = 0;
5108 slot < SchemaDefs::NetworkAdapterCount;
5109 ++slot)
5110 {
5111 ComPtr<INetworkAdapter> adapter;
5112 mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
5113 BOOL enabled = FALSE;
5114 adapter->COMGETTER(Enabled)(&enabled);
5115 if (!enabled)
5116 continue;
5117
5118 NetworkAttachmentType_T netattach;
5119 adapter->COMGETTER(AttachmentType)(&netattach);
5120 switch (netattach)
5121 {
5122 case NetworkAttachmentType_Bridged:
5123 {
5124#ifdef RT_OS_WINDOWS
5125 /* a valid host interface must have been set */
5126 Bstr hostif;
5127 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
5128 if (!hostif)
5129 {
5130 return setError(VBOX_E_HOST_ERROR,
5131 tr("VM cannot start because host interface networking requires a host interface name to be set"));
5132 }
5133 ComPtr<IVirtualBox> virtualBox;
5134 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
5135 ComPtr<IHost> host;
5136 virtualBox->COMGETTER(Host)(host.asOutParam());
5137 ComPtr<IHostNetworkInterface> hostInterface;
5138 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
5139 {
5140 return setError(VBOX_E_HOST_ERROR,
5141 tr("VM cannot start because the host interface '%ls' does not exist"),
5142 hostif.raw());
5143 }
5144#endif /* RT_OS_WINDOWS */
5145 break;
5146 }
5147 default:
5148 break;
5149 }
5150 }
5151
5152 /* Read console data stored in the saved state file (if not yet done) */
5153 rc = loadDataFromSavedState();
5154 if (FAILED(rc)) return rc;
5155
5156 /* Check all types of shared folders and compose a single list */
5157 SharedFolderDataMap sharedFolders;
5158 {
5159 /* first, insert global folders */
5160 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
5161 it != mGlobalSharedFolders.end(); ++ it)
5162 sharedFolders[it->first] = it->second;
5163
5164 /* second, insert machine folders */
5165 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
5166 it != mMachineSharedFolders.end(); ++ it)
5167 sharedFolders[it->first] = it->second;
5168
5169 /* third, insert console folders */
5170 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
5171 it != mSharedFolders.end(); ++ it)
5172 sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(),
5173 it->second->isWritable(),
5174 it->second->isAutoMounted());
5175 }
5176
5177 Bstr savedStateFile;
5178
5179 /*
5180 * Saved VMs will have to prove that their saved states seem kosher.
5181 */
5182 if (mMachineState == MachineState_Saved)
5183 {
5184 rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
5185 if (FAILED(rc)) return rc;
5186 ComAssertRet(!!savedStateFile, E_FAIL);
5187 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
5188 if (RT_FAILURE(vrc))
5189 return setError(VBOX_E_FILE_ERROR,
5190 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
5191 savedStateFile.raw(), vrc);
5192 }
5193
5194 /* test and clear the TeleporterEnabled property */
5195 BOOL fTeleporterEnabled;
5196 rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
5197 if (FAILED(rc)) return rc;
5198#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
5199 if (fTeleporterEnabled)
5200 {
5201 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
5202 if (FAILED(rc)) return rc;
5203 }
5204#endif
5205
5206 /* create a progress object to track progress of this operation */
5207 ComObjPtr<Progress> powerupProgress;
5208 powerupProgress.createObject();
5209 Bstr progressDesc;
5210 if (mMachineState == MachineState_Saved)
5211 progressDesc = tr("Restoring virtual machine");
5212 else if (fTeleporterEnabled)
5213 progressDesc = tr("Teleporting virtual machine");
5214 else
5215 progressDesc = tr("Starting virtual machine");
5216 if (mMachineState == MachineState_Saved || !fTeleporterEnabled)
5217 rc = powerupProgress->init(static_cast<IConsole *>(this),
5218 progressDesc,
5219 FALSE /* aCancelable */);
5220 else
5221 rc = powerupProgress->init(static_cast<IConsole *>(this),
5222 progressDesc,
5223 TRUE /* aCancelable */,
5224 3 /* cOperations */,
5225 10 /* ulTotalOperationsWeight */,
5226 Bstr(tr("Teleporting virtual machine")),
5227 1 /* ulFirstOperationWeight */,
5228 NULL);
5229 if (FAILED(rc))
5230 return rc;
5231
5232 /* Tell VBoxSVC and Machine about the progress object so they can combine
5233 proxy it to any openRemoteSession caller. */
5234 LogFlowThisFunc(("Calling BeginPowerUp...\n"));
5235 rc = mControl->BeginPowerUp(powerupProgress);
5236 if (FAILED(rc))
5237 {
5238 LogFlowThisFunc(("BeginPowerUp failed\n"));
5239 return rc;
5240 }
5241
5242 LogFlowThisFunc(("Checking if canceled...\n"));
5243 BOOL fCanceled;
5244 rc = powerupProgress->COMGETTER(Canceled)(&fCanceled);
5245 if (FAILED(rc))
5246 return rc;
5247 if (fCanceled)
5248 {
5249 LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
5250 return setError(E_FAIL, tr("Powerup was canceled"));
5251 }
5252 LogFlowThisFunc(("Not canceled yet.\n"));
5253
5254 /* setup task object and thread to carry out the operation
5255 * asynchronously */
5256
5257 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
5258 ComAssertComRCRetRC(task->rc());
5259
5260 task->mConfigConstructor = configConstructor;
5261 task->mSharedFolders = sharedFolders;
5262 task->mStartPaused = aPaused;
5263 if (mMachineState == MachineState_Saved)
5264 task->mSavedStateFile = savedStateFile;
5265 task->mTeleporterEnabled = fTeleporterEnabled;
5266
5267 /* Reset differencing hard disks for which autoReset is true,
5268 * but only if the machine has no snapshots OR the current snapshot
5269 * is an OFFLINE snapshot; otherwise we would reset the current differencing
5270 * image of an ONLINE snapshot which contains the disk state of the machine
5271 * while it was previously running, but without the corresponding machine
5272 * state, which is equivalent to powering off a running machine and not
5273 * good idea
5274 */
5275 ComPtr<ISnapshot> pCurrentSnapshot;
5276 rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
5277 if (FAILED(rc)) return rc;
5278
5279 BOOL fCurrentSnapshotIsOnline = false;
5280 if (pCurrentSnapshot)
5281 {
5282 rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
5283 if (FAILED(rc)) return rc;
5284 }
5285
5286 if (!fCurrentSnapshotIsOnline)
5287 {
5288 LogFlowThisFunc(("Looking for immutable images to reset\n"));
5289
5290 com::SafeIfaceArray<IMediumAttachment> atts;
5291 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
5292 if (FAILED(rc)) return rc;
5293
5294 for (size_t i = 0;
5295 i < atts.size();
5296 ++i)
5297 {
5298 DeviceType_T devType;
5299 rc = atts[i]->COMGETTER(Type)(&devType);
5300 /** @todo later applies to floppies as well */
5301 if (devType == DeviceType_HardDisk)
5302 {
5303 ComPtr<IMedium> medium;
5304 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
5305 if (FAILED(rc)) return rc;
5306
5307 /* needs autoreset? */
5308 BOOL autoReset = FALSE;
5309 rc = medium->COMGETTER(AutoReset)(&autoReset);
5310 if (FAILED(rc)) return rc;
5311
5312 if (autoReset)
5313 {
5314 ComPtr<IProgress> resetProgress;
5315 rc = medium->Reset(resetProgress.asOutParam());
5316 if (FAILED(rc)) return rc;
5317
5318 /* save for later use on the powerup thread */
5319 task->hardDiskProgresses.push_back(resetProgress);
5320 }
5321 }
5322 }
5323 }
5324 else
5325 LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
5326
5327 rc = consoleInitReleaseLog(mMachine);
5328 if (FAILED(rc)) return rc;
5329
5330 /* pass the progress object to the caller if requested */
5331 if (aProgress)
5332 {
5333 if (task->hardDiskProgresses.size() == 0)
5334 {
5335 /* there are no other operations to track, return the powerup
5336 * progress only */
5337 powerupProgress.queryInterfaceTo(aProgress);
5338 }
5339 else
5340 {
5341 /* create a combined progress object */
5342 ComObjPtr<CombinedProgress> progress;
5343 progress.createObject();
5344 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
5345 progresses.push_back(ComPtr<IProgress> (powerupProgress));
5346 rc = progress->init(static_cast<IConsole *>(this),
5347 progressDesc, progresses.begin(),
5348 progresses.end());
5349 AssertComRCReturnRC(rc);
5350 progress.queryInterfaceTo(aProgress);
5351 }
5352 }
5353
5354 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
5355 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
5356 if (RT_FAILURE(vrc))
5357 return setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
5358
5359 /* task is now owned by powerUpThread(), so release it */
5360 task.release();
5361
5362 /* finally, set the state: no right to fail in this method afterwards
5363 * since we've already started the thread and it is now responsible for
5364 * any error reporting and appropriate state change! */
5365
5366 if (mMachineState == MachineState_Saved)
5367 setMachineState(MachineState_Restoring);
5368 else if (fTeleporterEnabled)
5369 setMachineState(MachineState_TeleportingIn);
5370 else
5371 setMachineState(MachineState_Starting);
5372
5373 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
5374 LogFlowThisFuncLeave();
5375 return S_OK;
5376}
5377
5378/**
5379 * Internal power off worker routine.
5380 *
5381 * This method may be called only at certain places with the following meaning
5382 * as shown below:
5383 *
5384 * - if the machine state is either Running or Paused, a normal
5385 * Console-initiated powerdown takes place (e.g. PowerDown());
5386 * - if the machine state is Saving, saveStateThread() has successfully done its
5387 * job;
5388 * - if the machine state is Starting or Restoring, powerUpThread() has failed
5389 * to start/load the VM;
5390 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
5391 * as a result of the powerDown() call).
5392 *
5393 * Calling it in situations other than the above will cause unexpected behavior.
5394 *
5395 * Note that this method should be the only one that destroys mpVM and sets it
5396 * to NULL.
5397 *
5398 * @param aProgress Progress object to run (may be NULL).
5399 *
5400 * @note Locks this object for writing.
5401 *
5402 * @note Never call this method from a thread that called addVMCaller() or
5403 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
5404 * release(). Otherwise it will deadlock.
5405 */
5406HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
5407{
5408 LogFlowThisFuncEnter();
5409
5410 AutoCaller autoCaller(this);
5411 AssertComRCReturnRC(autoCaller.rc());
5412
5413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5414
5415 /* Total # of steps for the progress object. Must correspond to the
5416 * number of "advance percent count" comments in this method! */
5417 enum { StepCount = 7 };
5418 /* current step */
5419 ULONG step = 0;
5420
5421 HRESULT rc = S_OK;
5422 int vrc = VINF_SUCCESS;
5423
5424 /* sanity */
5425 Assert(mVMDestroying == false);
5426
5427 Assert(mpVM != NULL);
5428
5429 AssertMsg( mMachineState == MachineState_Running
5430 || mMachineState == MachineState_Paused
5431 || mMachineState == MachineState_Stuck
5432 || mMachineState == MachineState_Starting
5433 || mMachineState == MachineState_Stopping
5434 || mMachineState == MachineState_Saving
5435 || mMachineState == MachineState_Restoring
5436 || mMachineState == MachineState_TeleportingPausedVM
5437 || mMachineState == MachineState_TeleportingIn
5438 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
5439
5440 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
5441 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
5442
5443 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5444 * VM has already powered itself off in vmstateChangeCallback() and is just
5445 * notifying Console about that. In case of Starting or Restoring,
5446 * powerUpThread() is calling us on failure, so the VM is already off at
5447 * that point. */
5448 if ( !mVMPoweredOff
5449 && ( mMachineState == MachineState_Starting
5450 || mMachineState == MachineState_Restoring
5451 || mMachineState == MachineState_TeleportingIn)
5452 )
5453 mVMPoweredOff = true;
5454
5455 /*
5456 * Go to Stopping state if not already there.
5457 *
5458 * Note that we don't go from Saving/Restoring to Stopping because
5459 * vmstateChangeCallback() needs it to set the state to Saved on
5460 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5461 * while leaving the lock below, Saving or Restoring should be fine too.
5462 * Ditto for TeleportingPausedVM -> Teleported.
5463 */
5464 if ( mMachineState != MachineState_Saving
5465 && mMachineState != MachineState_Restoring
5466 && mMachineState != MachineState_Stopping
5467 && mMachineState != MachineState_TeleportingIn
5468 && mMachineState != MachineState_TeleportingPausedVM
5469 )
5470 setMachineState(MachineState_Stopping);
5471
5472 /* ----------------------------------------------------------------------
5473 * DONE with necessary state changes, perform the power down actions (it's
5474 * safe to leave the object lock now if needed)
5475 * ---------------------------------------------------------------------- */
5476
5477 /* Stop the VRDP server to prevent new clients connection while VM is being
5478 * powered off. */
5479 if (mConsoleVRDPServer)
5480 {
5481 LogFlowThisFunc(("Stopping VRDP server...\n"));
5482
5483 /* Leave the lock since EMT will call us back as addVMCaller()
5484 * in updateDisplayData(). */
5485 alock.leave();
5486
5487 mConsoleVRDPServer->Stop();
5488
5489 alock.enter();
5490 }
5491
5492 /* advance percent count */
5493 if (aProgress)
5494 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5495
5496
5497 /* ----------------------------------------------------------------------
5498 * Now, wait for all mpVM callers to finish their work if there are still
5499 * some on other threads. NO methods that need mpVM (or initiate other calls
5500 * that need it) may be called after this point
5501 * ---------------------------------------------------------------------- */
5502
5503 /* go to the destroying state to prevent from adding new callers */
5504 mVMDestroying = true;
5505
5506 if (mVMCallers > 0)
5507 {
5508 /* lazy creation */
5509 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5510 RTSemEventCreate(&mVMZeroCallersSem);
5511
5512 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5513 mVMCallers));
5514
5515 alock.leave();
5516
5517 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5518
5519 alock.enter();
5520 }
5521
5522 /* advance percent count */
5523 if (aProgress)
5524 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5525
5526 vrc = VINF_SUCCESS;
5527
5528 /*
5529 * Power off the VM if not already done that.
5530 * Leave the lock since EMT will call vmstateChangeCallback.
5531 *
5532 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5533 * VM-(guest-)initiated power off happened in parallel a ms before this
5534 * call. So far, we let this error pop up on the user's side.
5535 */
5536 if (!mVMPoweredOff)
5537 {
5538 LogFlowThisFunc(("Powering off the VM...\n"));
5539 alock.leave();
5540 vrc = VMR3PowerOff(mpVM);
5541 alock.enter();
5542 }
5543 else
5544 {
5545 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5546 * off. */
5547 /* reset the flag for future re-use */
5548 mVMPoweredOff = false;
5549 }
5550
5551 /* advance percent count */
5552 if (aProgress)
5553 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5554
5555#ifdef VBOX_WITH_HGCM
5556 /* Shutdown HGCM services before destroying the VM. */
5557 if (mVMMDev)
5558 {
5559 LogFlowThisFunc(("Shutdown HGCM...\n"));
5560
5561 /* Leave the lock since EMT will call us back as addVMCaller() */
5562 alock.leave();
5563
5564 mVMMDev->hgcmShutdown();
5565
5566 alock.enter();
5567 }
5568
5569 /* advance percent count */
5570 if (aProgress)
5571 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5572
5573#endif /* VBOX_WITH_HGCM */
5574
5575 LogFlowThisFunc(("Ready for VM destruction.\n"));
5576
5577 /* If we are called from Console::uninit(), then try to destroy the VM even
5578 * on failure (this will most likely fail too, but what to do?..) */
5579 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5580 {
5581 /* If the machine has an USB controller, release all USB devices
5582 * (symmetric to the code in captureUSBDevices()) */
5583 bool fHasUSBController = false;
5584 {
5585 PPDMIBASE pBase;
5586 vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5587 if (RT_SUCCESS(vrc))
5588 {
5589 fHasUSBController = true;
5590 detachAllUSBDevices(false /* aDone */);
5591 }
5592 }
5593
5594 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5595 * this point). We leave the lock before calling VMR3Destroy() because
5596 * it will result into calling destructors of drivers associated with
5597 * Console children which may in turn try to lock Console (e.g. by
5598 * instantiating SafeVMPtr to access mpVM). It's safe here because
5599 * mVMDestroying is set which should prevent any activity. */
5600
5601 /* Set mpVM to NULL early just in case if some old code is not using
5602 * addVMCaller()/releaseVMCaller(). */
5603 PVM pVM = mpVM;
5604 mpVM = NULL;
5605
5606 LogFlowThisFunc(("Destroying the VM...\n"));
5607
5608 alock.leave();
5609
5610 vrc = VMR3Destroy(pVM);
5611
5612 /* take the lock again */
5613 alock.enter();
5614
5615 /* advance percent count */
5616 if (aProgress)
5617 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5618
5619 if (RT_SUCCESS(vrc))
5620 {
5621 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5622 mMachineState));
5623 /* Note: the Console-level machine state change happens on the
5624 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5625 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5626 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5627 * occurred yet. This is okay, because mMachineState is already
5628 * Stopping in this case, so any other attempt to call PowerDown()
5629 * will be rejected. */
5630 }
5631 else
5632 {
5633 /* bad bad bad, but what to do? */
5634 mpVM = pVM;
5635 rc = setError(VBOX_E_VM_ERROR,
5636 tr("Could not destroy the machine. (Error: %Rrc)"),
5637 vrc);
5638 }
5639
5640 /* Complete the detaching of the USB devices. */
5641 if (fHasUSBController)
5642 detachAllUSBDevices(true /* aDone */);
5643
5644 /* advance percent count */
5645 if (aProgress)
5646 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5647 }
5648 else
5649 {
5650 rc = setError(VBOX_E_VM_ERROR,
5651 tr("Could not power off the machine. (Error: %Rrc)"),
5652 vrc);
5653 }
5654
5655 /* Finished with destruction. Note that if something impossible happened and
5656 * we've failed to destroy the VM, mVMDestroying will remain true and
5657 * mMachineState will be something like Stopping, so most Console methods
5658 * will return an error to the caller. */
5659 if (mpVM == NULL)
5660 mVMDestroying = false;
5661
5662 if (SUCCEEDED(rc))
5663 mCallbackData.clear();
5664
5665 /* complete the progress */
5666 if (aProgress)
5667 aProgress->notifyComplete(rc);
5668
5669 LogFlowThisFuncLeave();
5670 return rc;
5671}
5672
5673/**
5674 * @note Locks this object for writing.
5675 */
5676HRESULT Console::setMachineState(MachineState_T aMachineState,
5677 bool aUpdateServer /* = true */)
5678{
5679 AutoCaller autoCaller(this);
5680 AssertComRCReturnRC(autoCaller.rc());
5681
5682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5683
5684 HRESULT rc = S_OK;
5685
5686 if (mMachineState != aMachineState)
5687 {
5688 LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
5689 Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
5690 mMachineState = aMachineState;
5691
5692 /// @todo (dmik)
5693 // possibly, we need to redo onStateChange() using the dedicated
5694 // Event thread, like it is done in VirtualBox. This will make it
5695 // much safer (no deadlocks possible if someone tries to use the
5696 // console from the callback), however, listeners will lose the
5697 // ability to synchronously react to state changes (is it really
5698 // necessary??)
5699 LogFlowThisFunc(("Doing onStateChange()...\n"));
5700 onStateChange(aMachineState);
5701 LogFlowThisFunc(("Done onStateChange()\n"));
5702
5703 if (aUpdateServer)
5704 {
5705 /* Server notification MUST be done from under the lock; otherwise
5706 * the machine state here and on the server might go out of sync
5707 * which can lead to various unexpected results (like the machine
5708 * state being >= MachineState_Running on the server, while the
5709 * session state is already SessionState_Unlocked at the same time
5710 * there).
5711 *
5712 * Cross-lock conditions should be carefully watched out: calling
5713 * UpdateState we will require Machine and SessionMachine locks
5714 * (remember that here we're holding the Console lock here, and also
5715 * all locks that have been entered by the thread before calling
5716 * this method).
5717 */
5718 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5719 rc = mControl->UpdateState(aMachineState);
5720 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5721 }
5722 }
5723
5724 return rc;
5725}
5726
5727/**
5728 * Searches for a shared folder with the given logical name
5729 * in the collection of shared folders.
5730 *
5731 * @param aName logical name of the shared folder
5732 * @param aSharedFolder where to return the found object
5733 * @param aSetError whether to set the error info if the folder is
5734 * not found
5735 * @return
5736 * S_OK when found or E_INVALIDARG when not found
5737 *
5738 * @note The caller must lock this object for writing.
5739 */
5740HRESULT Console::findSharedFolder(CBSTR aName,
5741 ComObjPtr<SharedFolder> &aSharedFolder,
5742 bool aSetError /* = false */)
5743{
5744 /* sanity check */
5745 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5746
5747 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5748 if (it != mSharedFolders.end())
5749 {
5750 aSharedFolder = it->second;
5751 return S_OK;
5752 }
5753
5754 if (aSetError)
5755 setError(VBOX_E_FILE_ERROR,
5756 tr("Could not find a shared folder named '%ls'."),
5757 aName);
5758
5759 return VBOX_E_FILE_ERROR;
5760}
5761
5762/**
5763 * Fetches the list of global or machine shared folders from the server.
5764 *
5765 * @param aGlobal true to fetch global folders.
5766 *
5767 * @note The caller must lock this object for writing.
5768 */
5769HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5770{
5771 /* sanity check */
5772 AssertReturn(AutoCaller(this).state() == InInit ||
5773 isWriteLockOnCurrentThread(), E_FAIL);
5774
5775 /* protect mpVM (if not NULL) */
5776 AutoVMCallerQuietWeak autoVMCaller(this);
5777
5778 HRESULT rc = S_OK;
5779
5780 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5781
5782 if (aGlobal)
5783 {
5784 /// @todo grab & process global folders when they are done
5785 }
5786 else
5787 {
5788 SharedFolderDataMap oldFolders;
5789 if (online)
5790 oldFolders = mMachineSharedFolders;
5791
5792 mMachineSharedFolders.clear();
5793
5794 SafeIfaceArray<ISharedFolder> folders;
5795 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
5796 AssertComRCReturnRC(rc);
5797
5798 for (size_t i = 0; i < folders.size(); ++i)
5799 {
5800 ComPtr<ISharedFolder> folder = folders[i];
5801
5802 Bstr name;
5803 Bstr hostPath;
5804 BOOL writable;
5805 BOOL autoMount;
5806
5807 rc = folder->COMGETTER(Name)(name.asOutParam());
5808 if (FAILED(rc)) break;
5809 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
5810 if (FAILED(rc)) break;
5811 rc = folder->COMGETTER(Writable)(&writable);
5812 if (FAILED(rc)) break;
5813 rc = folder->COMGETTER(AutoMount)(&autoMount);
5814 if (FAILED(rc)) break;
5815
5816 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable, autoMount)));
5817
5818 /* send changes to HGCM if the VM is running */
5819 /// @todo report errors as runtime warnings through VMSetError
5820 if (online)
5821 {
5822 SharedFolderDataMap::iterator it = oldFolders.find(name);
5823 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5824 {
5825 /* a new machine folder is added or
5826 * the existing machine folder is changed */
5827 if (mSharedFolders.find(name) != mSharedFolders.end())
5828 ; /* the console folder exists, nothing to do */
5829 else
5830 {
5831 /* remove the old machine folder (when changed)
5832 * or the global folder if any (when new) */
5833 if (it != oldFolders.end() ||
5834 mGlobalSharedFolders.find(name) !=
5835 mGlobalSharedFolders.end())
5836 rc = removeSharedFolder(name);
5837 /* create the new machine folder */
5838 rc = createSharedFolder(name, SharedFolderData(hostPath, writable, autoMount));
5839 }
5840 }
5841 /* forget the processed (or identical) folder */
5842 if (it != oldFolders.end())
5843 oldFolders.erase(it);
5844
5845 rc = S_OK;
5846 }
5847 }
5848
5849 AssertComRCReturnRC(rc);
5850
5851 /* process outdated (removed) folders */
5852 /// @todo report errors as runtime warnings through VMSetError
5853 if (online)
5854 {
5855 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5856 it != oldFolders.end(); ++ it)
5857 {
5858 if (mSharedFolders.find(it->first) != mSharedFolders.end())
5859 ; /* the console folder exists, nothing to do */
5860 else
5861 {
5862 /* remove the outdated machine folder */
5863 rc = removeSharedFolder(it->first);
5864 /* create the global folder if there is any */
5865 SharedFolderDataMap::const_iterator git =
5866 mGlobalSharedFolders.find(it->first);
5867 if (git != mGlobalSharedFolders.end())
5868 rc = createSharedFolder(git->first, git->second);
5869 }
5870 }
5871
5872 rc = S_OK;
5873 }
5874 }
5875
5876 return rc;
5877}
5878
5879/**
5880 * Searches for a shared folder with the given name in the list of machine
5881 * shared folders and then in the list of the global shared folders.
5882 *
5883 * @param aName Name of the folder to search for.
5884 * @param aIt Where to store the pointer to the found folder.
5885 * @return @c true if the folder was found and @c false otherwise.
5886 *
5887 * @note The caller must lock this object for reading.
5888 */
5889bool Console::findOtherSharedFolder(IN_BSTR aName,
5890 SharedFolderDataMap::const_iterator &aIt)
5891{
5892 /* sanity check */
5893 AssertReturn(isWriteLockOnCurrentThread(), false);
5894
5895 /* first, search machine folders */
5896 aIt = mMachineSharedFolders.find(aName);
5897 if (aIt != mMachineSharedFolders.end())
5898 return true;
5899
5900 /* second, search machine folders */
5901 aIt = mGlobalSharedFolders.find(aName);
5902 if (aIt != mGlobalSharedFolders.end())
5903 return true;
5904
5905 return false;
5906}
5907
5908/**
5909 * Calls the HGCM service to add a shared folder definition.
5910 *
5911 * @param aName Shared folder name.
5912 * @param aHostPath Shared folder path.
5913 *
5914 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5915 * @note Doesn't lock anything.
5916 */
5917HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
5918{
5919 ComAssertRet(aName && *aName, E_FAIL);
5920 ComAssertRet(aData.mHostPath, E_FAIL);
5921
5922 /* sanity checks */
5923 AssertReturn(mpVM, E_FAIL);
5924 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5925
5926 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
5927 SHFLSTRING *pFolderName, *pMapName;
5928 size_t cbString;
5929
5930 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
5931
5932 cbString = (RTUtf16Len(aData.mHostPath) + 1) * sizeof(RTUTF16);
5933 if (cbString >= UINT16_MAX)
5934 return setError(E_INVALIDARG, tr("The name is too long"));
5935 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5936 Assert(pFolderName);
5937 memcpy(pFolderName->String.ucs2, aData.mHostPath, cbString);
5938
5939 pFolderName->u16Size = (uint16_t)cbString;
5940 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5941
5942 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
5943 parms[0].u.pointer.addr = pFolderName;
5944 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5945
5946 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
5947 if (cbString >= UINT16_MAX)
5948 {
5949 RTMemFree(pFolderName);
5950 return setError(E_INVALIDARG, tr("The host path is too long"));
5951 }
5952 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5953 Assert(pMapName);
5954 memcpy(pMapName->String.ucs2, aName, cbString);
5955
5956 pMapName->u16Size = (uint16_t)cbString;
5957 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5958
5959 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
5960 parms[1].u.pointer.addr = pMapName;
5961 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5962
5963 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
5964 parms[2].u.uint32 = aData.mWritable;
5965
5966 /*
5967 * Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
5968 * define below. This shows the host service that we have supplied
5969 * an additional parameter (auto-mount) and keeps the actual command
5970 * backwards compatible.
5971 */
5972 parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
5973 parms[3].u.uint32 = aData.mAutoMount;
5974
5975 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
5976 SHFL_FN_ADD_MAPPING,
5977 SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
5978 RTMemFree(pFolderName);
5979 RTMemFree(pMapName);
5980
5981 if (RT_FAILURE(vrc))
5982 return setError(E_FAIL,
5983 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
5984 aName, aData.mHostPath.raw(), vrc);
5985
5986 return S_OK;
5987}
5988
5989/**
5990 * Calls the HGCM service to remove the shared folder definition.
5991 *
5992 * @param aName Shared folder name.
5993 *
5994 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5995 * @note Doesn't lock anything.
5996 */
5997HRESULT Console::removeSharedFolder(CBSTR aName)
5998{
5999 ComAssertRet(aName && *aName, E_FAIL);
6000
6001 /* sanity checks */
6002 AssertReturn(mpVM, E_FAIL);
6003 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
6004
6005 VBOXHGCMSVCPARM parms;
6006 SHFLSTRING *pMapName;
6007 size_t cbString;
6008
6009 Log(("Removing shared folder '%ls'\n", aName));
6010
6011 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
6012 if (cbString >= UINT16_MAX)
6013 return setError(E_INVALIDARG, tr("The name is too long"));
6014 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
6015 Assert(pMapName);
6016 memcpy(pMapName->String.ucs2, aName, cbString);
6017
6018 pMapName->u16Size = (uint16_t)cbString;
6019 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
6020
6021 parms.type = VBOX_HGCM_SVC_PARM_PTR;
6022 parms.u.pointer.addr = pMapName;
6023 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
6024
6025 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
6026 SHFL_FN_REMOVE_MAPPING,
6027 1, &parms);
6028 RTMemFree(pMapName);
6029 if (RT_FAILURE(vrc))
6030 return setError(E_FAIL,
6031 tr("Could not remove the shared folder '%ls' (%Rrc)"),
6032 aName, vrc);
6033
6034 return S_OK;
6035}
6036
6037/**
6038 * VM state callback function. Called by the VMM
6039 * using its state machine states.
6040 *
6041 * Primarily used to handle VM initiated power off, suspend and state saving,
6042 * but also for doing termination completed work (VMSTATE_TERMINATE).
6043 *
6044 * In general this function is called in the context of the EMT.
6045 *
6046 * @param aVM The VM handle.
6047 * @param aState The new state.
6048 * @param aOldState The old state.
6049 * @param aUser The user argument (pointer to the Console object).
6050 *
6051 * @note Locks the Console object for writing.
6052 */
6053DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
6054 VMSTATE aState,
6055 VMSTATE aOldState,
6056 void *aUser)
6057{
6058 LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
6059 VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
6060
6061 Console *that = static_cast<Console *>(aUser);
6062 AssertReturnVoid(that);
6063
6064 AutoCaller autoCaller(that);
6065
6066 /* Note that we must let this method proceed even if Console::uninit() has
6067 * been already called. In such case this VMSTATE change is a result of:
6068 * 1) powerDown() called from uninit() itself, or
6069 * 2) VM-(guest-)initiated power off. */
6070 AssertReturnVoid( autoCaller.isOk()
6071 || autoCaller.state() == InUninit);
6072
6073 switch (aState)
6074 {
6075 /*
6076 * The VM has terminated
6077 */
6078 case VMSTATE_OFF:
6079 {
6080 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6081
6082 if (that->mVMStateChangeCallbackDisabled)
6083 break;
6084
6085 /* Do we still think that it is running? It may happen if this is a
6086 * VM-(guest-)initiated shutdown/poweroff.
6087 */
6088 if ( that->mMachineState != MachineState_Stopping
6089 && that->mMachineState != MachineState_Saving
6090 && that->mMachineState != MachineState_Restoring
6091 && that->mMachineState != MachineState_TeleportingIn
6092 && that->mMachineState != MachineState_TeleportingPausedVM
6093 && !that->mVMIsAlreadyPoweringOff
6094 )
6095 {
6096 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
6097
6098 /* prevent powerDown() from calling VMR3PowerOff() again */
6099 Assert(that->mVMPoweredOff == false);
6100 that->mVMPoweredOff = true;
6101
6102 /* we are stopping now */
6103 that->setMachineState(MachineState_Stopping);
6104
6105 /* Setup task object and thread to carry out the operation
6106 * asynchronously (if we call powerDown() right here but there
6107 * is one or more mpVM callers (added with addVMCaller()) we'll
6108 * deadlock).
6109 */
6110 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
6111 true /* aUsesVMPtr */));
6112
6113 /* If creating a task is falied, this can currently mean one of
6114 * two: either Console::uninit() has been called just a ms
6115 * before (so a powerDown() call is already on the way), or
6116 * powerDown() itself is being already executed. Just do
6117 * nothing.
6118 */
6119 if (!task->isOk())
6120 {
6121 LogFlowFunc(("Console is already being uninitialized.\n"));
6122 break;
6123 }
6124
6125 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
6126 (void *) task.get(), 0,
6127 RTTHREADTYPE_MAIN_WORKER, 0,
6128 "VMPowerDown");
6129 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
6130
6131 /* task is now owned by powerDownThread(), so release it */
6132 task.release();
6133 }
6134 break;
6135 }
6136
6137 /* The VM has been completely destroyed.
6138 *
6139 * Note: This state change can happen at two points:
6140 * 1) At the end of VMR3Destroy() if it was not called from EMT.
6141 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
6142 * called by EMT.
6143 */
6144 case VMSTATE_TERMINATED:
6145 {
6146 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6147
6148 if (that->mVMStateChangeCallbackDisabled)
6149 break;
6150
6151 /* Terminate host interface networking. If aVM is NULL, we've been
6152 * manually called from powerUpThread() either before calling
6153 * VMR3Create() or after VMR3Create() failed, so no need to touch
6154 * networking.
6155 */
6156 if (aVM)
6157 that->powerDownHostInterfaces();
6158
6159 /* From now on the machine is officially powered down or remains in
6160 * the Saved state.
6161 */
6162 switch (that->mMachineState)
6163 {
6164 default:
6165 AssertFailed();
6166 /* fall through */
6167 case MachineState_Stopping:
6168 /* successfully powered down */
6169 that->setMachineState(MachineState_PoweredOff);
6170 break;
6171 case MachineState_Saving:
6172 /* successfully saved (note that the machine is already in
6173 * the Saved state on the server due to EndSavingState()
6174 * called from saveStateThread(), so only change the local
6175 * state) */
6176 that->setMachineStateLocally(MachineState_Saved);
6177 break;
6178 case MachineState_Starting:
6179 /* failed to start, but be patient: set back to PoweredOff
6180 * (for similarity with the below) */
6181 that->setMachineState(MachineState_PoweredOff);
6182 break;
6183 case MachineState_Restoring:
6184 /* failed to load the saved state file, but be patient: set
6185 * back to Saved (to preserve the saved state file) */
6186 that->setMachineState(MachineState_Saved);
6187 break;
6188 case MachineState_TeleportingIn:
6189 /* Teleportation failed or was canceled. Back to powered off. */
6190 that->setMachineState(MachineState_PoweredOff);
6191 break;
6192 case MachineState_TeleportingPausedVM:
6193 /* Successfully teleported the VM. */
6194 that->setMachineState(MachineState_Teleported);
6195 break;
6196 }
6197 break;
6198 }
6199
6200 case VMSTATE_SUSPENDED:
6201 {
6202 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6203
6204 if (that->mVMStateChangeCallbackDisabled)
6205 break;
6206
6207 switch (that->mMachineState)
6208 {
6209 case MachineState_Teleporting:
6210 that->setMachineState(MachineState_TeleportingPausedVM);
6211 break;
6212
6213 case MachineState_LiveSnapshotting:
6214 that->setMachineState(MachineState_Saving);
6215 break;
6216
6217 case MachineState_TeleportingPausedVM:
6218 case MachineState_Saving:
6219 case MachineState_Restoring:
6220 case MachineState_Stopping:
6221 case MachineState_TeleportingIn:
6222 /* The worker threads handles the transition. */
6223 break;
6224
6225 default:
6226 AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
6227 case MachineState_Running:
6228 that->setMachineState(MachineState_Paused);
6229 break;
6230 }
6231 break;
6232 }
6233
6234 case VMSTATE_SUSPENDED_LS:
6235 case VMSTATE_SUSPENDED_EXT_LS:
6236 {
6237 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6238 if (that->mVMStateChangeCallbackDisabled)
6239 break;
6240 switch (that->mMachineState)
6241 {
6242 case MachineState_Teleporting:
6243 that->setMachineState(MachineState_TeleportingPausedVM);
6244 break;
6245
6246 case MachineState_LiveSnapshotting:
6247 that->setMachineState(MachineState_Saving);
6248 break;
6249
6250 case MachineState_TeleportingPausedVM:
6251 case MachineState_Saving:
6252 /* ignore */
6253 break;
6254
6255 default:
6256 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6257 that->setMachineState(MachineState_Paused);
6258 break;
6259 }
6260 break;
6261 }
6262
6263 case VMSTATE_RUNNING:
6264 {
6265 if ( aOldState == VMSTATE_POWERING_ON
6266 || aOldState == VMSTATE_RESUMING)
6267 {
6268 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6269
6270 if (that->mVMStateChangeCallbackDisabled)
6271 break;
6272
6273 Assert( ( ( that->mMachineState == MachineState_Starting
6274 || that->mMachineState == MachineState_Paused)
6275 && aOldState == VMSTATE_POWERING_ON)
6276 || ( ( that->mMachineState == MachineState_Restoring
6277 || that->mMachineState == MachineState_TeleportingIn
6278 || that->mMachineState == MachineState_Paused
6279 || that->mMachineState == MachineState_Saving
6280 )
6281 && aOldState == VMSTATE_RESUMING));
6282
6283 that->setMachineState(MachineState_Running);
6284 }
6285
6286 break;
6287 }
6288
6289 case VMSTATE_RUNNING_LS:
6290 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
6291 || that->mMachineState == MachineState_Teleporting,
6292 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
6293 break;
6294
6295 case VMSTATE_FATAL_ERROR:
6296 {
6297 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6298
6299 if (that->mVMStateChangeCallbackDisabled)
6300 break;
6301
6302 /* Fatal errors are only for running VMs. */
6303 Assert(Global::IsOnline(that->mMachineState));
6304
6305 /* Note! 'Pause' is used here in want of something better. There
6306 * are currently only two places where fatal errors might be
6307 * raised, so it is not worth adding a new externally
6308 * visible state for this yet. */
6309 that->setMachineState(MachineState_Paused);
6310 break;
6311 }
6312
6313 case VMSTATE_GURU_MEDITATION:
6314 {
6315 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6316
6317 if (that->mVMStateChangeCallbackDisabled)
6318 break;
6319
6320 /* Guru are only for running VMs */
6321 Assert(Global::IsOnline(that->mMachineState));
6322
6323 that->setMachineState(MachineState_Stuck);
6324 break;
6325 }
6326
6327 default: /* shut up gcc */
6328 break;
6329 }
6330}
6331
6332#ifdef VBOX_WITH_USB
6333
6334/**
6335 * Sends a request to VMM to attach the given host device.
6336 * After this method succeeds, the attached device will appear in the
6337 * mUSBDevices collection.
6338 *
6339 * @param aHostDevice device to attach
6340 *
6341 * @note Synchronously calls EMT.
6342 * @note Must be called from under this object's lock.
6343 */
6344HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
6345{
6346 AssertReturn(aHostDevice, E_FAIL);
6347 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6348
6349 /* still want a lock object because we need to leave it */
6350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 HRESULT hrc;
6353
6354 /*
6355 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
6356 * method in EMT (using usbAttachCallback()).
6357 */
6358 Bstr BstrAddress;
6359 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
6360 ComAssertComRCRetRC(hrc);
6361
6362 Utf8Str Address(BstrAddress);
6363
6364 Bstr id;
6365 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
6366 ComAssertComRCRetRC(hrc);
6367 Guid uuid(id);
6368
6369 BOOL fRemote = FALSE;
6370 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
6371 ComAssertComRCRetRC(hrc);
6372
6373 /* protect mpVM */
6374 AutoVMCaller autoVMCaller(this);
6375 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6376
6377 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
6378 Address.raw(), uuid.ptr()));
6379
6380 /* leave the lock before a VMR3* call (EMT will call us back)! */
6381 alock.leave();
6382
6383/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6384 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6385 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
6386
6387 /* restore the lock */
6388 alock.enter();
6389
6390 /* hrc is S_OK here */
6391
6392 if (RT_FAILURE(vrc))
6393 {
6394 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
6395 Address.raw(), uuid.ptr(), vrc));
6396
6397 switch (vrc)
6398 {
6399 case VERR_VUSB_NO_PORTS:
6400 hrc = setError(E_FAIL,
6401 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
6402 break;
6403 case VERR_VUSB_USBFS_PERMISSION:
6404 hrc = setError(E_FAIL,
6405 tr("Not permitted to open the USB device, check usbfs options"));
6406 break;
6407 default:
6408 hrc = setError(E_FAIL,
6409 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
6410 vrc);
6411 break;
6412 }
6413 }
6414
6415 return hrc;
6416}
6417
6418/**
6419 * USB device attach callback used by AttachUSBDevice().
6420 * Note that AttachUSBDevice() doesn't return until this callback is executed,
6421 * so we don't use AutoCaller and don't care about reference counters of
6422 * interface pointers passed in.
6423 *
6424 * @thread EMT
6425 * @note Locks the console object for writing.
6426 */
6427//static
6428DECLCALLBACK(int)
6429Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
6430{
6431 LogFlowFuncEnter();
6432 LogFlowFunc(("that={%p}\n", that));
6433
6434 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6435
6436 void *pvRemoteBackend = NULL;
6437 if (aRemote)
6438 {
6439 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
6440 Guid guid(*aUuid);
6441
6442 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
6443 if (!pvRemoteBackend)
6444 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
6445 }
6446
6447 USHORT portVersion = 1;
6448 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
6449 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
6450 Assert(portVersion == 1 || portVersion == 2);
6451
6452 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
6453 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
6454 if (RT_SUCCESS(vrc))
6455 {
6456 /* Create a OUSBDevice and add it to the device list */
6457 ComObjPtr<OUSBDevice> device;
6458 device.createObject();
6459 hrc = device->init(aHostDevice);
6460 AssertComRC(hrc);
6461
6462 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6463 that->mUSBDevices.push_back(device);
6464 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6465
6466 /* notify callbacks */
6467 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6468 }
6469
6470 LogFlowFunc(("vrc=%Rrc\n", vrc));
6471 LogFlowFuncLeave();
6472 return vrc;
6473}
6474
6475/**
6476 * Sends a request to VMM to detach the given host device. After this method
6477 * succeeds, the detached device will disappear from the mUSBDevices
6478 * collection.
6479 *
6480 * @param aIt Iterator pointing to the device to detach.
6481 *
6482 * @note Synchronously calls EMT.
6483 * @note Must be called from under this object's lock.
6484 */
6485HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6486{
6487 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6488
6489 /* still want a lock object because we need to leave it */
6490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6491
6492 /* protect mpVM */
6493 AutoVMCaller autoVMCaller(this);
6494 if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
6495
6496 /* if the device is attached, then there must at least one USB hub. */
6497 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6498
6499 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6500 (*aIt)->id().raw()));
6501
6502 /* leave the lock before a VMR3* call (EMT will call us back)! */
6503 alock.leave();
6504
6505/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6506 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6507 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6508 ComAssertRCRet(vrc, E_FAIL);
6509
6510 return S_OK;
6511}
6512
6513/**
6514 * USB device detach callback used by DetachUSBDevice().
6515 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6516 * so we don't use AutoCaller and don't care about reference counters of
6517 * interface pointers passed in.
6518 *
6519 * @thread EMT
6520 * @note Locks the console object for writing.
6521 */
6522//static
6523DECLCALLBACK(int)
6524Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6525{
6526 LogFlowFuncEnter();
6527 LogFlowFunc(("that={%p}\n", that));
6528
6529 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6530 ComObjPtr<OUSBDevice> device = **aIt;
6531
6532 /*
6533 * If that was a remote device, release the backend pointer.
6534 * The pointer was requested in usbAttachCallback.
6535 */
6536 BOOL fRemote = FALSE;
6537
6538 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6539 if (FAILED(hrc2))
6540 setErrorStatic(hrc2, "GetRemote() failed");
6541
6542 if (fRemote)
6543 {
6544 Guid guid(*aUuid);
6545 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6546 }
6547
6548 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6549
6550 if (RT_SUCCESS(vrc))
6551 {
6552 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
6553
6554 /* Remove the device from the collection */
6555 that->mUSBDevices.erase(*aIt);
6556 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6557
6558 /* notify callbacks */
6559 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6560 }
6561
6562 LogFlowFunc(("vrc=%Rrc\n", vrc));
6563 LogFlowFuncLeave();
6564 return vrc;
6565}
6566
6567#endif /* VBOX_WITH_USB */
6568#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
6569
6570/**
6571 * Helper function to handle host interface device creation and attachment.
6572 *
6573 * @param networkAdapter the network adapter which attachment should be reset
6574 * @return COM status code
6575 *
6576 * @note The caller must lock this object for writing.
6577 *
6578 * @todo Move this back into the driver!
6579 */
6580HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6581{
6582 LogFlowThisFunc(("\n"));
6583 /* sanity check */
6584 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6585
6586# ifdef VBOX_STRICT
6587 /* paranoia */
6588 NetworkAttachmentType_T attachment;
6589 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6590 Assert(attachment == NetworkAttachmentType_Bridged);
6591# endif /* VBOX_STRICT */
6592
6593 HRESULT rc = S_OK;
6594
6595 ULONG slot = 0;
6596 rc = networkAdapter->COMGETTER(Slot)(&slot);
6597 AssertComRC(rc);
6598
6599# ifdef RT_OS_LINUX
6600 /*
6601 * Allocate a host interface device
6602 */
6603 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6604 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6605 if (RT_SUCCESS(rcVBox))
6606 {
6607 /*
6608 * Set/obtain the tap interface.
6609 */
6610 struct ifreq IfReq;
6611 memset(&IfReq, 0, sizeof(IfReq));
6612 /* The name of the TAP interface we are using */
6613 Bstr tapDeviceName;
6614 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6615 if (FAILED(rc))
6616 tapDeviceName.setNull(); /* Is this necessary? */
6617 if (tapDeviceName.isEmpty())
6618 {
6619 LogRel(("No TAP device name was supplied.\n"));
6620 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6621 }
6622
6623 if (SUCCEEDED(rc))
6624 {
6625 /* If we are using a static TAP device then try to open it. */
6626 Utf8Str str(tapDeviceName);
6627 if (str.length() <= sizeof(IfReq.ifr_name))
6628 strcpy(IfReq.ifr_name, str.raw());
6629 else
6630 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6631 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6632 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6633 if (rcVBox != 0)
6634 {
6635 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6636 rc = setError(E_FAIL,
6637 tr("Failed to open the host network interface %ls"),
6638 tapDeviceName.raw());
6639 }
6640 }
6641 if (SUCCEEDED(rc))
6642 {
6643 /*
6644 * Make it pollable.
6645 */
6646 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6647 {
6648 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6649 /*
6650 * Here is the right place to communicate the TAP file descriptor and
6651 * the host interface name to the server if/when it becomes really
6652 * necessary.
6653 */
6654 maTAPDeviceName[slot] = tapDeviceName;
6655 rcVBox = VINF_SUCCESS;
6656 }
6657 else
6658 {
6659 int iErr = errno;
6660
6661 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6662 rcVBox = VERR_HOSTIF_BLOCKING;
6663 rc = setError(E_FAIL,
6664 tr("could not set up the host networking device for non blocking access: %s"),
6665 strerror(errno));
6666 }
6667 }
6668 }
6669 else
6670 {
6671 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6672 switch (rcVBox)
6673 {
6674 case VERR_ACCESS_DENIED:
6675 /* will be handled by our caller */
6676 rc = rcVBox;
6677 break;
6678 default:
6679 rc = setError(E_FAIL,
6680 tr("Could not set up the host networking device: %Rrc"),
6681 rcVBox);
6682 break;
6683 }
6684 }
6685
6686# elif defined(RT_OS_FREEBSD)
6687 /*
6688 * Set/obtain the tap interface.
6689 */
6690 /* The name of the TAP interface we are using */
6691 Bstr tapDeviceName;
6692 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6693 if (FAILED(rc))
6694 tapDeviceName.setNull(); /* Is this necessary? */
6695 if (tapDeviceName.isEmpty())
6696 {
6697 LogRel(("No TAP device name was supplied.\n"));
6698 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6699 }
6700 char szTapdev[1024] = "/dev/";
6701 /* If we are using a static TAP device then try to open it. */
6702 Utf8Str str(tapDeviceName);
6703 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6704 strcat(szTapdev, str.raw());
6705 else
6706 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6707 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6708 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6709
6710 if (RT_SUCCESS(rcVBox))
6711 maTAPDeviceName[slot] = tapDeviceName;
6712 else
6713 {
6714 switch (rcVBox)
6715 {
6716 case VERR_ACCESS_DENIED:
6717 /* will be handled by our caller */
6718 rc = rcVBox;
6719 break;
6720 default:
6721 rc = setError(E_FAIL,
6722 tr("Failed to open the host network interface %ls"),
6723 tapDeviceName.raw());
6724 break;
6725 }
6726 }
6727# else
6728# error "huh?"
6729# endif
6730 /* in case of failure, cleanup. */
6731 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6732 {
6733 LogRel(("General failure attaching to host interface\n"));
6734 rc = setError(E_FAIL,
6735 tr("General failure attaching to host interface"));
6736 }
6737 LogFlowThisFunc(("rc=%d\n", rc));
6738 return rc;
6739}
6740
6741
6742/**
6743 * Helper function to handle detachment from a host interface
6744 *
6745 * @param networkAdapter the network adapter which attachment should be reset
6746 * @return COM status code
6747 *
6748 * @note The caller must lock this object for writing.
6749 *
6750 * @todo Move this back into the driver!
6751 */
6752HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6753{
6754 /* sanity check */
6755 LogFlowThisFunc(("\n"));
6756 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6757
6758 HRESULT rc = S_OK;
6759# ifdef VBOX_STRICT
6760 /* paranoia */
6761 NetworkAttachmentType_T attachment;
6762 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6763 Assert(attachment == NetworkAttachmentType_Bridged);
6764# endif /* VBOX_STRICT */
6765
6766 ULONG slot = 0;
6767 rc = networkAdapter->COMGETTER(Slot)(&slot);
6768 AssertComRC(rc);
6769
6770 /* is there an open TAP device? */
6771 if (maTapFD[slot] != NIL_RTFILE)
6772 {
6773 /*
6774 * Close the file handle.
6775 */
6776 Bstr tapDeviceName, tapTerminateApplication;
6777 bool isStatic = true;
6778 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6779 if (FAILED(rc) || tapDeviceName.isEmpty())
6780 {
6781 /* If the name is empty, this is a dynamic TAP device, so close it now,
6782 so that the termination script can remove the interface. Otherwise we still
6783 need the FD to pass to the termination script. */
6784 isStatic = false;
6785 int rcVBox = RTFileClose(maTapFD[slot]);
6786 AssertRC(rcVBox);
6787 maTapFD[slot] = NIL_RTFILE;
6788 }
6789 if (isStatic)
6790 {
6791 /* If we are using a static TAP device, we close it now, after having called the
6792 termination script. */
6793 int rcVBox = RTFileClose(maTapFD[slot]);
6794 AssertRC(rcVBox);
6795 }
6796 /* the TAP device name and handle are no longer valid */
6797 maTapFD[slot] = NIL_RTFILE;
6798 maTAPDeviceName[slot] = "";
6799 }
6800 LogFlowThisFunc(("returning %d\n", rc));
6801 return rc;
6802}
6803
6804#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6805
6806/**
6807 * Called at power down to terminate host interface networking.
6808 *
6809 * @note The caller must lock this object for writing.
6810 */
6811HRESULT Console::powerDownHostInterfaces()
6812{
6813 LogFlowThisFunc(("\n"));
6814
6815 /* sanity check */
6816 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6817
6818 /*
6819 * host interface termination handling
6820 */
6821 HRESULT rc;
6822 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6823 {
6824 ComPtr<INetworkAdapter> networkAdapter;
6825 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6826 if (FAILED(rc)) break;
6827
6828 BOOL enabled = FALSE;
6829 networkAdapter->COMGETTER(Enabled)(&enabled);
6830 if (!enabled)
6831 continue;
6832
6833 NetworkAttachmentType_T attachment;
6834 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6835 if (attachment == NetworkAttachmentType_Bridged)
6836 {
6837#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6838 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6839 if (FAILED(rc2) && SUCCEEDED(rc))
6840 rc = rc2;
6841#endif
6842 }
6843 }
6844
6845 return rc;
6846}
6847
6848
6849/**
6850 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
6851 * and VMR3Teleport.
6852 *
6853 * @param pVM The VM handle.
6854 * @param uPercent Completetion precentage (0-100).
6855 * @param pvUser Pointer to the VMProgressTask structure.
6856 * @return VINF_SUCCESS.
6857 */
6858/*static*/
6859DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6860{
6861 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6862 AssertReturn(task, VERR_INVALID_PARAMETER);
6863
6864 /* update the progress object */
6865 if (task->mProgress)
6866 task->mProgress->SetCurrentOperationProgress(uPercent);
6867
6868 return VINF_SUCCESS;
6869}
6870
6871/**
6872 * @copydoc FNVMATERROR
6873 *
6874 * @remarks Might be some tiny serialization concerns with access to the string
6875 * object here...
6876 */
6877/*static*/ DECLCALLBACK(void)
6878Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6879 const char *pszErrorFmt, va_list va)
6880{
6881 Utf8Str *pErrorText = (Utf8Str *)pvUser;
6882 AssertPtr(pErrorText);
6883
6884 /* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
6885 va_list va2;
6886 va_copy(va2, va);
6887
6888 /* Append to any the existing error message. */
6889 if (pErrorText->length())
6890 *pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
6891 pszErrorFmt, &va2, rc, rc);
6892 else
6893 *pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
6894
6895 va_end(va2);
6896}
6897
6898/**
6899 * VM runtime error callback function.
6900 * See VMSetRuntimeError for the detailed description of parameters.
6901 *
6902 * @param pVM The VM handle.
6903 * @param pvUser The user argument.
6904 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6905 * @param pszErrorId Error ID string.
6906 * @param pszFormat Error message format string.
6907 * @param va Error message arguments.
6908 * @thread EMT.
6909 */
6910/* static */ DECLCALLBACK(void)
6911Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
6912 const char *pszErrorId,
6913 const char *pszFormat, va_list va)
6914{
6915 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6916 LogFlowFuncEnter();
6917
6918 Console *that = static_cast<Console *>(pvUser);
6919 AssertReturnVoid(that);
6920
6921 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
6922
6923 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
6924 fFatal, pszErrorId, message.raw()));
6925
6926 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
6927
6928 LogFlowFuncLeave();
6929}
6930
6931/**
6932 * Captures USB devices that match filters of the VM.
6933 * Called at VM startup.
6934 *
6935 * @param pVM The VM handle.
6936 *
6937 * @note The caller must lock this object for writing.
6938 */
6939HRESULT Console::captureUSBDevices(PVM pVM)
6940{
6941 LogFlowThisFunc(("\n"));
6942
6943 /* sanity check */
6944 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6945
6946 /* If the machine has an USB controller, ask the USB proxy service to
6947 * capture devices */
6948 PPDMIBASE pBase;
6949 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
6950 if (RT_SUCCESS(vrc))
6951 {
6952 /* leave the lock before calling Host in VBoxSVC since Host may call
6953 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6954 * produce an inter-process dead-lock otherwise. */
6955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6956 alock.leave();
6957
6958 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6959 ComAssertComRCRetRC(hrc);
6960 }
6961 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6962 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6963 vrc = VINF_SUCCESS;
6964 else
6965 AssertRC(vrc);
6966
6967 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
6968}
6969
6970
6971/**
6972 * Detach all USB device which are attached to the VM for the
6973 * purpose of clean up and such like.
6974 *
6975 * @note The caller must lock this object for writing.
6976 */
6977void Console::detachAllUSBDevices(bool aDone)
6978{
6979 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6980
6981 /* sanity check */
6982 AssertReturnVoid(isWriteLockOnCurrentThread());
6983
6984 mUSBDevices.clear();
6985
6986 /* leave the lock before calling Host in VBoxSVC since Host may call
6987 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6988 * produce an inter-process dead-lock otherwise. */
6989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6990 alock.leave();
6991
6992 mControl->DetachAllUSBDevices(aDone);
6993}
6994
6995/**
6996 * @note Locks this object for writing.
6997 */
6998void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6999{
7000 LogFlowThisFuncEnter();
7001 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
7002
7003 AutoCaller autoCaller(this);
7004 if (!autoCaller.isOk())
7005 {
7006 /* Console has been already uninitialized, deny request */
7007 AssertMsgFailed(("Console is already uninitialized\n"));
7008 LogFlowThisFunc(("Console is already uninitialized\n"));
7009 LogFlowThisFuncLeave();
7010 return;
7011 }
7012
7013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7014
7015 /*
7016 * Mark all existing remote USB devices as dirty.
7017 */
7018 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7019 it != mRemoteUSBDevices.end();
7020 ++it)
7021 {
7022 (*it)->dirty(true);
7023 }
7024
7025 /*
7026 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
7027 */
7028 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
7029 VRDPUSBDEVICEDESC *e = pDevList;
7030
7031 /* The cbDevList condition must be checked first, because the function can
7032 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
7033 */
7034 while (cbDevList >= 2 && e->oNext)
7035 {
7036 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
7037 e->idVendor, e->idProduct,
7038 e->oProduct? (char *)e + e->oProduct: ""));
7039
7040 bool fNewDevice = true;
7041
7042 for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7043 it != mRemoteUSBDevices.end();
7044 ++it)
7045 {
7046 if ((*it)->devId() == e->id
7047 && (*it)->clientId() == u32ClientId)
7048 {
7049 /* The device is already in the list. */
7050 (*it)->dirty(false);
7051 fNewDevice = false;
7052 break;
7053 }
7054 }
7055
7056 if (fNewDevice)
7057 {
7058 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
7059 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
7060
7061 /* Create the device object and add the new device to list. */
7062 ComObjPtr<RemoteUSBDevice> device;
7063 device.createObject();
7064 device->init(u32ClientId, e);
7065
7066 mRemoteUSBDevices.push_back(device);
7067
7068 /* Check if the device is ok for current USB filters. */
7069 BOOL fMatched = FALSE;
7070 ULONG fMaskedIfs = 0;
7071
7072 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
7073
7074 AssertComRC(hrc);
7075
7076 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
7077
7078 if (fMatched)
7079 {
7080 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
7081
7082 /// @todo (r=dmik) warning reporting subsystem
7083
7084 if (hrc == S_OK)
7085 {
7086 LogFlowThisFunc(("Device attached\n"));
7087 device->captured(true);
7088 }
7089 }
7090 }
7091
7092 if (cbDevList < e->oNext)
7093 {
7094 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
7095 cbDevList, e->oNext));
7096 break;
7097 }
7098
7099 cbDevList -= e->oNext;
7100
7101 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
7102 }
7103
7104 /*
7105 * Remove dirty devices, that is those which are not reported by the server anymore.
7106 */
7107 for (;;)
7108 {
7109 ComObjPtr<RemoteUSBDevice> device;
7110
7111 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
7112 while (it != mRemoteUSBDevices.end())
7113 {
7114 if ((*it)->dirty())
7115 {
7116 device = *it;
7117 break;
7118 }
7119
7120 ++ it;
7121 }
7122
7123 if (!device)
7124 {
7125 break;
7126 }
7127
7128 USHORT vendorId = 0;
7129 device->COMGETTER(VendorId)(&vendorId);
7130
7131 USHORT productId = 0;
7132 device->COMGETTER(ProductId)(&productId);
7133
7134 Bstr product;
7135 device->COMGETTER(Product)(product.asOutParam());
7136
7137 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
7138 vendorId, productId, product.raw()));
7139
7140 /* Detach the device from VM. */
7141 if (device->captured())
7142 {
7143 Bstr uuid;
7144 device->COMGETTER(Id)(uuid.asOutParam());
7145 onUSBDeviceDetach(uuid, NULL);
7146 }
7147
7148 /* And remove it from the list. */
7149 mRemoteUSBDevices.erase(it);
7150 }
7151
7152 LogFlowThisFuncLeave();
7153}
7154
7155/**
7156 * Thread function which starts the VM (also from saved state) and
7157 * track progress.
7158 *
7159 * @param Thread The thread id.
7160 * @param pvUser Pointer to a VMPowerUpTask structure.
7161 * @return VINF_SUCCESS (ignored).
7162 *
7163 * @note Locks the Console object for writing.
7164 */
7165/*static*/
7166DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
7167{
7168 LogFlowFuncEnter();
7169
7170 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
7171 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7172
7173 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
7174 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
7175
7176#if defined(RT_OS_WINDOWS)
7177 {
7178 /* initialize COM */
7179 HRESULT hrc = CoInitializeEx(NULL,
7180 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
7181 COINIT_SPEED_OVER_MEMORY);
7182 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
7183 }
7184#endif
7185
7186 HRESULT rc = S_OK;
7187 int vrc = VINF_SUCCESS;
7188
7189 /* Set up a build identifier so that it can be seen from core dumps what
7190 * exact build was used to produce the core. */
7191 static char saBuildID[40];
7192 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
7193 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
7194
7195 ComObjPtr<Console> console = task->mConsole;
7196
7197 /* Note: no need to use addCaller() because VMPowerUpTask does that */
7198
7199 /* The lock is also used as a signal from the task initiator (which
7200 * releases it only after RTThreadCreate()) that we can start the job */
7201 AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
7202
7203 /* sanity */
7204 Assert(console->mpVM == NULL);
7205
7206 try
7207 {
7208 /* wait for auto reset ops to complete so that we can successfully lock
7209 * the attached hard disks by calling LockMedia() below */
7210 for (VMPowerUpTask::ProgressList::const_iterator
7211 it = task->hardDiskProgresses.begin();
7212 it != task->hardDiskProgresses.end(); ++ it)
7213 {
7214 HRESULT rc2 = (*it)->WaitForCompletion(-1);
7215 AssertComRC(rc2);
7216 }
7217
7218 /*
7219 * Lock attached media. This method will also check their accessibility.
7220 * If we're a teleporter, we'll have to postpone this action so we can
7221 * migrate between local processes.
7222 *
7223 * Note! The media will be unlocked automatically by
7224 * SessionMachine::setMachineState() when the VM is powered down.
7225 */
7226 if (!task->mTeleporterEnabled)
7227 {
7228 rc = console->mControl->LockMedia();
7229 if (FAILED(rc)) throw rc;
7230 }
7231
7232#ifdef VBOX_WITH_VRDP
7233
7234 /* Create the VRDP server. In case of headless operation, this will
7235 * also create the framebuffer, required at VM creation.
7236 */
7237 ConsoleVRDPServer *server = console->consoleVRDPServer();
7238 Assert(server);
7239
7240 /* Does VRDP server call Console from the other thread?
7241 * Not sure (and can change), so leave the lock just in case.
7242 */
7243 alock.leave();
7244 vrc = server->Launch();
7245 alock.enter();
7246
7247 if (vrc == VERR_NET_ADDRESS_IN_USE)
7248 {
7249 Utf8Str errMsg;
7250 Bstr bstr;
7251 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
7252 Utf8Str ports = bstr;
7253 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
7254 ports.raw());
7255 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
7256 vrc, errMsg.raw()));
7257 }
7258 else if (RT_FAILURE(vrc))
7259 {
7260 Utf8Str errMsg;
7261 switch (vrc)
7262 {
7263 case VERR_FILE_NOT_FOUND:
7264 {
7265 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
7266 break;
7267 }
7268 default:
7269 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
7270 vrc);
7271 }
7272 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
7273 vrc, errMsg.raw()));
7274 throw setErrorStatic(E_FAIL, errMsg.c_str());
7275 }
7276
7277#endif /* VBOX_WITH_VRDP */
7278
7279 ComPtr<IMachine> pMachine = console->machine();
7280 ULONG cCpus = 1;
7281 pMachine->COMGETTER(CPUCount)(&cCpus);
7282
7283 /*
7284 * Create the VM
7285 */
7286 PVM pVM;
7287 /*
7288 * leave the lock since EMT will call Console. It's safe because
7289 * mMachineState is either Starting or Restoring state here.
7290 */
7291 alock.leave();
7292
7293 vrc = VMR3Create(cCpus, Console::genericVMSetErrorCallback, &task->mErrorMsg,
7294 task->mConfigConstructor, static_cast<Console *>(console),
7295 &pVM);
7296
7297 alock.enter();
7298
7299#ifdef VBOX_WITH_VRDP
7300 /* Enable client connections to the server. */
7301 console->consoleVRDPServer()->EnableConnections();
7302#endif /* VBOX_WITH_VRDP */
7303
7304 if (RT_SUCCESS(vrc))
7305 {
7306 do
7307 {
7308 /*
7309 * Register our load/save state file handlers
7310 */
7311 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
7312 NULL, NULL, NULL,
7313 NULL, saveStateFileExec, NULL,
7314 NULL, loadStateFileExec, NULL,
7315 static_cast<Console *>(console));
7316 AssertRCBreak(vrc);
7317
7318 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
7319 AssertRC(vrc);
7320 if (RT_FAILURE(vrc))
7321 break;
7322
7323 /*
7324 * Synchronize debugger settings
7325 */
7326 MachineDebugger *machineDebugger = console->getMachineDebugger();
7327 if (machineDebugger)
7328 {
7329 machineDebugger->flushQueuedSettings();
7330 }
7331
7332 /*
7333 * Shared Folders
7334 */
7335 if (console->getVMMDev()->isShFlActive())
7336 {
7337 /* Does the code below call Console from the other thread?
7338 * Not sure, so leave the lock just in case. */
7339 alock.leave();
7340
7341 for (SharedFolderDataMap::const_iterator
7342 it = task->mSharedFolders.begin();
7343 it != task->mSharedFolders.end();
7344 ++ it)
7345 {
7346 rc = console->createSharedFolder((*it).first, (*it).second);
7347 if (FAILED(rc)) break;
7348 }
7349 if (FAILED(rc)) break;
7350
7351 /* enter the lock again */
7352 alock.enter();
7353 }
7354
7355 /*
7356 * Capture USB devices.
7357 */
7358 rc = console->captureUSBDevices(pVM);
7359 if (FAILED(rc)) break;
7360
7361 /* leave the lock before a lengthy operation */
7362 alock.leave();
7363
7364 /* Load saved state? */
7365 if (task->mSavedStateFile.length())
7366 {
7367 LogFlowFunc(("Restoring saved state from '%s'...\n",
7368 task->mSavedStateFile.raw()));
7369
7370 vrc = VMR3LoadFromFile(pVM,
7371 task->mSavedStateFile.c_str(),
7372 Console::stateProgressCallback,
7373 static_cast<VMProgressTask*>(task.get()));
7374
7375 if (RT_SUCCESS(vrc))
7376 {
7377 if (task->mStartPaused)
7378 /* done */
7379 console->setMachineState(MachineState_Paused);
7380 else
7381 {
7382 /* Start/Resume the VM execution */
7383 vrc = VMR3Resume(pVM);
7384 AssertRC(vrc);
7385 }
7386 }
7387
7388 /* Power off in case we failed loading or resuming the VM */
7389 if (RT_FAILURE(vrc))
7390 {
7391 int vrc2 = VMR3PowerOff(pVM);
7392 AssertRC(vrc2);
7393 }
7394 }
7395 else if (task->mTeleporterEnabled)
7396 {
7397 /* -> ConsoleImplTeleporter.cpp */
7398 bool fPowerOffOnFailure;
7399 rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
7400 task->mProgress, &fPowerOffOnFailure);
7401 if (FAILED(rc) && fPowerOffOnFailure)
7402 {
7403 ErrorInfoKeeper eik;
7404 int vrc2 = VMR3PowerOff(pVM);
7405 AssertRC(vrc2);
7406 }
7407 }
7408 else if (task->mStartPaused)
7409 /* done */
7410 console->setMachineState(MachineState_Paused);
7411 else
7412 {
7413 /* Power on the VM (i.e. start executing) */
7414 vrc = VMR3PowerOn(pVM);
7415 AssertRC(vrc);
7416 }
7417
7418 /* enter the lock again */
7419 alock.enter();
7420 }
7421 while (0);
7422
7423 /* On failure, destroy the VM */
7424 if (FAILED(rc) || RT_FAILURE(vrc))
7425 {
7426 /* preserve existing error info */
7427 ErrorInfoKeeper eik;
7428
7429 /* powerDown() will call VMR3Destroy() and do all necessary
7430 * cleanup (VRDP, USB devices) */
7431 HRESULT rc2 = console->powerDown();
7432 AssertComRC(rc2);
7433 }
7434 else
7435 {
7436 /*
7437 * Deregister the VMSetError callback. This is necessary as the
7438 * pfnVMAtError() function passed to VMR3Create() is supposed to
7439 * be sticky but our error callback isn't.
7440 */
7441 alock.leave();
7442 VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
7443 /** @todo register another VMSetError callback? */
7444 alock.enter();
7445 }
7446 }
7447 else
7448 {
7449 /*
7450 * If VMR3Create() failed it has released the VM memory.
7451 */
7452 console->mpVM = NULL;
7453 }
7454
7455 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7456 {
7457 /* If VMR3Create() or one of the other calls in this function fail,
7458 * an appropriate error message has been set in task->mErrorMsg.
7459 * However since that happens via a callback, the rc status code in
7460 * this function is not updated.
7461 */
7462 if (!task->mErrorMsg.length())
7463 {
7464 /* If the error message is not set but we've got a failure,
7465 * convert the VBox status code into a meaningful error message.
7466 * This becomes unused once all the sources of errors set the
7467 * appropriate error message themselves.
7468 */
7469 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7470 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7471 vrc);
7472 }
7473
7474 /* Set the error message as the COM error.
7475 * Progress::notifyComplete() will pick it up later. */
7476 throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
7477 }
7478 }
7479 catch (HRESULT aRC) { rc = aRC; }
7480
7481 if ( console->mMachineState == MachineState_Starting
7482 || console->mMachineState == MachineState_Restoring
7483 || console->mMachineState == MachineState_TeleportingIn
7484 )
7485 {
7486 /* We are still in the Starting/Restoring state. This means one of:
7487 *
7488 * 1) we failed before VMR3Create() was called;
7489 * 2) VMR3Create() failed.
7490 *
7491 * In both cases, there is no need to call powerDown(), but we still
7492 * need to go back to the PoweredOff/Saved state. Reuse
7493 * vmstateChangeCallback() for that purpose.
7494 */
7495
7496 /* preserve existing error info */
7497 ErrorInfoKeeper eik;
7498
7499 Assert(console->mpVM == NULL);
7500 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7501 console);
7502 }
7503
7504 /*
7505 * Evaluate the final result. Note that the appropriate mMachineState value
7506 * is already set by vmstateChangeCallback() in all cases.
7507 */
7508
7509 /* leave the lock, don't need it any more */
7510 alock.leave();
7511
7512 if (SUCCEEDED(rc))
7513 {
7514 /* Notify the progress object of the success */
7515 task->mProgress->notifyComplete(S_OK);
7516 }
7517 else
7518 {
7519 /* The progress object will fetch the current error info */
7520 task->mProgress->notifyComplete(rc);
7521 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7522 }
7523
7524 /* Notify VBoxSVC and any waiting openRemoteSession progress object. */
7525 console->mControl->EndPowerUp(rc);
7526
7527#if defined(RT_OS_WINDOWS)
7528 /* uninitialize COM */
7529 CoUninitialize();
7530#endif
7531
7532 LogFlowFuncLeave();
7533
7534 return VINF_SUCCESS;
7535}
7536
7537
7538/**
7539 * Reconfigures a medium attachment (part of taking or deleting an online snapshot).
7540 *
7541 * @param pConsole Reference to the console object.
7542 * @param pVM The VM handle.
7543 * @param lInstance The instance of the controller.
7544 * @param pcszDevice The name of the controller type.
7545 * @param enmBus The storage bus type of the controller.
7546 * @param fSetupMerge Whether to set up a medium merge
7547 * @param uMergeSource Merge source image index
7548 * @param uMergeTarget Merge target image index
7549 * @param aMediumAtt The medium attachment.
7550 * @param aMachineState The current machine state.
7551 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7552 * @return VBox status code.
7553 */
7554/* static */
7555DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
7556 PVM pVM,
7557 const char *pcszDevice,
7558 unsigned uInstance,
7559 StorageBus_T enmBus,
7560 bool fUseHostIOCache,
7561 bool fSetupMerge,
7562 unsigned uMergeSource,
7563 unsigned uMergeTarget,
7564 IMediumAttachment *aMediumAtt,
7565 MachineState_T aMachineState,
7566 HRESULT *phrc)
7567{
7568 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7569
7570 int rc;
7571 HRESULT hrc;
7572 Bstr bstr;
7573 *phrc = S_OK;
7574#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7575#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7576
7577 /* Ignore attachments other than hard disks, since at the moment they are
7578 * not subject to snapshotting in general. */
7579 DeviceType_T lType;
7580 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7581 if (lType != DeviceType_HardDisk)
7582 return VINF_SUCCESS;
7583
7584 /* Determine the base path for the device instance. */
7585 PCFGMNODE pCtlInst;
7586 pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
7587 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
7588
7589 /* Update the device instance configuration. */
7590 rc = pConsole->configMediumAttachment(pCtlInst,
7591 pcszDevice,
7592 uInstance,
7593 enmBus,
7594 fUseHostIOCache,
7595 fSetupMerge,
7596 uMergeSource,
7597 uMergeTarget,
7598 aMediumAtt,
7599 aMachineState,
7600 phrc,
7601 true /* fAttachDetach */,
7602 false /* fForceUnmount */,
7603 pVM,
7604 NULL /* paLedDevType */);
7605 /** @todo this dumps everything attached to this device instance, which
7606 * is more than necessary. Dumping the changed LUN would be enough. */
7607 CFGMR3Dump(pCtlInst);
7608 RC_CHECK();
7609
7610#undef RC_CHECK
7611#undef H
7612
7613 LogFlowFunc(("Returns success\n"));
7614 return VINF_SUCCESS;
7615}
7616
7617/**
7618 * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
7619 */
7620static void takesnapshotProgressCancelCallback(void *pvUser)
7621{
7622 PVM pVM = (PVM)pvUser;
7623 SSMR3Cancel(pVM);
7624}
7625
7626/**
7627 * Worker thread created by Console::TakeSnapshot.
7628 * @param Thread The current thread (ignored).
7629 * @param pvUser The task.
7630 * @return VINF_SUCCESS (ignored).
7631 */
7632/*static*/
7633DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7634{
7635 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7636
7637 // taking a snapshot consists of the following:
7638
7639 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7640 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7641 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7642 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7643 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7644
7645 Console *that = pTask->mConsole;
7646 bool fBeganTakingSnapshot = false;
7647 bool fSuspenededBySave = false;
7648
7649 AutoCaller autoCaller(that);
7650 if (FAILED(autoCaller.rc()))
7651 {
7652 that->mptrCancelableProgress.setNull();
7653 return autoCaller.rc();
7654 }
7655
7656 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
7657
7658 HRESULT rc = S_OK;
7659
7660 try
7661 {
7662 /* STEP 1 + 2:
7663 * request creating the diff images on the server and create the snapshot object
7664 * (this will set the machine state to Saving on the server to block
7665 * others from accessing this machine)
7666 */
7667 rc = that->mControl->BeginTakingSnapshot(that,
7668 pTask->bstrName,
7669 pTask->bstrDescription,
7670 pTask->mProgress,
7671 pTask->fTakingSnapshotOnline,
7672 pTask->bstrSavedStateFile.asOutParam());
7673 if (FAILED(rc))
7674 throw rc;
7675
7676 fBeganTakingSnapshot = true;
7677
7678 /*
7679 * state file is non-null only when the VM is paused
7680 * (i.e. creating a snapshot online)
7681 */
7682 bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
7683 || ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
7684 if (!f)
7685 throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
7686
7687 /* sync the state with the server */
7688 if (pTask->lastMachineState == MachineState_Running)
7689 that->setMachineStateLocally(MachineState_LiveSnapshotting);
7690 else
7691 that->setMachineStateLocally(MachineState_Saving);
7692
7693 // STEP 3: save the VM state (if online)
7694 if (pTask->fTakingSnapshotOnline)
7695 {
7696 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7697
7698 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7699 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7700 pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
7701
7702 alock.leave();
7703 LogFlowFunc(("VMR3Save...\n"));
7704 int vrc = VMR3Save(that->mpVM,
7705 strSavedStateFile.c_str(),
7706 true /*fContinueAfterwards*/,
7707 Console::stateProgressCallback,
7708 (void*)pTask,
7709 &fSuspenededBySave);
7710 alock.enter();
7711 if (RT_FAILURE(vrc))
7712 throw setErrorStatic(E_FAIL,
7713 tr("Failed to save the machine state to '%s' (%Rrc)"),
7714 strSavedStateFile.c_str(), vrc);
7715
7716 pTask->mProgress->setCancelCallback(NULL, NULL);
7717 if (!pTask->mProgress->notifyPointOfNoReturn())
7718 throw setErrorStatic(E_FAIL, tr("Canceled"));
7719 that->mptrCancelableProgress.setNull();
7720
7721 // STEP 4: reattach hard disks
7722 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7723
7724 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7725 1); // operation weight, same as computed when setting up progress object
7726
7727 com::SafeIfaceArray<IMediumAttachment> atts;
7728 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7729 if (FAILED(rc))
7730 throw rc;
7731
7732 for (size_t i = 0;
7733 i < atts.size();
7734 ++i)
7735 {
7736 ComPtr<IStorageController> controller;
7737 Bstr controllerName;
7738 ULONG lInstance;
7739 StorageControllerType_T enmController;
7740 StorageBus_T enmBus;
7741 BOOL fUseHostIOCache;
7742
7743 /*
7744 * We can't pass a storage controller object directly
7745 * (g++ complains about not being able to pass non POD types through '...')
7746 * so we have to query needed values here and pass them.
7747 */
7748 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
7749 if (FAILED(rc))
7750 throw rc;
7751
7752 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7753 if (FAILED(rc))
7754 throw rc;
7755
7756 rc = controller->COMGETTER(ControllerType)(&enmController);
7757 if (FAILED(rc))
7758 throw rc;
7759 rc = controller->COMGETTER(Instance)(&lInstance);
7760 if (FAILED(rc))
7761 throw rc;
7762 rc = controller->COMGETTER(Bus)(&enmBus);
7763 if (FAILED(rc))
7764 throw rc;
7765 rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
7766 if (FAILED(rc))
7767 throw rc;
7768
7769 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7770
7771 /*
7772 * don't leave the lock since reconfigureMediumAttachment
7773 * isn't going to need the Console lock.
7774 */
7775 vrc = VMR3ReqCallWait(that->mpVM,
7776 VMCPUID_ANY,
7777 (PFNRT)reconfigureMediumAttachment,
7778 12,
7779 that,
7780 that->mpVM,
7781 pcszDevice,
7782 lInstance,
7783 enmBus,
7784 fUseHostIOCache,
7785 false /* fSetupMerge */,
7786 0 /* uMergeSource */,
7787 0 /* uMergeTarget */,
7788 atts[i],
7789 that->mMachineState,
7790 &rc);
7791 if (RT_FAILURE(vrc))
7792 throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
7793 if (FAILED(rc))
7794 throw rc;
7795 }
7796 }
7797
7798 /*
7799 * finalize the requested snapshot object.
7800 * This will reset the machine state to the state it had right
7801 * before calling mControl->BeginTakingSnapshot().
7802 */
7803 rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
7804 // do not throw rc here because we can't call EndTakingSnapshot() twice
7805 LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7806 }
7807 catch (HRESULT rcThrown)
7808 {
7809 /* preserve existing error info */
7810 ErrorInfoKeeper eik;
7811
7812 if (fBeganTakingSnapshot)
7813 that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
7814
7815 rc = rcThrown;
7816 LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
7817 }
7818 Assert(alock.isWriteLockOnCurrentThread());
7819
7820 if (FAILED(rc)) /* Must come before calling setMachineState. */
7821 pTask->mProgress->notifyComplete(rc);
7822
7823 /*
7824 * Fix up the machine state.
7825 *
7826 * For live snapshots we do all the work, for the two other variantions we
7827 * just update the local copy.
7828 */
7829 MachineState_T enmMachineState;
7830 that->mMachine->COMGETTER(State)(&enmMachineState);
7831 if ( that->mMachineState == MachineState_LiveSnapshotting
7832 || that->mMachineState == MachineState_Saving)
7833 {
7834
7835 if (!pTask->fTakingSnapshotOnline)
7836 that->setMachineStateLocally(pTask->lastMachineState);
7837 else if (SUCCEEDED(rc))
7838 {
7839 Assert( pTask->lastMachineState == MachineState_Running
7840 || pTask->lastMachineState == MachineState_Paused);
7841 Assert(that->mMachineState == MachineState_Saving);
7842 if (pTask->lastMachineState == MachineState_Running)
7843 {
7844 LogFlowFunc(("VMR3Resume...\n"));
7845 alock.leave();
7846 int vrc = VMR3Resume(that->mpVM);
7847 alock.enter();
7848 if (RT_FAILURE(vrc))
7849 {
7850 rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
7851 pTask->mProgress->notifyComplete(rc);
7852 if (that->mMachineState == MachineState_Saving)
7853 that->setMachineStateLocally(MachineState_Paused);
7854 }
7855 }
7856 else
7857 that->setMachineStateLocally(MachineState_Paused);
7858 }
7859 else
7860 {
7861 /** @todo this could probably be made more generic and reused elsewhere. */
7862 /* paranoid cleanup on for a failed online snapshot. */
7863 VMSTATE enmVMState = VMR3GetState(that->mpVM);
7864 switch (enmVMState)
7865 {
7866 case VMSTATE_RUNNING:
7867 case VMSTATE_RUNNING_LS:
7868 case VMSTATE_DEBUGGING:
7869 case VMSTATE_DEBUGGING_LS:
7870 case VMSTATE_POWERING_OFF:
7871 case VMSTATE_POWERING_OFF_LS:
7872 case VMSTATE_RESETTING:
7873 case VMSTATE_RESETTING_LS:
7874 Assert(!fSuspenededBySave);
7875 that->setMachineState(MachineState_Running);
7876 break;
7877
7878 case VMSTATE_GURU_MEDITATION:
7879 case VMSTATE_GURU_MEDITATION_LS:
7880 that->setMachineState(MachineState_Stuck);
7881 break;
7882
7883 case VMSTATE_FATAL_ERROR:
7884 case VMSTATE_FATAL_ERROR_LS:
7885 if (pTask->lastMachineState == MachineState_Paused)
7886 that->setMachineStateLocally(pTask->lastMachineState);
7887 else
7888 that->setMachineState(MachineState_Paused);
7889 break;
7890
7891 default:
7892 AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
7893 case VMSTATE_SUSPENDED:
7894 case VMSTATE_SUSPENDED_LS:
7895 case VMSTATE_SUSPENDING:
7896 case VMSTATE_SUSPENDING_LS:
7897 case VMSTATE_SUSPENDING_EXT_LS:
7898 if (fSuspenededBySave)
7899 {
7900 Assert(pTask->lastMachineState == MachineState_Running);
7901 LogFlowFunc(("VMR3Resume (on failure)...\n"));
7902 alock.leave();
7903 int vrc = VMR3Resume(that->mpVM);
7904 alock.enter();
7905 AssertLogRelRC(vrc);
7906 if (RT_FAILURE(vrc))
7907 that->setMachineState(MachineState_Paused);
7908 }
7909 else if (pTask->lastMachineState == MachineState_Paused)
7910 that->setMachineStateLocally(pTask->lastMachineState);
7911 else
7912 that->setMachineState(MachineState_Paused);
7913 break;
7914 }
7915
7916 }
7917 }
7918 /*else: somebody else has change the state... Leave it. */
7919
7920 /* check the remote state to see that we got it right. */
7921 that->mMachine->COMGETTER(State)(&enmMachineState);
7922 AssertLogRelMsg(that->mMachineState == enmMachineState,
7923 ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
7924 Global::stringifyMachineState(enmMachineState) ));
7925
7926
7927 if (SUCCEEDED(rc)) /* The failure cases are handled above. */
7928 pTask->mProgress->notifyComplete(rc);
7929
7930 delete pTask;
7931
7932 LogFlowFuncLeave();
7933 return VINF_SUCCESS;
7934}
7935
7936/**
7937 * Thread for executing the saved state operation.
7938 *
7939 * @param Thread The thread handle.
7940 * @param pvUser Pointer to a VMSaveTask structure.
7941 * @return VINF_SUCCESS (ignored).
7942 *
7943 * @note Locks the Console object for writing.
7944 */
7945/*static*/
7946DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
7947{
7948 LogFlowFuncEnter();
7949
7950 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
7951 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7952
7953 Assert(task->mSavedStateFile.length());
7954 Assert(!task->mProgress.isNull());
7955
7956 const ComObjPtr<Console> &that = task->mConsole;
7957 Utf8Str errMsg;
7958 HRESULT rc = S_OK;
7959
7960 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7961
7962 bool fSuspenededBySave;
7963 int vrc = VMR3Save(that->mpVM,
7964 task->mSavedStateFile.c_str(),
7965 false, /*fContinueAfterwards*/
7966 Console::stateProgressCallback,
7967 static_cast<VMProgressTask*>(task.get()),
7968 &fSuspenededBySave);
7969 if (RT_FAILURE(vrc))
7970 {
7971 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
7972 task->mSavedStateFile.raw(), vrc);
7973 rc = E_FAIL;
7974 }
7975 Assert(!fSuspenededBySave);
7976
7977 /* lock the console once we're going to access it */
7978 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
7979
7980 /*
7981 * finalize the requested save state procedure.
7982 * In case of success, the server will set the machine state to Saved;
7983 * in case of failure it will reset the it to the state it had right
7984 * before calling mControl->BeginSavingState().
7985 */
7986 that->mControl->EndSavingState(SUCCEEDED(rc));
7987
7988 /* synchronize the state with the server */
7989 if (!FAILED(rc))
7990 {
7991 /*
7992 * The machine has been successfully saved, so power it down
7993 * (vmstateChangeCallback() will set state to Saved on success).
7994 * Note: we release the task's VM caller, otherwise it will
7995 * deadlock.
7996 */
7997 task->releaseVMCaller();
7998
7999 rc = that->powerDown();
8000 }
8001
8002 /* notify the progress object about operation completion */
8003 if (SUCCEEDED(rc))
8004 task->mProgress->notifyComplete(S_OK);
8005 else
8006 {
8007 if (errMsg.length())
8008 task->mProgress->notifyComplete(rc,
8009 COM_IIDOF(IConsole),
8010 Console::getStaticComponentName(),
8011 errMsg.c_str());
8012 else
8013 task->mProgress->notifyComplete(rc);
8014 }
8015
8016 LogFlowFuncLeave();
8017 return VINF_SUCCESS;
8018}
8019
8020/**
8021 * Thread for powering down the Console.
8022 *
8023 * @param Thread The thread handle.
8024 * @param pvUser Pointer to the VMTask structure.
8025 * @return VINF_SUCCESS (ignored).
8026 *
8027 * @note Locks the Console object for writing.
8028 */
8029/*static*/
8030DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
8031{
8032 LogFlowFuncEnter();
8033
8034 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
8035 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
8036
8037 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
8038
8039 const ComObjPtr<Console> &that = task->mConsole;
8040
8041 /* Note: no need to use addCaller() to protect Console because VMTask does
8042 * that */
8043
8044 /* wait until the method tat started us returns */
8045 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
8046
8047 /* release VM caller to avoid the powerDown() deadlock */
8048 task->releaseVMCaller();
8049
8050 that->powerDown(task->mProgress);
8051
8052 LogFlowFuncLeave();
8053 return VINF_SUCCESS;
8054}
8055
8056/**
8057 * The Main status driver instance data.
8058 */
8059typedef struct DRVMAINSTATUS
8060{
8061 /** The LED connectors. */
8062 PDMILEDCONNECTORS ILedConnectors;
8063 /** Pointer to the LED ports interface above us. */
8064 PPDMILEDPORTS pLedPorts;
8065 /** Pointer to the array of LED pointers. */
8066 PPDMLED *papLeds;
8067 /** The unit number corresponding to the first entry in the LED array. */
8068 RTUINT iFirstLUN;
8069 /** The unit number corresponding to the last entry in the LED array.
8070 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
8071 RTUINT iLastLUN;
8072} DRVMAINSTATUS, *PDRVMAINSTATUS;
8073
8074
8075/**
8076 * Notification about a unit which have been changed.
8077 *
8078 * The driver must discard any pointers to data owned by
8079 * the unit and requery it.
8080 *
8081 * @param pInterface Pointer to the interface structure containing the called function pointer.
8082 * @param iLUN The unit number.
8083 */
8084DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
8085{
8086 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
8087 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
8088 {
8089 PPDMLED pLed;
8090 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
8091 if (RT_FAILURE(rc))
8092 pLed = NULL;
8093 ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
8094 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
8095 }
8096}
8097
8098
8099/**
8100 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
8101 */
8102DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
8103{
8104 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
8105 PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8106 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
8107 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
8108 return NULL;
8109}
8110
8111
8112/**
8113 * Destruct a status driver instance.
8114 *
8115 * @returns VBox status.
8116 * @param pDrvIns The driver instance data.
8117 */
8118DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
8119{
8120 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8121 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8122 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
8123
8124 if (pData->papLeds)
8125 {
8126 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
8127 while (iLed-- > 0)
8128 ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
8129 }
8130}
8131
8132
8133/**
8134 * Construct a status driver instance.
8135 *
8136 * @copydoc FNPDMDRVCONSTRUCT
8137 */
8138DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
8139{
8140 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
8141 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
8142 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
8143
8144 /*
8145 * Validate configuration.
8146 */
8147 if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
8148 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
8149 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
8150 ("Configuration error: Not possible to attach anything to this driver!\n"),
8151 VERR_PDM_DRVINS_NO_ATTACH);
8152
8153 /*
8154 * Data.
8155 */
8156 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
8157 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
8158
8159 /*
8160 * Read config.
8161 */
8162 int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
8163 if (RT_FAILURE(rc))
8164 {
8165 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
8166 return rc;
8167 }
8168
8169 rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
8170 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8171 pData->iFirstLUN = 0;
8172 else if (RT_FAILURE(rc))
8173 {
8174 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
8175 return rc;
8176 }
8177
8178 rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
8179 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
8180 pData->iLastLUN = 0;
8181 else if (RT_FAILURE(rc))
8182 {
8183 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
8184 return rc;
8185 }
8186 if (pData->iFirstLUN > pData->iLastLUN)
8187 {
8188 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
8189 return VERR_GENERAL_FAILURE;
8190 }
8191
8192 /*
8193 * Get the ILedPorts interface of the above driver/device and
8194 * query the LEDs we want.
8195 */
8196 pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
8197 AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
8198 VERR_PDM_MISSING_INTERFACE_ABOVE);
8199
8200 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
8201 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
8202
8203 return VINF_SUCCESS;
8204}
8205
8206
8207/**
8208 * Keyboard driver registration record.
8209 */
8210const PDMDRVREG Console::DrvStatusReg =
8211{
8212 /* u32Version */
8213 PDM_DRVREG_VERSION,
8214 /* szName */
8215 "MainStatus",
8216 /* szRCMod */
8217 "",
8218 /* szR0Mod */
8219 "",
8220 /* pszDescription */
8221 "Main status driver (Main as in the API).",
8222 /* fFlags */
8223 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
8224 /* fClass. */
8225 PDM_DRVREG_CLASS_STATUS,
8226 /* cMaxInstances */
8227 ~0,
8228 /* cbInstance */
8229 sizeof(DRVMAINSTATUS),
8230 /* pfnConstruct */
8231 Console::drvStatus_Construct,
8232 /* pfnDestruct */
8233 Console::drvStatus_Destruct,
8234 /* pfnRelocate */
8235 NULL,
8236 /* pfnIOCtl */
8237 NULL,
8238 /* pfnPowerOn */
8239 NULL,
8240 /* pfnReset */
8241 NULL,
8242 /* pfnSuspend */
8243 NULL,
8244 /* pfnResume */
8245 NULL,
8246 /* pfnAttach */
8247 NULL,
8248 /* pfnDetach */
8249 NULL,
8250 /* pfnPowerOff */
8251 NULL,
8252 /* pfnSoftReset */
8253 NULL,
8254 /* u32EndVersion */
8255 PDM_DRVREG_VERSION
8256};
8257
8258/* 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