VirtualBox

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

Last change on this file since 30825 was 30825, checked in by vboxsync, 15 years ago

Main: callbacks removal

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette