VirtualBox

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

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

Added Global::vboxStatusCodeFromCOM (and Global::vboxStatusCodeToCOM), made Console::doGuestPropNotification make use of it.

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

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