VirtualBox

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

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

Activated large guest memory support for 32-bit guests

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

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