VirtualBox

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

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

Main: removed bogus mouse shape data assert

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

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