VirtualBox

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

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

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

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