VirtualBox

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

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

ConsoleImpl: Assertion update

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

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