VirtualBox

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

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

SessionMachine::PushGuestProperty: Fixed state validation. mHWData.backup() seems to have to trouble with our mutable state when teleporting.

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