VirtualBox

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

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

pdmifs.h: Moved the network interfaces to a separate header called pdmnetifs.h.

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

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