VirtualBox

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

Last change on this file since 31131 was 31008, checked in by vboxsync, 14 years ago

Main: reorganize session APIs

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