VirtualBox

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

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

API/Machine,Session: add a force mount parameter to MountMedium and the associated other places

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

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