VirtualBox

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

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

Main: back out r57775

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