VirtualBox

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

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

Main/Session+Console: By default, create a console with no sub-objects (saves resources). VM processes need to explicitly ask for a full console when before opening the session.

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