VirtualBox

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

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

ConsoleImpl.cpp: Fussing over the guest property code. (no fixes yet)

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