VirtualBox

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

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

Main: fixed saving guest properties at machine power off

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