VirtualBox

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

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

fixed status LEDs for storage devices

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