VirtualBox

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

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

ConsoleImpl.cpp: Flush the directory + hint how to get a good release log on panic. Some cleanup

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

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