VirtualBox

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

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

Main: make restoreSnapshot() work with the lock validator; take saveSettings() out of a lot of functions and instead return a flag to the caller so the caller can make that call; as a side effect, this no longer calls saveSettings multiple times in several code paths (e.g. restoreSnapshot()) and cleans up locking in medium tasks

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

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