VirtualBox

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

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

Pass RDP credentials to the guest only if there are no logged in users (xTracker 4514)

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