VirtualBox

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

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

virtio: string mismatch between device and some parts of main.

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