VirtualBox

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

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

ConsoleImpl.cpp: Pick up any SSM error message that might be around when teleporterTrg fails.

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