VirtualBox

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

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

Main: Bstr makeover (third attempt) -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull??() usage

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