VirtualBox

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

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

Main: Don't try to automatically clear TeleporterEnabled when it isn't safe / working.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 247.8 KB
Line 
1/* $Id: ConsoleImpl.cpp 24786 2009-11-19 12:57:05Z 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 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
4809 if (fTeleporterEnabled)
4810 {
4811 rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
4812 CheckComRCReturnRC(rc);
4813 }
4814#endif
4815
4816 /* create a progress object to track progress of this operation */
4817 ComObjPtr<Progress> powerupProgress;
4818 powerupProgress.createObject();
4819 Bstr progressDesc;
4820 if (mMachineState == MachineState_Saved)
4821 progressDesc = tr("Restoring virtual machine");
4822 else if (fTeleporterEnabled)
4823 progressDesc = tr("Teleporting virtual machine");
4824 else
4825 progressDesc = tr("Starting virtual machine");
4826 rc = powerupProgress->init(static_cast<IConsole *>(this),
4827 progressDesc,
4828 fTeleporterEnabled /* aCancelable */);
4829 CheckComRCReturnRC(rc);
4830
4831 /* setup task object and thread to carry out the operation
4832 * asynchronously */
4833
4834 std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
4835 ComAssertComRCRetRC(task->rc());
4836
4837 task->mSetVMErrorCallback = setVMErrorCallback;
4838 task->mConfigConstructor = configConstructor;
4839 task->mSharedFolders = sharedFolders;
4840 task->mStartPaused = aPaused;
4841 if (mMachineState == MachineState_Saved)
4842 task->mSavedStateFile = savedStateFile;
4843 task->mTeleporterEnabled = fTeleporterEnabled;
4844
4845 /* Reset differencing hard disks for which autoReset is true */
4846 {
4847 com::SafeIfaceArray<IMediumAttachment> atts;
4848 rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
4849 CheckComRCReturnRC(rc);
4850
4851 for (size_t i = 0; i < atts.size(); ++ i)
4852 {
4853 DeviceType_T devType;
4854 rc = atts[i]->COMGETTER(Type)(&devType);
4855 /** @todo later applies to floppies as well */
4856 if (devType == DeviceType_HardDisk)
4857 {
4858 ComPtr<IMedium> medium;
4859 rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
4860 CheckComRCReturnRC(rc);
4861
4862 /* save for later use on the powerup thread */
4863 task->hardDisks.push_back(medium);
4864
4865 /* needs autoreset? */
4866 BOOL autoReset = FALSE;
4867 rc = medium->COMGETTER(AutoReset)(&autoReset);
4868 CheckComRCReturnRC(rc);
4869
4870 if (autoReset)
4871 {
4872 ComPtr<IProgress> resetProgress;
4873 rc = medium->Reset(resetProgress.asOutParam());
4874 CheckComRCReturnRC(rc);
4875
4876 /* save for later use on the powerup thread */
4877 task->hardDiskProgresses.push_back(resetProgress);
4878 }
4879 }
4880 }
4881 }
4882
4883 rc = consoleInitReleaseLog(mMachine);
4884 CheckComRCReturnRC(rc);
4885
4886 /* pass the progress object to the caller if requested */
4887 if (aProgress)
4888 {
4889 if (task->hardDiskProgresses.size() == 0)
4890 {
4891 /* there are no other operations to track, return the powerup
4892 * progress only */
4893 powerupProgress.queryInterfaceTo(aProgress);
4894 }
4895 else
4896 {
4897 /* create a combined progress object */
4898 ComObjPtr<CombinedProgress> progress;
4899 progress.createObject();
4900 VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
4901 progresses.push_back(ComPtr<IProgress> (powerupProgress));
4902 rc = progress->init(static_cast<IConsole *>(this),
4903 progressDesc, progresses.begin(),
4904 progresses.end());
4905 AssertComRCReturnRC(rc);
4906 progress.queryInterfaceTo(aProgress);
4907 }
4908 }
4909
4910 int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
4911 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
4912
4913 ComAssertMsgRCRet(vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
4914 E_FAIL);
4915
4916 /* task is now owned by powerUpThread(), so release it */
4917 task.release();
4918
4919 /* finally, set the state: no right to fail in this method afterwards
4920 * since we've already started the thread and it is now responsible for
4921 * any error reporting and appropriate state change! */
4922
4923 if (mMachineState == MachineState_Saved)
4924 setMachineState(MachineState_Restoring);
4925 else if (fTeleporterEnabled)
4926 setMachineState(MachineState_TeleportingIn);
4927 else
4928 setMachineState(MachineState_Starting);
4929
4930 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
4931 LogFlowThisFuncLeave();
4932 return S_OK;
4933}
4934
4935/**
4936 * Internal power off worker routine.
4937 *
4938 * This method may be called only at certain places with the following meaning
4939 * as shown below:
4940 *
4941 * - if the machine state is either Running or Paused, a normal
4942 * Console-initiated powerdown takes place (e.g. PowerDown());
4943 * - if the machine state is Saving, saveStateThread() has successfully done its
4944 * job;
4945 * - if the machine state is Starting or Restoring, powerUpThread() has failed
4946 * to start/load the VM;
4947 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
4948 * as a result of the powerDown() call).
4949 *
4950 * Calling it in situations other than the above will cause unexpected behavior.
4951 *
4952 * Note that this method should be the only one that destroys mpVM and sets it
4953 * to NULL.
4954 *
4955 * @param aProgress Progress object to run (may be NULL).
4956 *
4957 * @note Locks this object for writing.
4958 *
4959 * @note Never call this method from a thread that called addVMCaller() or
4960 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
4961 * release(). Otherwise it will deadlock.
4962 */
4963HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
4964{
4965 LogFlowThisFuncEnter();
4966
4967 AutoCaller autoCaller(this);
4968 AssertComRCReturnRC(autoCaller.rc());
4969
4970 AutoWriteLock alock(this);
4971
4972 /* Total # of steps for the progress object. Must correspond to the
4973 * number of "advance percent count" comments in this method! */
4974 enum { StepCount = 7 };
4975 /* current step */
4976 ULONG step = 0;
4977
4978 HRESULT rc = S_OK;
4979 int vrc = VINF_SUCCESS;
4980
4981 /* sanity */
4982 Assert(mVMDestroying == false);
4983
4984 Assert(mpVM != NULL);
4985
4986 AssertMsg( mMachineState == MachineState_Running
4987 || mMachineState == MachineState_Paused
4988 || mMachineState == MachineState_Stuck
4989 || mMachineState == MachineState_Starting
4990 || mMachineState == MachineState_Stopping
4991 || mMachineState == MachineState_Saving
4992 || mMachineState == MachineState_Restoring
4993 || mMachineState == MachineState_TeleportingPausedVM
4994 || mMachineState == MachineState_TeleportingIn
4995 , ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
4996
4997 LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
4998 Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
4999
5000 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
5001 * VM has already powered itself off in vmstateChangeCallback() and is just
5002 * notifying Console about that. In case of Starting or Restoring,
5003 * powerUpThread() is calling us on failure, so the VM is already off at
5004 * that point. */
5005 if ( !mVMPoweredOff
5006 && ( mMachineState == MachineState_Starting
5007 || mMachineState == MachineState_Restoring
5008 || mMachineState == MachineState_TeleportingIn)
5009 )
5010 mVMPoweredOff = true;
5011
5012 /*
5013 * Go to Stopping state if not already there.
5014 *
5015 * Note that we don't go from Saving/Restoring to Stopping because
5016 * vmstateChangeCallback() needs it to set the state to Saved on
5017 * VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
5018 * while leaving the lock below, Saving or Restoring should be fine too.
5019 * Ditto for TeleportingPausedVM -> Teleported.
5020 */
5021 if ( mMachineState != MachineState_Saving
5022 && mMachineState != MachineState_Restoring
5023 && mMachineState != MachineState_Stopping
5024 && mMachineState != MachineState_TeleportingIn
5025 && mMachineState != MachineState_TeleportingPausedVM
5026 )
5027 setMachineState(MachineState_Stopping);
5028
5029 /* ----------------------------------------------------------------------
5030 * DONE with necessary state changes, perform the power down actions (it's
5031 * safe to leave the object lock now if needed)
5032 * ---------------------------------------------------------------------- */
5033
5034 /* Stop the VRDP server to prevent new clients connection while VM is being
5035 * powered off. */
5036 if (mConsoleVRDPServer)
5037 {
5038 LogFlowThisFunc(("Stopping VRDP server...\n"));
5039
5040 /* Leave the lock since EMT will call us back as addVMCaller()
5041 * in updateDisplayData(). */
5042 alock.leave();
5043
5044 mConsoleVRDPServer->Stop();
5045
5046 alock.enter();
5047 }
5048
5049 /* advance percent count */
5050 if (aProgress)
5051 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5052
5053
5054 /* ----------------------------------------------------------------------
5055 * Now, wait for all mpVM callers to finish their work if there are still
5056 * some on other threads. NO methods that need mpVM (or initiate other calls
5057 * that need it) may be called after this point
5058 * ---------------------------------------------------------------------- */
5059
5060 if (mVMCallers > 0)
5061 {
5062 /* go to the destroying state to prevent from adding new callers */
5063 mVMDestroying = true;
5064
5065 /* lazy creation */
5066 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5067 RTSemEventCreate(&mVMZeroCallersSem);
5068
5069 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5070 mVMCallers));
5071
5072 alock.leave();
5073
5074 RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5075
5076 alock.enter();
5077 }
5078
5079 /* advance percent count */
5080 if (aProgress)
5081 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5082
5083 vrc = VINF_SUCCESS;
5084
5085 /*
5086 * Power off the VM if not already done that.
5087 * Leave the lock since EMT will call vmstateChangeCallback.
5088 *
5089 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5090 * VM-(guest-)initiated power off happened in parallel a ms before this
5091 * call. So far, we let this error pop up on the user's side.
5092 */
5093 if (!mVMPoweredOff)
5094 {
5095 LogFlowThisFunc(("Powering off the VM...\n"));
5096 alock.leave();
5097 vrc = VMR3PowerOff(mpVM);
5098 alock.enter();
5099 }
5100 else
5101 {
5102 /** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
5103 * off. */
5104 /* reset the flag for future re-use */
5105 mVMPoweredOff = false;
5106 }
5107
5108 /* advance percent count */
5109 if (aProgress)
5110 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5111
5112#ifdef VBOX_WITH_HGCM
5113# ifdef VBOX_WITH_GUEST_PROPS
5114 /*
5115 * Save all guest property store entries to the machine XML file
5116 * and hand controll over to VBoxSVC. Ignoring failure for now.
5117 */
5118 LogFlowThisFunc(("Moving Guest Properties to XML/VBoxSVC...\n"));
5119 bool fIsSaving = mMachineState == MachineState_Saving
5120 || mMachineState == MachineState_LiveSnapshotting;
5121 alock.leave();
5122 doMoveGuestPropertiesOnPowerOff(fIsSaving);
5123 alock.enter();
5124
5125 /* advance percent count */
5126 if (aProgress)
5127 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5128
5129# endif /* VBOX_WITH_GUEST_PROPS defined */
5130
5131 /* Shutdown HGCM services before destroying the VM. */
5132 if (mVMMDev)
5133 {
5134 LogFlowThisFunc(("Shutdown HGCM...\n"));
5135
5136 /* Leave the lock since EMT will call us back as addVMCaller() */
5137 alock.leave();
5138
5139 mVMMDev->hgcmShutdown();
5140
5141 alock.enter();
5142 }
5143
5144 /* advance percent count */
5145 if (aProgress)
5146 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5147
5148#endif /* VBOX_WITH_HGCM */
5149
5150 LogFlowThisFunc(("Ready for VM destruction.\n"));
5151
5152 /* If we are called from Console::uninit(), then try to destroy the VM even
5153 * on failure (this will most likely fail too, but what to do?..) */
5154 if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
5155 {
5156 /* If the machine has an USB controller, release all USB devices
5157 * (symmetric to the code in captureUSBDevices()) */
5158 bool fHasUSBController = false;
5159 {
5160 PPDMIBASE pBase;
5161 int vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
5162 if (RT_SUCCESS(vrc))
5163 {
5164 fHasUSBController = true;
5165 detachAllUSBDevices(false /* aDone */);
5166 }
5167 }
5168
5169 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5170 * this point). We leave the lock before calling VMR3Destroy() because
5171 * it will result into calling destructors of drivers associated with
5172 * Console children which may in turn try to lock Console (e.g. by
5173 * instantiating SafeVMPtr to access mpVM). It's safe here because
5174 * mVMDestroying is set which should prevent any activity. */
5175
5176 /* Set mpVM to NULL early just in case if some old code is not using
5177 * addVMCaller()/releaseVMCaller(). */
5178 PVM pVM = mpVM;
5179 mpVM = NULL;
5180
5181 LogFlowThisFunc(("Destroying the VM...\n"));
5182
5183 alock.leave();
5184
5185 vrc = VMR3Destroy(pVM);
5186
5187 /* take the lock again */
5188 alock.enter();
5189
5190 /* advance percent count */
5191 if (aProgress)
5192 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5193
5194 if (RT_SUCCESS(vrc))
5195 {
5196 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5197 mMachineState));
5198 /* Note: the Console-level machine state change happens on the
5199 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5200 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5201 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5202 * occurred yet. This is okay, because mMachineState is already
5203 * Stopping in this case, so any other attempt to call PowerDown()
5204 * will be rejected. */
5205 }
5206 else
5207 {
5208 /* bad bad bad, but what to do? */
5209 mpVM = pVM;
5210 rc = setError(VBOX_E_VM_ERROR,
5211 tr("Could not destroy the machine. (Error: %Rrc)"),
5212 vrc);
5213 }
5214
5215 /* Complete the detaching of the USB devices. */
5216 if (fHasUSBController)
5217 detachAllUSBDevices(true /* aDone */);
5218
5219 /* advance percent count */
5220 if (aProgress)
5221 aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
5222 }
5223 else
5224 {
5225 rc = setError(VBOX_E_VM_ERROR,
5226 tr("Could not power off the machine. (Error: %Rrc)"),
5227 vrc);
5228 }
5229
5230 /* Finished with destruction. Note that if something impossible happened and
5231 * we've failed to destroy the VM, mVMDestroying will remain true and
5232 * mMachineState will be something like Stopping, so most Console methods
5233 * will return an error to the caller. */
5234 if (mpVM == NULL)
5235 mVMDestroying = false;
5236
5237 if (SUCCEEDED(rc))
5238 {
5239 /* uninit dynamically allocated members of mCallbackData */
5240 if (mCallbackData.mpsc.valid)
5241 {
5242 if (mCallbackData.mpsc.shape != NULL)
5243 RTMemFree(mCallbackData.mpsc.shape);
5244 }
5245 memset(&mCallbackData, 0, sizeof(mCallbackData));
5246 }
5247
5248 /* complete the progress */
5249 if (aProgress)
5250 aProgress->notifyComplete(rc);
5251
5252 LogFlowThisFuncLeave();
5253 return rc;
5254}
5255
5256/**
5257 * @note Locks this object for writing.
5258 */
5259HRESULT Console::setMachineState(MachineState_T aMachineState,
5260 bool aUpdateServer /* = true */)
5261{
5262 AutoCaller autoCaller(this);
5263 AssertComRCReturnRC(autoCaller.rc());
5264
5265 AutoWriteLock alock(this);
5266
5267 HRESULT rc = S_OK;
5268
5269 if (mMachineState != aMachineState)
5270 {
5271 LogFlowThisFunc(("machineState=%d\n", aMachineState));
5272 mMachineState = aMachineState;
5273
5274 /// @todo (dmik)
5275 // possibly, we need to redo onStateChange() using the dedicated
5276 // Event thread, like it is done in VirtualBox. This will make it
5277 // much safer (no deadlocks possible if someone tries to use the
5278 // console from the callback), however, listeners will lose the
5279 // ability to synchronously react to state changes (is it really
5280 // necessary??)
5281 LogFlowThisFunc(("Doing onStateChange()...\n"));
5282 onStateChange(aMachineState);
5283 LogFlowThisFunc(("Done onStateChange()\n"));
5284
5285 if (aUpdateServer)
5286 {
5287 /* Server notification MUST be done from under the lock; otherwise
5288 * the machine state here and on the server might go out of sync
5289 * which can lead to various unexpected results (like the machine
5290 * state being >= MachineState_Running on the server, while the
5291 * session state is already SessionState_Closed at the same time
5292 * there).
5293 *
5294 * Cross-lock conditions should be carefully watched out: calling
5295 * UpdateState we will require Machine and SessionMachine locks
5296 * (remember that here we're holding the Console lock here, and also
5297 * all locks that have been entered by the thread before calling
5298 * this method).
5299 */
5300 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5301 rc = mControl->UpdateState(aMachineState);
5302 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5303 }
5304 }
5305
5306 return rc;
5307}
5308
5309/**
5310 * Searches for a shared folder with the given logical name
5311 * in the collection of shared folders.
5312 *
5313 * @param aName logical name of the shared folder
5314 * @param aSharedFolder where to return the found object
5315 * @param aSetError whether to set the error info if the folder is
5316 * not found
5317 * @return
5318 * S_OK when found or E_INVALIDARG when not found
5319 *
5320 * @note The caller must lock this object for writing.
5321 */
5322HRESULT Console::findSharedFolder(CBSTR aName,
5323 ComObjPtr<SharedFolder> &aSharedFolder,
5324 bool aSetError /* = false */)
5325{
5326 /* sanity check */
5327 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5328
5329 SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
5330 if (it != mSharedFolders.end())
5331 {
5332 aSharedFolder = it->second;
5333 return S_OK;
5334 }
5335
5336 if (aSetError)
5337 setError(VBOX_E_FILE_ERROR,
5338 tr("Could not find a shared folder named '%ls'."),
5339 aName);
5340
5341 return VBOX_E_FILE_ERROR;
5342}
5343
5344/**
5345 * Fetches the list of global or machine shared folders from the server.
5346 *
5347 * @param aGlobal true to fetch global folders.
5348 *
5349 * @note The caller must lock this object for writing.
5350 */
5351HRESULT Console::fetchSharedFolders(BOOL aGlobal)
5352{
5353 /* sanity check */
5354 AssertReturn(AutoCaller(this).state() == InInit ||
5355 isWriteLockOnCurrentThread(), E_FAIL);
5356
5357 /* protect mpVM (if not NULL) */
5358 AutoVMCallerQuietWeak autoVMCaller(this);
5359
5360 HRESULT rc = S_OK;
5361
5362 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5363
5364 if (aGlobal)
5365 {
5366 /// @todo grab & process global folders when they are done
5367 }
5368 else
5369 {
5370 SharedFolderDataMap oldFolders;
5371 if (online)
5372 oldFolders = mMachineSharedFolders;
5373
5374 mMachineSharedFolders.clear();
5375
5376 SafeIfaceArray<ISharedFolder> folders;
5377 rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
5378 AssertComRCReturnRC(rc);
5379
5380 for (size_t i = 0; i < folders.size(); ++i)
5381 {
5382 ComPtr<ISharedFolder> folder = folders[i];
5383
5384 Bstr name;
5385 Bstr hostPath;
5386 BOOL writable;
5387
5388 rc = folder->COMGETTER(Name)(name.asOutParam());
5389 CheckComRCBreakRC(rc);
5390 rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
5391 CheckComRCBreakRC(rc);
5392 rc = folder->COMGETTER(Writable)(&writable);
5393
5394 mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable)));
5395
5396 /* send changes to HGCM if the VM is running */
5397 /// @todo report errors as runtime warnings through VMSetError
5398 if (online)
5399 {
5400 SharedFolderDataMap::iterator it = oldFolders.find(name);
5401 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5402 {
5403 /* a new machine folder is added or
5404 * the existing machine folder is changed */
5405 if (mSharedFolders.find(name) != mSharedFolders.end())
5406 ; /* the console folder exists, nothing to do */
5407 else
5408 {
5409 /* remove the old machine folder (when changed)
5410 * or the global folder if any (when new) */
5411 if (it != oldFolders.end() ||
5412 mGlobalSharedFolders.find(name) !=
5413 mGlobalSharedFolders.end())
5414 rc = removeSharedFolder(name);
5415 /* create the new machine folder */
5416 rc = createSharedFolder(name, SharedFolderData(hostPath, writable));
5417 }
5418 }
5419 /* forget the processed (or identical) folder */
5420 if (it != oldFolders.end())
5421 oldFolders.erase(it);
5422
5423 rc = S_OK;
5424 }
5425 }
5426
5427 AssertComRCReturnRC(rc);
5428
5429 /* process outdated (removed) folders */
5430 /// @todo report errors as runtime warnings through VMSetError
5431 if (online)
5432 {
5433 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5434 it != oldFolders.end(); ++ it)
5435 {
5436 if (mSharedFolders.find(it->first) != mSharedFolders.end())
5437 ; /* the console folder exists, nothing to do */
5438 else
5439 {
5440 /* remove the outdated machine folder */
5441 rc = removeSharedFolder(it->first);
5442 /* create the global folder if there is any */
5443 SharedFolderDataMap::const_iterator git =
5444 mGlobalSharedFolders.find(it->first);
5445 if (git != mGlobalSharedFolders.end())
5446 rc = createSharedFolder(git->first, git->second);
5447 }
5448 }
5449
5450 rc = S_OK;
5451 }
5452 }
5453
5454 return rc;
5455}
5456
5457/**
5458 * Searches for a shared folder with the given name in the list of machine
5459 * shared folders and then in the list of the global shared folders.
5460 *
5461 * @param aName Name of the folder to search for.
5462 * @param aIt Where to store the pointer to the found folder.
5463 * @return @c true if the folder was found and @c false otherwise.
5464 *
5465 * @note The caller must lock this object for reading.
5466 */
5467bool Console::findOtherSharedFolder(IN_BSTR aName,
5468 SharedFolderDataMap::const_iterator &aIt)
5469{
5470 /* sanity check */
5471 AssertReturn(isWriteLockOnCurrentThread(), false);
5472
5473 /* first, search machine folders */
5474 aIt = mMachineSharedFolders.find(aName);
5475 if (aIt != mMachineSharedFolders.end())
5476 return true;
5477
5478 /* second, search machine folders */
5479 aIt = mGlobalSharedFolders.find(aName);
5480 if (aIt != mGlobalSharedFolders.end())
5481 return true;
5482
5483 return false;
5484}
5485
5486/**
5487 * Calls the HGCM service to add a shared folder definition.
5488 *
5489 * @param aName Shared folder name.
5490 * @param aHostPath Shared folder path.
5491 *
5492 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5493 * @note Doesn't lock anything.
5494 */
5495HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
5496{
5497 ComAssertRet(aName && *aName, E_FAIL);
5498 ComAssertRet(aData.mHostPath, E_FAIL);
5499
5500 /* sanity checks */
5501 AssertReturn(mpVM, E_FAIL);
5502 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5503
5504 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
5505 SHFLSTRING *pFolderName, *pMapName;
5506 size_t cbString;
5507
5508 Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
5509
5510 cbString = (RTUtf16Len(aData.mHostPath) + 1) * sizeof(RTUTF16);
5511 if (cbString >= UINT16_MAX)
5512 return setError(E_INVALIDARG, tr("The name is too long"));
5513 pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5514 Assert(pFolderName);
5515 memcpy(pFolderName->String.ucs2, aData.mHostPath, cbString);
5516
5517 pFolderName->u16Size = (uint16_t)cbString;
5518 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5519
5520 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
5521 parms[0].u.pointer.addr = pFolderName;
5522 parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5523
5524 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
5525 if (cbString >= UINT16_MAX)
5526 {
5527 RTMemFree(pFolderName);
5528 return setError(E_INVALIDARG, tr("The host path is too long"));
5529 }
5530 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5531 Assert(pMapName);
5532 memcpy(pMapName->String.ucs2, aName, cbString);
5533
5534 pMapName->u16Size = (uint16_t)cbString;
5535 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5536
5537 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
5538 parms[1].u.pointer.addr = pMapName;
5539 parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5540
5541 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
5542 parms[2].u.uint32 = aData.mWritable;
5543
5544 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
5545 SHFL_FN_ADD_MAPPING,
5546 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
5547 RTMemFree(pFolderName);
5548 RTMemFree(pMapName);
5549
5550 if (RT_FAILURE(vrc))
5551 return setError(E_FAIL,
5552 tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
5553 aName, aData.mHostPath.raw(), vrc);
5554
5555 return S_OK;
5556}
5557
5558/**
5559 * Calls the HGCM service to remove the shared folder definition.
5560 *
5561 * @param aName Shared folder name.
5562 *
5563 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5564 * @note Doesn't lock anything.
5565 */
5566HRESULT Console::removeSharedFolder(CBSTR aName)
5567{
5568 ComAssertRet(aName && *aName, E_FAIL);
5569
5570 /* sanity checks */
5571 AssertReturn(mpVM, E_FAIL);
5572 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5573
5574 VBOXHGCMSVCPARM parms;
5575 SHFLSTRING *pMapName;
5576 size_t cbString;
5577
5578 Log(("Removing shared folder '%ls'\n", aName));
5579
5580 cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
5581 if (cbString >= UINT16_MAX)
5582 return setError(E_INVALIDARG, tr("The name is too long"));
5583 pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
5584 Assert(pMapName);
5585 memcpy(pMapName->String.ucs2, aName, cbString);
5586
5587 pMapName->u16Size = (uint16_t)cbString;
5588 pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5589
5590 parms.type = VBOX_HGCM_SVC_PARM_PTR;
5591 parms.u.pointer.addr = pMapName;
5592 parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
5593
5594 int vrc = mVMMDev->hgcmHostCall("VBoxSharedFolders",
5595 SHFL_FN_REMOVE_MAPPING,
5596 1, &parms);
5597 RTMemFree(pMapName);
5598 if (RT_FAILURE(vrc))
5599 return setError(E_FAIL,
5600 tr("Could not remove the shared folder '%ls' (%Rrc)"),
5601 aName, vrc);
5602
5603 return S_OK;
5604}
5605
5606/**
5607 * VM state callback function. Called by the VMM
5608 * using its state machine states.
5609 *
5610 * Primarily used to handle VM initiated power off, suspend and state saving,
5611 * but also for doing termination completed work (VMSTATE_TERMINATE).
5612 *
5613 * In general this function is called in the context of the EMT.
5614 *
5615 * @param aVM The VM handle.
5616 * @param aState The new state.
5617 * @param aOldState The old state.
5618 * @param aUser The user argument (pointer to the Console object).
5619 *
5620 * @note Locks the Console object for writing.
5621 */
5622DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
5623 VMSTATE aState,
5624 VMSTATE aOldState,
5625 void *aUser)
5626{
5627 LogFlowFunc(("Changing state from %d to %d (aVM=%p)\n",
5628 aOldState, aState, aVM));
5629
5630 Console *that = static_cast<Console *>(aUser);
5631 AssertReturnVoid(that);
5632
5633 AutoCaller autoCaller(that);
5634
5635 /* Note that we must let this method proceed even if Console::uninit() has
5636 * been already called. In such case this VMSTATE change is a result of:
5637 * 1) powerDown() called from uninit() itself, or
5638 * 2) VM-(guest-)initiated power off. */
5639 AssertReturnVoid( autoCaller.isOk()
5640 || autoCaller.state() == InUninit);
5641
5642 switch (aState)
5643 {
5644 /*
5645 * The VM has terminated
5646 */
5647 case VMSTATE_OFF:
5648 {
5649 AutoWriteLock alock(that);
5650
5651 if (that->mVMStateChangeCallbackDisabled)
5652 break;
5653
5654 /* Do we still think that it is running? It may happen if this is a
5655 * VM-(guest-)initiated shutdown/poweroff.
5656 */
5657 if ( that->mMachineState != MachineState_Stopping
5658 && that->mMachineState != MachineState_Saving
5659 && that->mMachineState != MachineState_Restoring
5660 && that->mMachineState != MachineState_TeleportingIn
5661 && that->mMachineState != MachineState_LiveSnapshotting
5662 && that->mMachineState != MachineState_Teleporting
5663 && that->mMachineState != MachineState_TeleportingPausedVM
5664 )
5665 {
5666 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
5667
5668 /* prevent powerDown() from calling VMR3PowerOff() again */
5669 Assert(that->mVMPoweredOff == false);
5670 that->mVMPoweredOff = true;
5671
5672 /* we are stopping now */
5673 that->setMachineState(MachineState_Stopping);
5674
5675 /* Setup task object and thread to carry out the operation
5676 * asynchronously (if we call powerDown() right here but there
5677 * is one or more mpVM callers (added with addVMCaller()) we'll
5678 * deadlock).
5679 */
5680 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
5681 true /* aUsesVMPtr */));
5682
5683 /* If creating a task is falied, this can currently mean one of
5684 * two: either Console::uninit() has been called just a ms
5685 * before (so a powerDown() call is already on the way), or
5686 * powerDown() itself is being already executed. Just do
5687 * nothing.
5688 */
5689 if (!task->isOk())
5690 {
5691 LogFlowFunc(("Console is already being uninitialized.\n"));
5692 break;
5693 }
5694
5695 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
5696 (void *) task.get(), 0,
5697 RTTHREADTYPE_MAIN_WORKER, 0,
5698 "VMPowerDown");
5699 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
5700
5701 /* task is now owned by powerDownThread(), so release it */
5702 task.release();
5703 }
5704 break;
5705 }
5706
5707 /* The VM has been completely destroyed.
5708 *
5709 * Note: This state change can happen at two points:
5710 * 1) At the end of VMR3Destroy() if it was not called from EMT.
5711 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
5712 * called by EMT.
5713 */
5714 case VMSTATE_TERMINATED:
5715 {
5716 AutoWriteLock alock(that);
5717
5718 if (that->mVMStateChangeCallbackDisabled)
5719 break;
5720
5721 /* Terminate host interface networking. If aVM is NULL, we've been
5722 * manually called from powerUpThread() either before calling
5723 * VMR3Create() or after VMR3Create() failed, so no need to touch
5724 * networking.
5725 */
5726 if (aVM)
5727 that->powerDownHostInterfaces();
5728
5729 /* From now on the machine is officially powered down or remains in
5730 * the Saved state.
5731 */
5732 switch (that->mMachineState)
5733 {
5734 default:
5735 AssertFailed();
5736 /* fall through */
5737 case MachineState_Stopping:
5738 /* successfully powered down */
5739 that->setMachineState(MachineState_PoweredOff);
5740 break;
5741 case MachineState_Saving:
5742 /* successfully saved (note that the machine is already in
5743 * the Saved state on the server due to EndSavingState()
5744 * called from saveStateThread(), so only change the local
5745 * state) */
5746 that->setMachineStateLocally(MachineState_Saved);
5747 break;
5748 case MachineState_Starting:
5749 /* failed to start, but be patient: set back to PoweredOff
5750 * (for similarity with the below) */
5751 that->setMachineState(MachineState_PoweredOff);
5752 break;
5753 case MachineState_Restoring:
5754 /* failed to load the saved state file, but be patient: set
5755 * back to Saved (to preserve the saved state file) */
5756 that->setMachineState(MachineState_Saved);
5757 break;
5758 case MachineState_TeleportingIn:
5759 /* Teleportation failed or was cancelled. Back to powered off. */
5760 that->setMachineState(MachineState_PoweredOff);
5761 break;
5762 case MachineState_Teleporting:
5763 case MachineState_TeleportingPausedVM:
5764 /* Successfully teleported the VM. */
5765 that->setMachineState(MachineState_Teleported);
5766 break;
5767 }
5768 break;
5769 }
5770
5771 case VMSTATE_SUSPENDED:
5772 {
5773 if (aOldState == VMSTATE_SUSPENDING)
5774 {
5775 AutoWriteLock alock(that);
5776
5777 if (that->mVMStateChangeCallbackDisabled)
5778 break;
5779
5780 /* Change the machine state from Running to Paused. */
5781/** @todo Live Migration: Deal with Pause happening before VMR3Teleport! */
5782 AssertBreak(that->mMachineState == MachineState_Running);
5783 that->setMachineState(MachineState_Paused);
5784 }
5785 break;
5786 }
5787
5788 case VMSTATE_SUSPENDED_LS:
5789 case VMSTATE_SUSPENDED_EXT_LS:
5790 {
5791 AutoWriteLock alock(that);
5792 if (that->mVMStateChangeCallbackDisabled)
5793 break;
5794 if (that->mMachineState == MachineState_Teleporting)
5795 that->setMachineState(MachineState_TeleportingPausedVM);
5796 else if (that->mMachineState == MachineState_LiveSnapshotting)
5797 that->setMachineState(MachineState_Saving);
5798 else
5799 AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
5800 break;
5801 }
5802
5803 case VMSTATE_RUNNING:
5804 {
5805 if ( aOldState == VMSTATE_POWERING_ON
5806 || aOldState == VMSTATE_RESUMING)
5807 {
5808 AutoWriteLock alock(that);
5809
5810 if (that->mVMStateChangeCallbackDisabled)
5811 break;
5812
5813 /* Change the machine state from Starting, Restoring or Paused
5814 * to Running */
5815 Assert( ( ( that->mMachineState == MachineState_Starting
5816 || that->mMachineState == MachineState_Paused)
5817 && aOldState == VMSTATE_POWERING_ON)
5818 || ( ( that->mMachineState == MachineState_Restoring
5819 || that->mMachineState == MachineState_TeleportingIn
5820 || that->mMachineState == MachineState_Paused)
5821 && aOldState == VMSTATE_RESUMING));
5822
5823 that->setMachineState(MachineState_Running);
5824 }
5825
5826 break;
5827 }
5828
5829 case VMSTATE_RUNNING_LS:
5830 AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
5831 || that->mMachineState == MachineState_Teleporting,
5832 ("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
5833 break;
5834
5835 case VMSTATE_FATAL_ERROR:
5836 {
5837 AutoWriteLock alock(that);
5838
5839 if (that->mVMStateChangeCallbackDisabled)
5840 break;
5841
5842 /* Fatal errors are only for running VMs. */
5843 Assert(Global::IsOnline(that->mMachineState));
5844
5845 /* Note! 'Pause' is used here in want of something better. There
5846 * are currently only two places where fatal errors might be
5847 * raised, so it is not worth adding a new externally
5848 * visible state for this yet. */
5849 that->setMachineState(MachineState_Paused);
5850 break;
5851 }
5852
5853 case VMSTATE_GURU_MEDITATION:
5854 {
5855 AutoWriteLock alock(that);
5856
5857 if (that->mVMStateChangeCallbackDisabled)
5858 break;
5859
5860 /* Guru are only for running VMs */
5861 Assert(Global::IsOnline(that->mMachineState));
5862
5863 that->setMachineState(MachineState_Stuck);
5864 break;
5865 }
5866
5867 default: /* shut up gcc */
5868 break;
5869 }
5870}
5871
5872#ifdef VBOX_WITH_USB
5873
5874/**
5875 * Sends a request to VMM to attach the given host device.
5876 * After this method succeeds, the attached device will appear in the
5877 * mUSBDevices collection.
5878 *
5879 * @param aHostDevice device to attach
5880 *
5881 * @note Synchronously calls EMT.
5882 * @note Must be called from under this object's lock.
5883 */
5884HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
5885{
5886 AssertReturn(aHostDevice, E_FAIL);
5887 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5888
5889 /* still want a lock object because we need to leave it */
5890 AutoWriteLock alock(this);
5891
5892 HRESULT hrc;
5893
5894 /*
5895 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
5896 * method in EMT (using usbAttachCallback()).
5897 */
5898 Bstr BstrAddress;
5899 hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
5900 ComAssertComRCRetRC(hrc);
5901
5902 Utf8Str Address(BstrAddress);
5903
5904 Bstr id;
5905 hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
5906 ComAssertComRCRetRC(hrc);
5907 Guid uuid(id);
5908
5909 BOOL fRemote = FALSE;
5910 hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
5911 ComAssertComRCRetRC(hrc);
5912
5913 /* protect mpVM */
5914 AutoVMCaller autoVMCaller(this);
5915 CheckComRCReturnRC(autoVMCaller.rc());
5916
5917 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
5918 Address.raw(), uuid.ptr()));
5919
5920 /* leave the lock before a VMR3* call (EMT will call us back)! */
5921 alock.leave();
5922
5923/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
5924 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
5925 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
5926
5927 /* restore the lock */
5928 alock.enter();
5929
5930 /* hrc is S_OK here */
5931
5932 if (RT_FAILURE(vrc))
5933 {
5934 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
5935 Address.raw(), uuid.ptr(), vrc));
5936
5937 switch (vrc)
5938 {
5939 case VERR_VUSB_NO_PORTS:
5940 hrc = setError(E_FAIL,
5941 tr("Failed to attach the USB device. (No available ports on the USB controller)."));
5942 break;
5943 case VERR_VUSB_USBFS_PERMISSION:
5944 hrc = setError(E_FAIL,
5945 tr("Not permitted to open the USB device, check usbfs options"));
5946 break;
5947 default:
5948 hrc = setError(E_FAIL,
5949 tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
5950 vrc);
5951 break;
5952 }
5953 }
5954
5955 return hrc;
5956}
5957
5958/**
5959 * USB device attach callback used by AttachUSBDevice().
5960 * Note that AttachUSBDevice() doesn't return until this callback is executed,
5961 * so we don't use AutoCaller and don't care about reference counters of
5962 * interface pointers passed in.
5963 *
5964 * @thread EMT
5965 * @note Locks the console object for writing.
5966 */
5967//static
5968DECLCALLBACK(int)
5969Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
5970{
5971 LogFlowFuncEnter();
5972 LogFlowFunc(("that={%p}\n", that));
5973
5974 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
5975
5976 void *pvRemoteBackend = NULL;
5977 if (aRemote)
5978 {
5979 RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
5980 Guid guid(*aUuid);
5981
5982 pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
5983 if (!pvRemoteBackend)
5984 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
5985 }
5986
5987 USHORT portVersion = 1;
5988 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
5989 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
5990 Assert(portVersion == 1 || portVersion == 2);
5991
5992 int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
5993 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
5994 if (RT_SUCCESS(vrc))
5995 {
5996 /* Create a OUSBDevice and add it to the device list */
5997 ComObjPtr<OUSBDevice> device;
5998 device.createObject();
5999 HRESULT hrc = device->init(aHostDevice);
6000 AssertComRC(hrc);
6001
6002 AutoWriteLock alock(that);
6003 that->mUSBDevices.push_back(device);
6004 LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
6005
6006 /* notify callbacks */
6007 that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
6008 }
6009
6010 LogFlowFunc(("vrc=%Rrc\n", vrc));
6011 LogFlowFuncLeave();
6012 return vrc;
6013}
6014
6015/**
6016 * Sends a request to VMM to detach the given host device. After this method
6017 * succeeds, the detached device will disappear from the mUSBDevices
6018 * collection.
6019 *
6020 * @param aIt Iterator pointing to the device to detach.
6021 *
6022 * @note Synchronously calls EMT.
6023 * @note Must be called from under this object's lock.
6024 */
6025HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
6026{
6027 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6028
6029 /* still want a lock object because we need to leave it */
6030 AutoWriteLock alock(this);
6031
6032 /* protect mpVM */
6033 AutoVMCaller autoVMCaller(this);
6034 CheckComRCReturnRC(autoVMCaller.rc());
6035
6036 /* if the device is attached, then there must at least one USB hub. */
6037 AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
6038
6039 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
6040 (*aIt)->id().raw()));
6041
6042 /* leave the lock before a VMR3* call (EMT will call us back)! */
6043 alock.leave();
6044
6045/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6046 int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
6047 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6048 ComAssertRCRet(vrc, E_FAIL);
6049
6050 return S_OK;
6051}
6052
6053/**
6054 * USB device detach callback used by DetachUSBDevice().
6055 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6056 * so we don't use AutoCaller and don't care about reference counters of
6057 * interface pointers passed in.
6058 *
6059 * @thread EMT
6060 * @note Locks the console object for writing.
6061 */
6062//static
6063DECLCALLBACK(int)
6064Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6065{
6066 LogFlowFuncEnter();
6067 LogFlowFunc(("that={%p}\n", that));
6068
6069 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6070 ComObjPtr<OUSBDevice> device = **aIt;
6071
6072 /*
6073 * If that was a remote device, release the backend pointer.
6074 * The pointer was requested in usbAttachCallback.
6075 */
6076 BOOL fRemote = FALSE;
6077
6078 HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
6079 ComAssertComRC(hrc2);
6080
6081 if (fRemote)
6082 {
6083 Guid guid(*aUuid);
6084 that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
6085 }
6086
6087 int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
6088
6089 if (RT_SUCCESS(vrc))
6090 {
6091 AutoWriteLock alock(that);
6092
6093 /* Remove the device from the collection */
6094 that->mUSBDevices.erase(*aIt);
6095 LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
6096
6097 /* notify callbacks */
6098 that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
6099 }
6100
6101 LogFlowFunc(("vrc=%Rrc\n", vrc));
6102 LogFlowFuncLeave();
6103 return vrc;
6104}
6105
6106#endif /* VBOX_WITH_USB */
6107#if (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT)
6108
6109/**
6110 * Helper function to handle host interface device creation and attachment.
6111 *
6112 * @param networkAdapter the network adapter which attachment should be reset
6113 * @return COM status code
6114 *
6115 * @note The caller must lock this object for writing.
6116 *
6117 * @todo Move this back into the driver!
6118 */
6119HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6120{
6121 LogFlowThisFunc(("\n"));
6122 /* sanity check */
6123 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6124
6125# ifdef VBOX_STRICT
6126 /* paranoia */
6127 NetworkAttachmentType_T attachment;
6128 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6129 Assert(attachment == NetworkAttachmentType_Bridged);
6130# endif /* VBOX_STRICT */
6131
6132 HRESULT rc = S_OK;
6133
6134 ULONG slot = 0;
6135 rc = networkAdapter->COMGETTER(Slot)(&slot);
6136 AssertComRC(rc);
6137
6138# ifdef RT_OS_LINUX
6139 /*
6140 * Allocate a host interface device
6141 */
6142 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6143 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6144 if (RT_SUCCESS(rcVBox))
6145 {
6146 /*
6147 * Set/obtain the tap interface.
6148 */
6149 struct ifreq IfReq;
6150 memset(&IfReq, 0, sizeof(IfReq));
6151 /* The name of the TAP interface we are using */
6152 Bstr tapDeviceName;
6153 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6154 if (FAILED(rc))
6155 tapDeviceName.setNull(); /* Is this necessary? */
6156 if (tapDeviceName.isEmpty())
6157 {
6158 LogRel(("No TAP device name was supplied.\n"));
6159 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6160 }
6161
6162 if (SUCCEEDED(rc))
6163 {
6164 /* If we are using a static TAP device then try to open it. */
6165 Utf8Str str(tapDeviceName);
6166 if (str.length() <= sizeof(IfReq.ifr_name))
6167 strcpy(IfReq.ifr_name, str.raw());
6168 else
6169 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6170 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6171 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6172 if (rcVBox != 0)
6173 {
6174 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6175 rc = setError(E_FAIL,
6176 tr("Failed to open the host network interface %ls"),
6177 tapDeviceName.raw());
6178 }
6179 }
6180 if (SUCCEEDED(rc))
6181 {
6182 /*
6183 * Make it pollable.
6184 */
6185 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6186 {
6187 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6188 /*
6189 * Here is the right place to communicate the TAP file descriptor and
6190 * the host interface name to the server if/when it becomes really
6191 * necessary.
6192 */
6193 maTAPDeviceName[slot] = tapDeviceName;
6194 rcVBox = VINF_SUCCESS;
6195 }
6196 else
6197 {
6198 int iErr = errno;
6199
6200 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6201 rcVBox = VERR_HOSTIF_BLOCKING;
6202 rc = setError(E_FAIL,
6203 tr("could not set up the host networking device for non blocking access: %s"),
6204 strerror(errno));
6205 }
6206 }
6207 }
6208 else
6209 {
6210 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6211 switch (rcVBox)
6212 {
6213 case VERR_ACCESS_DENIED:
6214 /* will be handled by our caller */
6215 rc = rcVBox;
6216 break;
6217 default:
6218 rc = setError(E_FAIL,
6219 tr("Could not set up the host networking device: %Rrc"),
6220 rcVBox);
6221 break;
6222 }
6223 }
6224
6225# elif defined(RT_OS_FREEBSD)
6226 /*
6227 * Set/obtain the tap interface.
6228 */
6229 /* The name of the TAP interface we are using */
6230 Bstr tapDeviceName;
6231 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6232 if (FAILED(rc))
6233 tapDeviceName.setNull(); /* Is this necessary? */
6234 if (tapDeviceName.isEmpty())
6235 {
6236 LogRel(("No TAP device name was supplied.\n"));
6237 rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
6238 }
6239 char szTapdev[1024] = "/dev/";
6240 /* If we are using a static TAP device then try to open it. */
6241 Utf8Str str(tapDeviceName);
6242 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6243 strcat(szTapdev, str.raw());
6244 else
6245 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6246 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6247 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6248
6249 if (RT_SUCCESS(rcVBox))
6250 maTAPDeviceName[slot] = tapDeviceName;
6251 else
6252 {
6253 switch (rcVBox)
6254 {
6255 case VERR_ACCESS_DENIED:
6256 /* will be handled by our caller */
6257 rc = rcVBox;
6258 break;
6259 default:
6260 rc = setError(E_FAIL,
6261 tr("Failed to open the host network interface %ls"),
6262 tapDeviceName.raw());
6263 break;
6264 }
6265 }
6266# else
6267# error "huh?"
6268# endif
6269 /* in case of failure, cleanup. */
6270 if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
6271 {
6272 LogRel(("General failure attaching to host interface\n"));
6273 rc = setError(E_FAIL,
6274 tr("General failure attaching to host interface"));
6275 }
6276 LogFlowThisFunc(("rc=%d\n", rc));
6277 return rc;
6278}
6279
6280
6281/**
6282 * Helper function to handle detachment from a host interface
6283 *
6284 * @param networkAdapter the network adapter which attachment should be reset
6285 * @return COM status code
6286 *
6287 * @note The caller must lock this object for writing.
6288 *
6289 * @todo Move this back into the driver!
6290 */
6291HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6292{
6293 /* sanity check */
6294 LogFlowThisFunc(("\n"));
6295 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6296
6297 HRESULT rc = S_OK;
6298# ifdef VBOX_STRICT
6299 /* paranoia */
6300 NetworkAttachmentType_T attachment;
6301 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6302 Assert(attachment == NetworkAttachmentType_Bridged);
6303# endif /* VBOX_STRICT */
6304
6305 ULONG slot = 0;
6306 rc = networkAdapter->COMGETTER(Slot)(&slot);
6307 AssertComRC(rc);
6308
6309 /* is there an open TAP device? */
6310 if (maTapFD[slot] != NIL_RTFILE)
6311 {
6312 /*
6313 * Close the file handle.
6314 */
6315 Bstr tapDeviceName, tapTerminateApplication;
6316 bool isStatic = true;
6317 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6318 if (FAILED(rc) || tapDeviceName.isEmpty())
6319 {
6320 /* If the name is empty, this is a dynamic TAP device, so close it now,
6321 so that the termination script can remove the interface. Otherwise we still
6322 need the FD to pass to the termination script. */
6323 isStatic = false;
6324 int rcVBox = RTFileClose(maTapFD[slot]);
6325 AssertRC(rcVBox);
6326 maTapFD[slot] = NIL_RTFILE;
6327 }
6328 if (isStatic)
6329 {
6330 /* If we are using a static TAP device, we close it now, after having called the
6331 termination script. */
6332 int rcVBox = RTFileClose(maTapFD[slot]);
6333 AssertRC(rcVBox);
6334 }
6335 /* the TAP device name and handle are no longer valid */
6336 maTapFD[slot] = NIL_RTFILE;
6337 maTAPDeviceName[slot] = "";
6338 }
6339 LogFlowThisFunc(("returning %d\n", rc));
6340 return rc;
6341}
6342
6343#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6344
6345/**
6346 * Called at power down to terminate host interface networking.
6347 *
6348 * @note The caller must lock this object for writing.
6349 */
6350HRESULT Console::powerDownHostInterfaces()
6351{
6352 LogFlowThisFunc(("\n"));
6353
6354 /* sanity check */
6355 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6356
6357 /*
6358 * host interface termination handling
6359 */
6360 HRESULT rc;
6361 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6362 {
6363 ComPtr<INetworkAdapter> networkAdapter;
6364 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6365 CheckComRCBreakRC(rc);
6366
6367 BOOL enabled = FALSE;
6368 networkAdapter->COMGETTER(Enabled)(&enabled);
6369 if (!enabled)
6370 continue;
6371
6372 NetworkAttachmentType_T attachment;
6373 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6374 if (attachment == NetworkAttachmentType_Bridged)
6375 {
6376#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6377 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6378 if (FAILED(rc2) && SUCCEEDED(rc))
6379 rc = rc2;
6380#endif
6381 }
6382 }
6383
6384 return rc;
6385}
6386
6387
6388/**
6389 * Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
6390 * and VMR3Teleport.
6391 *
6392 * @param pVM The VM handle.
6393 * @param uPercent Completetion precentage (0-100).
6394 * @param pvUser Pointer to the VMProgressTask structure.
6395 * @return VINF_SUCCESS.
6396 */
6397/*static*/
6398DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
6399{
6400 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6401 AssertReturn(task, VERR_INVALID_PARAMETER);
6402
6403 /* update the progress object */
6404 if (task->mProgress)
6405 task->mProgress->SetCurrentOperationProgress(uPercent);
6406
6407 return VINF_SUCCESS;
6408}
6409
6410/**
6411 * VM error callback function. Called by the various VM components.
6412 *
6413 * @param pVM VM handle. Can be NULL if an error occurred before
6414 * successfully creating a VM.
6415 * @param pvUser Pointer to the VMProgressTask structure.
6416 * @param rc VBox status code.
6417 * @param pszFormat Printf-like error message.
6418 * @param args Various number of arguments for the error message.
6419 *
6420 * @thread EMT, VMPowerUp...
6421 *
6422 * @note The VMProgressTask structure modified by this callback is not thread
6423 * safe.
6424 */
6425/* static */ DECLCALLBACK(void)
6426Console::setVMErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6427 const char *pszFormat, va_list args)
6428{
6429 VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
6430 AssertReturnVoid(task);
6431
6432 /* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */
6433 va_list va2;
6434 va_copy(va2, args); /* Have to make a copy here or GCC will break. */
6435
6436 /* append to the existing error message if any */
6437 if (task->mErrorMsg.length())
6438 task->mErrorMsg = Utf8StrFmt("%s.\n%N (%Rrc)", task->mErrorMsg.raw(),
6439 pszFormat, &va2, rc, rc);
6440 else
6441 task->mErrorMsg = Utf8StrFmt("%N (%Rrc)",
6442 pszFormat, &va2, rc, rc);
6443
6444 va_end (va2);
6445}
6446
6447/**
6448 * VM runtime error callback function.
6449 * See VMSetRuntimeError for the detailed description of parameters.
6450 *
6451 * @param pVM The VM handle.
6452 * @param pvUser The user argument.
6453 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6454 * @param pszErrorId Error ID string.
6455 * @param pszFormat Error message format string.
6456 * @param va Error message arguments.
6457 * @thread EMT.
6458 */
6459/* static */ DECLCALLBACK(void)
6460Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
6461 const char *pszErrorId,
6462 const char *pszFormat, va_list va)
6463{
6464 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6465 LogFlowFuncEnter();
6466
6467 Console *that = static_cast<Console *>(pvUser);
6468 AssertReturnVoid(that);
6469
6470 Utf8Str message = Utf8StrFmtVA(pszFormat, va);
6471
6472 LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
6473 fFatal, pszErrorId, message.raw()));
6474
6475 that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId), Bstr(message));
6476
6477 LogFlowFuncLeave();
6478}
6479
6480/**
6481 * Captures USB devices that match filters of the VM.
6482 * Called at VM startup.
6483 *
6484 * @param pVM The VM handle.
6485 *
6486 * @note The caller must lock this object for writing.
6487 */
6488HRESULT Console::captureUSBDevices(PVM pVM)
6489{
6490 LogFlowThisFunc(("\n"));
6491
6492 /* sanity check */
6493 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
6494
6495 /* If the machine has an USB controller, ask the USB proxy service to
6496 * capture devices */
6497 PPDMIBASE pBase;
6498 int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
6499 if (RT_SUCCESS(vrc))
6500 {
6501 /* leave the lock before calling Host in VBoxSVC since Host may call
6502 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6503 * produce an inter-process dead-lock otherwise. */
6504 AutoWriteLock alock(this);
6505 alock.leave();
6506
6507 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6508 ComAssertComRCRetRC(hrc);
6509 }
6510 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6511 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6512 vrc = VINF_SUCCESS;
6513 else
6514 AssertRC(vrc);
6515
6516 return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
6517}
6518
6519
6520/**
6521 * Detach all USB device which are attached to the VM for the
6522 * purpose of clean up and such like.
6523 *
6524 * @note The caller must lock this object for writing.
6525 */
6526void Console::detachAllUSBDevices(bool aDone)
6527{
6528 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6529
6530 /* sanity check */
6531 AssertReturnVoid(isWriteLockOnCurrentThread());
6532
6533 mUSBDevices.clear();
6534
6535 /* leave the lock before calling Host in VBoxSVC since Host may call
6536 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6537 * produce an inter-process dead-lock otherwise. */
6538 AutoWriteLock alock(this);
6539 alock.leave();
6540
6541 mControl->DetachAllUSBDevices(aDone);
6542}
6543
6544/**
6545 * @note Locks this object for writing.
6546 */
6547void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6548{
6549 LogFlowThisFuncEnter();
6550 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
6551
6552 AutoCaller autoCaller(this);
6553 if (!autoCaller.isOk())
6554 {
6555 /* Console has been already uninitialized, deny request */
6556 AssertMsgFailed(("Console is already uninitialized\n"));
6557 LogFlowThisFunc(("Console is already uninitialized\n"));
6558 LogFlowThisFuncLeave();
6559 return;
6560 }
6561
6562 AutoWriteLock alock(this);
6563
6564 /*
6565 * Mark all existing remote USB devices as dirty.
6566 */
6567 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6568 while (it != mRemoteUSBDevices.end())
6569 {
6570 (*it)->dirty(true);
6571 ++ it;
6572 }
6573
6574 /*
6575 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
6576 */
6577 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
6578 VRDPUSBDEVICEDESC *e = pDevList;
6579
6580 /* The cbDevList condition must be checked first, because the function can
6581 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
6582 */
6583 while (cbDevList >= 2 && e->oNext)
6584 {
6585 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
6586 e->idVendor, e->idProduct,
6587 e->oProduct? (char *)e + e->oProduct: ""));
6588
6589 bool fNewDevice = true;
6590
6591 it = mRemoteUSBDevices.begin();
6592 while (it != mRemoteUSBDevices.end())
6593 {
6594 if ((*it)->devId() == e->id
6595 && (*it)->clientId() == u32ClientId)
6596 {
6597 /* The device is already in the list. */
6598 (*it)->dirty(false);
6599 fNewDevice = false;
6600 break;
6601 }
6602
6603 ++ it;
6604 }
6605
6606 if (fNewDevice)
6607 {
6608 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
6609 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
6610
6611 /* Create the device object and add the new device to list. */
6612 ComObjPtr<RemoteUSBDevice> device;
6613 device.createObject();
6614 device->init(u32ClientId, e);
6615
6616 mRemoteUSBDevices.push_back(device);
6617
6618 /* Check if the device is ok for current USB filters. */
6619 BOOL fMatched = FALSE;
6620 ULONG fMaskedIfs = 0;
6621
6622 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
6623
6624 AssertComRC(hrc);
6625
6626 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
6627
6628 if (fMatched)
6629 {
6630 hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
6631
6632 /// @todo (r=dmik) warning reporting subsystem
6633
6634 if (hrc == S_OK)
6635 {
6636 LogFlowThisFunc(("Device attached\n"));
6637 device->captured(true);
6638 }
6639 }
6640 }
6641
6642 if (cbDevList < e->oNext)
6643 {
6644 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
6645 cbDevList, e->oNext));
6646 break;
6647 }
6648
6649 cbDevList -= e->oNext;
6650
6651 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
6652 }
6653
6654 /*
6655 * Remove dirty devices, that is those which are not reported by the server anymore.
6656 */
6657 for (;;)
6658 {
6659 ComObjPtr<RemoteUSBDevice> device;
6660
6661 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6662 while (it != mRemoteUSBDevices.end())
6663 {
6664 if ((*it)->dirty())
6665 {
6666 device = *it;
6667 break;
6668 }
6669
6670 ++ it;
6671 }
6672
6673 if (!device)
6674 {
6675 break;
6676 }
6677
6678 USHORT vendorId = 0;
6679 device->COMGETTER(VendorId)(&vendorId);
6680
6681 USHORT productId = 0;
6682 device->COMGETTER(ProductId)(&productId);
6683
6684 Bstr product;
6685 device->COMGETTER(Product)(product.asOutParam());
6686
6687 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
6688 vendorId, productId, product.raw()));
6689
6690 /* Detach the device from VM. */
6691 if (device->captured())
6692 {
6693 Bstr uuid;
6694 device->COMGETTER(Id)(uuid.asOutParam());
6695 onUSBDeviceDetach(uuid, NULL);
6696 }
6697
6698 /* And remove it from the list. */
6699 mRemoteUSBDevices.erase(it);
6700 }
6701
6702 LogFlowThisFuncLeave();
6703}
6704
6705/**
6706 * Thread function which starts the VM (also from saved state) and
6707 * track progress.
6708 *
6709 * @param Thread The thread id.
6710 * @param pvUser Pointer to a VMPowerUpTask structure.
6711 * @return VINF_SUCCESS (ignored).
6712 *
6713 * @note Locks the Console object for writing.
6714 */
6715/*static*/
6716DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
6717{
6718 LogFlowFuncEnter();
6719
6720 std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
6721 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
6722
6723 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
6724 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
6725
6726#if defined(RT_OS_WINDOWS)
6727 {
6728 /* initialize COM */
6729 HRESULT hrc = CoInitializeEx(NULL,
6730 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
6731 COINIT_SPEED_OVER_MEMORY);
6732 LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
6733 }
6734#endif
6735
6736 HRESULT rc = S_OK;
6737 int vrc = VINF_SUCCESS;
6738
6739 /* Set up a build identifier so that it can be seen from core dumps what
6740 * exact build was used to produce the core. */
6741 static char saBuildID[40];
6742 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
6743 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
6744
6745 ComObjPtr<Console> console = task->mConsole;
6746
6747 /* Note: no need to use addCaller() because VMPowerUpTask does that */
6748
6749 /* The lock is also used as a signal from the task initiator (which
6750 * releases it only after RTThreadCreate()) that we can start the job */
6751 AutoWriteLock alock(console);
6752
6753 /* sanity */
6754 Assert(console->mpVM == NULL);
6755
6756 try
6757 {
6758 /* wait for auto reset ops to complete so that we can successfully lock
6759 * the attached hard disks by calling LockMedia() below */
6760 for (VMPowerUpTask::ProgressList::const_iterator
6761 it = task->hardDiskProgresses.begin();
6762 it != task->hardDiskProgresses.end(); ++ it)
6763 {
6764 HRESULT rc2 = (*it)->WaitForCompletion(-1);
6765 AssertComRC(rc2);
6766 }
6767
6768 /*
6769 * Lock attached media. This method will also check their accessibility.
6770 * If we're a teleporter, we'll have to postpone this action so we can
6771 * migrate between local processes.
6772 *
6773 * Note! The media will be unlocked automatically by
6774 * SessionMachine::setMachineState() when the VM is powered down.
6775 */
6776 if (!task->mTeleporterEnabled)
6777 {
6778 rc = console->mControl->LockMedia();
6779 CheckComRCThrowRC(rc);
6780 }
6781
6782#ifdef VBOX_WITH_VRDP
6783
6784 /* Create the VRDP server. In case of headless operation, this will
6785 * also create the framebuffer, required at VM creation.
6786 */
6787 ConsoleVRDPServer *server = console->consoleVRDPServer();
6788 Assert(server);
6789
6790 /* Does VRDP server call Console from the other thread?
6791 * Not sure (and can change), so leave the lock just in case.
6792 */
6793 alock.leave();
6794 vrc = server->Launch();
6795 alock.enter();
6796
6797 if (vrc == VERR_NET_ADDRESS_IN_USE)
6798 {
6799 Utf8Str errMsg;
6800 Bstr bstr;
6801 console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
6802 Utf8Str ports = bstr;
6803 errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
6804 ports.raw());
6805 LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
6806 vrc, errMsg.raw()));
6807 }
6808 else if (RT_FAILURE(vrc))
6809 {
6810 Utf8Str errMsg;
6811 switch (vrc)
6812 {
6813 case VERR_FILE_NOT_FOUND:
6814 {
6815 errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
6816 break;
6817 }
6818 default:
6819 errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
6820 vrc);
6821 }
6822 LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
6823 vrc, errMsg.raw()));
6824 throw setError(E_FAIL, errMsg.c_str());
6825 }
6826
6827#endif /* VBOX_WITH_VRDP */
6828
6829 ComPtr<IMachine> pMachine = console->machine();
6830 ULONG cCpus = 1;
6831 pMachine->COMGETTER(CPUCount)(&cCpus);
6832
6833 /*
6834 * Create the VM
6835 */
6836 PVM pVM;
6837 /*
6838 * leave the lock since EMT will call Console. It's safe because
6839 * mMachineState is either Starting or Restoring state here.
6840 */
6841 alock.leave();
6842
6843 vrc = VMR3Create(cCpus, task->mSetVMErrorCallback, task.get(),
6844 task->mConfigConstructor, static_cast<Console *>(console),
6845 &pVM);
6846
6847 alock.enter();
6848
6849#ifdef VBOX_WITH_VRDP
6850 /* Enable client connections to the server. */
6851 console->consoleVRDPServer()->EnableConnections();
6852#endif /* VBOX_WITH_VRDP */
6853
6854 if (RT_SUCCESS(vrc))
6855 {
6856 do
6857 {
6858 /*
6859 * Register our load/save state file handlers
6860 */
6861 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
6862 NULL, NULL, NULL,
6863 NULL, saveStateFileExec, NULL,
6864 NULL, loadStateFileExec, NULL,
6865 static_cast<Console *>(console));
6866 AssertRCBreak(vrc);
6867
6868 vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
6869 AssertRC(vrc);
6870 if (RT_FAILURE(vrc))
6871 break;
6872
6873 /*
6874 * Synchronize debugger settings
6875 */
6876 MachineDebugger *machineDebugger = console->getMachineDebugger();
6877 if (machineDebugger)
6878 {
6879 machineDebugger->flushQueuedSettings();
6880 }
6881
6882 /*
6883 * Shared Folders
6884 */
6885 if (console->getVMMDev()->isShFlActive())
6886 {
6887 /* Does the code below call Console from the other thread?
6888 * Not sure, so leave the lock just in case. */
6889 alock.leave();
6890
6891 for (SharedFolderDataMap::const_iterator
6892 it = task->mSharedFolders.begin();
6893 it != task->mSharedFolders.end();
6894 ++ it)
6895 {
6896 rc = console->createSharedFolder((*it).first, (*it).second);
6897 CheckComRCBreakRC(rc);
6898 }
6899
6900 /* enter the lock again */
6901 alock.enter();
6902
6903 CheckComRCBreakRC(rc);
6904 }
6905
6906 /*
6907 * Capture USB devices.
6908 */
6909 rc = console->captureUSBDevices(pVM);
6910 CheckComRCBreakRC(rc);
6911
6912 /* leave the lock before a lengthy operation */
6913 alock.leave();
6914
6915 /* Load saved state? */
6916 if (task->mSavedStateFile.length())
6917 {
6918 LogFlowFunc(("Restoring saved state from '%s'...\n",
6919 task->mSavedStateFile.raw()));
6920
6921 vrc = VMR3LoadFromFile(pVM,
6922 task->mSavedStateFile.c_str(),
6923 Console::stateProgressCallback,
6924 static_cast<VMProgressTask*>(task.get()));
6925
6926 if (RT_SUCCESS(vrc))
6927 {
6928 if (task->mStartPaused)
6929 /* done */
6930 console->setMachineState(MachineState_Paused);
6931 else
6932 {
6933 /* Start/Resume the VM execution */
6934 vrc = VMR3Resume(pVM);
6935 AssertRC(vrc);
6936 }
6937 }
6938
6939 /* Power off in case we failed loading or resuming the VM */
6940 if (RT_FAILURE(vrc))
6941 {
6942 int vrc2 = VMR3PowerOff(pVM);
6943 AssertRC(vrc2);
6944 }
6945 }
6946 else if (task->mTeleporterEnabled)
6947 {
6948 /* -> ConsoleImplTeleporter.cpp */
6949 vrc = console->teleporterTrg(pVM, pMachine, task->mStartPaused, task->mProgress);
6950 if (RT_FAILURE(vrc) && !task->mErrorMsg.length())
6951 rc = E_FAIL; /* Avoid the "Missing error message..." assertion. */
6952 }
6953 else if (task->mStartPaused)
6954 /* done */
6955 console->setMachineState(MachineState_Paused);
6956 else
6957 {
6958 /* Power on the VM (i.e. start executing) */
6959 vrc = VMR3PowerOn(pVM);
6960 AssertRC(vrc);
6961 }
6962
6963 /* enter the lock again */
6964 alock.enter();
6965 }
6966 while (0);
6967
6968 /* On failure, destroy the VM */
6969 if (FAILED(rc) || RT_FAILURE(vrc))
6970 {
6971 /* preserve existing error info */
6972 ErrorInfoKeeper eik;
6973
6974 /* powerDown() will call VMR3Destroy() and do all necessary
6975 * cleanup (VRDP, USB devices) */
6976 HRESULT rc2 = console->powerDown();
6977 AssertComRC(rc2);
6978 }
6979 else
6980 {
6981 /*
6982 * Deregister the VMSetError callback. This is necessary as the
6983 * pfnVMAtError() function passed to VMR3Create() is supposed to
6984 * be sticky but our error callback isn't.
6985 */
6986 alock.leave();
6987 VMR3AtErrorDeregister(pVM, task->mSetVMErrorCallback, task.get());
6988 /** @todo register another VMSetError callback? */
6989 alock.enter();
6990 }
6991 }
6992 else
6993 {
6994 /*
6995 * If VMR3Create() failed it has released the VM memory.
6996 */
6997 console->mpVM = NULL;
6998 }
6999
7000 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
7001 {
7002 /* If VMR3Create() or one of the other calls in this function fail,
7003 * an appropriate error message has been set in task->mErrorMsg.
7004 * However since that happens via a callback, the rc status code in
7005 * this function is not updated.
7006 */
7007 if (!task->mErrorMsg.length())
7008 {
7009 /* If the error message is not set but we've got a failure,
7010 * convert the VBox status code into a meaningful error message.
7011 * This becomes unused once all the sources of errors set the
7012 * appropriate error message themselves.
7013 */
7014 AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
7015 task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
7016 vrc);
7017 }
7018
7019 /* Set the error message as the COM error.
7020 * Progress::notifyComplete() will pick it up later. */
7021 throw setError(E_FAIL, task->mErrorMsg.c_str());
7022 }
7023 }
7024 catch (HRESULT aRC) { rc = aRC; }
7025
7026 if ( console->mMachineState == MachineState_Starting
7027 || console->mMachineState == MachineState_Restoring
7028 || console->mMachineState == MachineState_TeleportingIn
7029 )
7030 {
7031 /* We are still in the Starting/Restoring state. This means one of:
7032 *
7033 * 1) we failed before VMR3Create() was called;
7034 * 2) VMR3Create() failed.
7035 *
7036 * In both cases, there is no need to call powerDown(), but we still
7037 * need to go back to the PoweredOff/Saved state. Reuse
7038 * vmstateChangeCallback() for that purpose.
7039 */
7040
7041 /* preserve existing error info */
7042 ErrorInfoKeeper eik;
7043
7044 Assert(console->mpVM == NULL);
7045 vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
7046 console);
7047 }
7048
7049 /*
7050 * Evaluate the final result. Note that the appropriate mMachineState value
7051 * is already set by vmstateChangeCallback() in all cases.
7052 */
7053
7054 /* leave the lock, don't need it any more */
7055 alock.leave();
7056
7057 if (SUCCEEDED(rc))
7058 {
7059 /* Notify the progress object of the success */
7060 task->mProgress->notifyComplete(S_OK);
7061 }
7062 else
7063 {
7064 /* The progress object will fetch the current error info */
7065 task->mProgress->notifyComplete(rc);
7066
7067 LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7068 }
7069
7070#if defined(RT_OS_WINDOWS)
7071 /* uninitialize COM */
7072 CoUninitialize();
7073#endif
7074
7075 LogFlowFuncLeave();
7076
7077 return VINF_SUCCESS;
7078}
7079
7080
7081/**
7082 * Reconfigures a medium attachment (part of taking an online snapshot).
7083 *
7084 * @param pVM The VM handle.
7085 * @param lInstance The instance of the controller.
7086 * @param enmController The type of the controller.
7087 * @param enmBus The storage bus type of the controller.
7088 * @param aMediumAtt The medium attachment.
7089 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7090 * @return VBox status code.
7091 */
7092static DECLCALLBACK(int) reconfigureMedium(PVM pVM, ULONG lInstance,
7093 StorageControllerType_T enmController,
7094 StorageBus_T enmBus,
7095 IMediumAttachment *aMediumAtt,
7096 HRESULT *phrc)
7097{
7098 LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
7099
7100 int rc;
7101 HRESULT hrc;
7102 Bstr bstr;
7103 *phrc = S_OK;
7104#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7105#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7106
7107 /*
7108 * Figure out medium and other attachment details.
7109 */
7110 ComPtr<IMedium> medium;
7111 hrc = aMediumAtt->COMGETTER(Medium)(medium.asOutParam()); H();
7112 LONG lDev;
7113 hrc = aMediumAtt->COMGETTER(Device)(&lDev); H();
7114 LONG lPort;
7115 hrc = aMediumAtt->COMGETTER(Port)(&lPort); H();
7116 DeviceType_T lType;
7117 hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
7118
7119 unsigned iLUN;
7120 const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
7121 AssertMsgReturn(pcszDevice, ("invalid disk controller type: %d\n", enmController), VERR_GENERAL_FAILURE);
7122 hrc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, iLUN); H();
7123
7124 /* Ignore attachments other than hard disks, since at the moment they are
7125 * not subject to snapshotting in general. */
7126 if (lType != DeviceType_HardDisk || medium.isNull())
7127 return VINF_SUCCESS;
7128
7129 /** @todo this should be unified with the relevant part of
7130 * Console::configConstructor to avoid inconsistencies. */
7131
7132 /*
7133 * Is there an existing LUN? If not create it.
7134 */
7135 PCFGMNODE pCfg;
7136 PCFGMNODE pLunL1;
7137
7138 /* SCSI has an extra driver between the device and the block driver. */
7139 if (enmBus == StorageBus_SCSI)
7140 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%u/AttachedDriver/AttachedDriver/", pcszDevice, lInstance, iLUN);
7141 else
7142 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%u/AttachedDriver/", pcszDevice, lInstance, iLUN);
7143
7144 if (!pLunL1)
7145 {
7146 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, lInstance);
7147 AssertReturn(pInst, VERR_INTERNAL_ERROR);
7148
7149 PCFGMNODE pLunL0;
7150 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%u", iLUN); RC_CHECK();
7151
7152 if (enmBus == StorageBus_SCSI)
7153 {
7154 rc = CFGMR3InsertString(pLunL0, "Driver", "SCSI"); RC_CHECK();
7155 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
7156
7157 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();
7158 }
7159
7160 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();
7161 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
7162 rc = CFGMR3InsertString(pCfg, "Type", "HardDisk"); RC_CHECK();
7163 rc = CFGMR3InsertInteger(pCfg, "Mountable", 0); RC_CHECK();
7164
7165 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();
7166 rc = CFGMR3InsertString(pLunL1, "Driver", "VD"); RC_CHECK();
7167 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
7168 }
7169 else
7170 {
7171#ifdef VBOX_STRICT
7172 char *pszDriver;
7173 rc = CFGMR3QueryStringAlloc(pLunL1, "Driver", &pszDriver); RC_CHECK();
7174 Assert(!strcmp(pszDriver, "VD"));
7175 MMR3HeapFree(pszDriver);
7176#endif
7177
7178 pCfg = CFGMR3GetChild(pLunL1, "Config");
7179 AssertReturn(pCfg, VERR_INTERNAL_ERROR);
7180
7181 /* Here used to be a lot of code checking if things have changed,
7182 * but that's not really worth it, as with snapshots there is always
7183 * some change, so the code was just logging useless information in
7184 * a hard to analyze form. */
7185
7186 /*
7187 * Detach the driver and replace the config node.
7188 */
7189 rc = PDMR3DeviceDetach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG); RC_CHECK();
7190 CFGMR3RemoveNode(pCfg);
7191 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
7192 }
7193
7194 /*
7195 * Create the driver configuration.
7196 */
7197 hrc = medium->COMGETTER(Location)(bstr.asOutParam()); H();
7198 LogFlowFunc(("LUN#%u: leaf location '%ls'\n", iLUN, bstr.raw()));
7199 rc = CFGMR3InsertString(pCfg, "Path", Utf8Str(bstr).c_str()); RC_CHECK();
7200 hrc = medium->COMGETTER(Format)(bstr.asOutParam()); H();
7201 LogFlowFunc(("LUN#%u: leaf format '%ls'\n", iLUN, bstr.raw()));
7202 rc = CFGMR3InsertString(pCfg, "Format", Utf8Str(bstr).c_str()); RC_CHECK();
7203
7204 /* Pass all custom parameters. */
7205 bool fHostIP = true;
7206 SafeArray<BSTR> names;
7207 SafeArray<BSTR> values;
7208 hrc = medium->GetProperties(NULL,
7209 ComSafeArrayAsOutParam(names),
7210 ComSafeArrayAsOutParam(values)); H();
7211
7212 if (names.size() != 0)
7213 {
7214 PCFGMNODE pVDC;
7215 rc = CFGMR3InsertNode(pCfg, "VDConfig", &pVDC); RC_CHECK();
7216 for (size_t i = 0; i < names.size(); ++ i)
7217 {
7218 if (values[i])
7219 {
7220 Utf8Str name = names[i];
7221 Utf8Str value = values[i];
7222 rc = CFGMR3InsertString(pVDC, name.c_str(), value.c_str());
7223 if ( !(name.compare("HostIPStack"))
7224 && !(value.compare("0")))
7225 fHostIP = false;
7226 }
7227 }
7228 }
7229
7230 /* Create an inversed tree of parents. */
7231 ComPtr<IMedium> parentMedium = medium;
7232 for (PCFGMNODE pParent = pCfg;;)
7233 {
7234 hrc = parentMedium->COMGETTER(Parent)(medium.asOutParam()); H();
7235 if (medium.isNull())
7236 break;
7237
7238 PCFGMNODE pCur;
7239 rc = CFGMR3InsertNode(pParent, "Parent", &pCur); RC_CHECK();
7240 hrc = medium->COMGETTER(Location)(bstr.asOutParam()); H();
7241 rc = CFGMR3InsertString(pCur, "Path", Utf8Str(bstr).c_str()); RC_CHECK();
7242
7243 hrc = medium->COMGETTER(Format)(bstr.asOutParam()); H();
7244 rc = CFGMR3InsertString(pCur, "Format", Utf8Str(bstr).c_str()); RC_CHECK();
7245
7246 /* Pass all custom parameters. */
7247 SafeArray<BSTR> names;
7248 SafeArray<BSTR> values;
7249 hrc = medium->GetProperties(NULL,
7250 ComSafeArrayAsOutParam(names),
7251 ComSafeArrayAsOutParam(values)); H();
7252
7253 if (names.size() != 0)
7254 {
7255 PCFGMNODE pVDC;
7256 rc = CFGMR3InsertNode(pCur, "VDConfig", &pVDC); RC_CHECK();
7257 for (size_t i = 0; i < names.size(); ++ i)
7258 {
7259 if (values[i])
7260 {
7261 Utf8Str name = names[i];
7262 Utf8Str value = values[i];
7263 rc = CFGMR3InsertString(pVDC, name.c_str(), value.c_str());
7264 if ( !(name.compare("HostIPStack"))
7265 && !(value.compare("0")))
7266 fHostIP = false;
7267 }
7268 }
7269 }
7270
7271 /* Custom code: put marker to not use host IP stack to driver
7272 * configuration node. Simplifies life of DrvVD a bit. */
7273 if (!fHostIP)
7274 {
7275 rc = CFGMR3InsertInteger(pCfg, "HostIPStack", 0); RC_CHECK();
7276 }
7277
7278
7279 /* next */
7280 pParent = pCur;
7281 parentMedium = medium;
7282 }
7283
7284 CFGMR3Dump(CFGMR3GetRoot(pVM));
7285
7286 /*
7287 * Attach the new driver.
7288 */
7289 rc = PDMR3DeviceAttach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); RC_CHECK();
7290
7291 LogFlowFunc(("Returns success\n"));
7292 return rc;
7293}
7294
7295/**
7296 * Worker thread created by Console::TakeSnapshot.
7297 * @param Thread The current thread (ignored).
7298 * @param pvUser The task.
7299 * @return VINF_SUCCESS (ignored).
7300 */
7301/*static*/
7302DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
7303{
7304 VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
7305
7306 // taking a snapshot consists of the following:
7307
7308 // 1) creating a diff image for each virtual hard disk, into which write operations go after
7309 // the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
7310 // 2) creating a Snapshot object with the state of the machine (hardware + storage,
7311 // done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
7312 // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
7313
7314 bool fBeganTakingSnapshot = false;
7315
7316 AutoCaller autoCaller(pTask->mConsole);
7317 CheckComRCReturnRC(autoCaller.rc());
7318
7319 AutoWriteLock alock(pTask->mConsole);
7320
7321 HRESULT rc = S_OK;
7322
7323 Console *that = pTask->mConsole;
7324
7325 try
7326 {
7327 /* STEP 1 + 2:
7328 * request creating the diff images on the server and create the snapshot object
7329 * (this will set the machine state to Saving on the server to block
7330 * others from accessing this machine)
7331 */
7332 rc = pTask->mConsole->mControl->BeginTakingSnapshot(that,
7333 pTask->bstrName,
7334 pTask->bstrDescription,
7335 pTask->mProgress,
7336 pTask->fTakingSnapshotOnline,
7337 pTask->bstrSavedStateFile.asOutParam());
7338 if (FAILED(rc)) throw rc;
7339
7340 fBeganTakingSnapshot = true;
7341
7342 /*
7343 * state file is non-null only when the VM is paused
7344 * (i.e. creating a snapshot online)
7345 */
7346 ComAssertThrow( (!pTask->bstrSavedStateFile.isNull() && pTask->fTakingSnapshotOnline)
7347 || (pTask->bstrSavedStateFile.isNull() && !pTask->fTakingSnapshotOnline),
7348 rc = E_FAIL);
7349
7350 /* sync the state with the server */
7351 that->setMachineStateLocally(MachineState_Saving);
7352
7353 // STEP 3: save the VM state (if online)
7354 if (pTask->fTakingSnapshotOnline)
7355 {
7356 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
7357
7358 pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")),
7359 pTask->ulMemSize); // operation weight, same as computed when setting up progress object
7360
7361 alock.leave();
7362
7363 int vrc = VMR3Save(that->mpVM,
7364 strSavedStateFile.c_str(),
7365 true /*fContinueAfterwards*/,
7366 Console::stateProgressCallback,
7367 (void*)pTask);
7368 if (RT_FAILURE(vrc))
7369 throw setError(E_FAIL,
7370 tr("Failed to save the machine state to '%s' (%Rrc)"),
7371 strSavedStateFile.c_str(), vrc);
7372
7373 alock.enter();
7374
7375 // STEP 4: reattach hard disks
7376 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7377
7378 pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")),
7379 1); // operation weight, same as computed when setting up progress object
7380
7381 com::SafeIfaceArray<IMediumAttachment> atts;
7382 rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
7383 if (FAILED(rc)) throw rc;
7384
7385 for (size_t i = 0;
7386 i < atts.size();
7387 ++i)
7388 {
7389 ComPtr<IStorageController> controller;
7390 BSTR controllerName;
7391 ULONG lInstance;
7392 StorageControllerType_T enmController;
7393 StorageBus_T enmBus;
7394
7395 /*
7396 * We can't pass a storage controller object directly
7397 * (g++ complains about not being able to pass non POD types through '...')
7398 * so we have to query needed values here and pass them.
7399 */
7400 rc = atts[i]->COMGETTER(Controller)(&controllerName);
7401 if (FAILED(rc)) throw rc;
7402
7403 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7404 if (FAILED(rc)) throw rc;
7405
7406 rc = controller->COMGETTER(ControllerType)(&enmController);
7407 if (FAILED(rc)) throw rc;
7408 rc = controller->COMGETTER(Instance)(&lInstance);
7409 if (FAILED(rc)) throw rc;
7410 rc = controller->COMGETTER(Bus)(&enmBus);
7411 if (FAILED(rc)) throw rc;
7412
7413 /*
7414 * don't leave the lock since reconfigureMedium isn't going
7415 * to access Console.
7416 */
7417 int vrc = VMR3ReqCallWait(that->mpVM,
7418 VMCPUID_ANY,
7419 (PFNRT)reconfigureMedium,
7420 6,
7421 that->mpVM,
7422 lInstance,
7423 enmController,
7424 enmBus,
7425 atts[i],
7426 &rc);
7427 if (RT_FAILURE(vrc))
7428 throw setError(E_FAIL, Console::tr("%Rrc"), vrc);
7429 if (FAILED(rc)) throw rc;
7430 }
7431 }
7432
7433 /*
7434 * finalize the requested snapshot object.
7435 * This will reset the machine state to the state it had right
7436 * before calling mControl->BeginTakingSnapshot().
7437 */
7438 rc = that->mControl->EndTakingSnapshot(TRUE); // success
7439 // do not throw rc here because we can't call EndTakingSnapshot() twice
7440 }
7441 catch (HRESULT rc)
7442 {
7443 /* preserve existing error info */
7444 ErrorInfoKeeper eik;
7445
7446 if (fBeganTakingSnapshot)
7447 that->mControl->EndTakingSnapshot(FALSE); // failure
7448 }
7449
7450 pTask->mProgress->notifyComplete(rc);
7451
7452 delete pTask;
7453
7454 if (pTask->lastMachineState == MachineState_Running)
7455 {
7456 /* restore the paused state if appropriate */
7457 that->setMachineStateLocally(MachineState_Paused);
7458 /* restore the running state if appropriate */
7459 that->Resume();
7460 }
7461 else
7462 that->setMachineStateLocally(pTask->lastMachineState);
7463
7464 LogFlowFuncLeave();
7465 return VINF_SUCCESS;
7466}
7467
7468/**
7469 * Thread for executing the saved state operation.
7470 *
7471 * @param Thread The thread handle.
7472 * @param pvUser Pointer to a VMSaveTask structure.
7473 * @return VINF_SUCCESS (ignored).
7474 *
7475 * @note Locks the Console object for writing.
7476 */
7477/*static*/
7478DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
7479{
7480 LogFlowFuncEnter();
7481
7482 std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
7483 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7484
7485 Assert(task->mSavedStateFile.length());
7486 Assert(!task->mProgress.isNull());
7487
7488 const ComObjPtr<Console> &that = task->mConsole;
7489 Utf8Str errMsg;
7490 HRESULT rc = S_OK;
7491
7492 LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7493
7494 int vrc = VMR3Save(that->mpVM,
7495 task->mSavedStateFile.c_str(),
7496 false, /*fContinueAfterwards*/
7497 Console::stateProgressCallback,
7498 static_cast<VMProgressTask*>(task.get()));
7499 if (RT_FAILURE(vrc))
7500 {
7501 errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
7502 task->mSavedStateFile.raw(), vrc);
7503 rc = E_FAIL;
7504 }
7505
7506 /* lock the console once we're going to access it */
7507 AutoWriteLock thatLock(that);
7508
7509 /*
7510 * finalize the requested save state procedure.
7511 * In case of success, the server will set the machine state to Saved;
7512 * in case of failure it will reset the it to the state it had right
7513 * before calling mControl->BeginSavingState().
7514 */
7515 that->mControl->EndSavingState(SUCCEEDED(rc));
7516
7517 /* synchronize the state with the server */
7518 if (!FAILED(rc))
7519 {
7520 /*
7521 * The machine has been successfully saved, so power it down
7522 * (vmstateChangeCallback() will set state to Saved on success).
7523 * Note: we release the task's VM caller, otherwise it will
7524 * deadlock.
7525 */
7526 task->releaseVMCaller();
7527
7528 rc = that->powerDown();
7529 }
7530
7531 /* notify the progress object about operation completion */
7532 if (SUCCEEDED(rc))
7533 task->mProgress->notifyComplete(S_OK);
7534 else
7535 {
7536 if (errMsg.length())
7537 task->mProgress->notifyComplete(rc,
7538 COM_IIDOF(IConsole),
7539 Console::getComponentName(),
7540 errMsg.c_str());
7541 else
7542 task->mProgress->notifyComplete(rc);
7543 }
7544
7545 LogFlowFuncLeave();
7546 return VINF_SUCCESS;
7547}
7548
7549/**
7550 * Thread for powering down the Console.
7551 *
7552 * @param Thread The thread handle.
7553 * @param pvUser Pointer to the VMTask structure.
7554 * @return VINF_SUCCESS (ignored).
7555 *
7556 * @note Locks the Console object for writing.
7557 */
7558/*static*/
7559DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
7560{
7561 LogFlowFuncEnter();
7562
7563 std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
7564 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7565
7566 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
7567
7568 const ComObjPtr<Console> &that = task->mConsole;
7569
7570 /* Note: no need to use addCaller() to protect Console because VMTask does
7571 * that */
7572
7573 /* wait until the method tat started us returns */
7574 AutoWriteLock thatLock(that);
7575
7576 /* release VM caller to avoid the powerDown() deadlock */
7577 task->releaseVMCaller();
7578
7579 that->powerDown(task->mProgress);
7580
7581 LogFlowFuncLeave();
7582 return VINF_SUCCESS;
7583}
7584
7585/**
7586 * The Main status driver instance data.
7587 */
7588typedef struct DRVMAINSTATUS
7589{
7590 /** The LED connectors. */
7591 PDMILEDCONNECTORS ILedConnectors;
7592 /** Pointer to the LED ports interface above us. */
7593 PPDMILEDPORTS pLedPorts;
7594 /** Pointer to the array of LED pointers. */
7595 PPDMLED *papLeds;
7596 /** The unit number corresponding to the first entry in the LED array. */
7597 RTUINT iFirstLUN;
7598 /** The unit number corresponding to the last entry in the LED array.
7599 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
7600 RTUINT iLastLUN;
7601} DRVMAINSTATUS, *PDRVMAINSTATUS;
7602
7603
7604/**
7605 * Notification about a unit which have been changed.
7606 *
7607 * The driver must discard any pointers to data owned by
7608 * the unit and requery it.
7609 *
7610 * @param pInterface Pointer to the interface structure containing the called function pointer.
7611 * @param iLUN The unit number.
7612 */
7613DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
7614{
7615 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
7616 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
7617 {
7618 PPDMLED pLed;
7619 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
7620 if (RT_FAILURE(rc))
7621 pLed = NULL;
7622 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
7623 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
7624 }
7625}
7626
7627
7628/**
7629 * Queries an interface to the driver.
7630 *
7631 * @returns Pointer to interface.
7632 * @returns NULL if the interface was not supported by the driver.
7633 * @param pInterface Pointer to this interface structure.
7634 * @param enmInterface The requested interface identification.
7635 */
7636DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
7637{
7638 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
7639 PDRVMAINSTATUS pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7640 switch (enmInterface)
7641 {
7642 case PDMINTERFACE_BASE:
7643 return &pDrvIns->IBase;
7644 case PDMINTERFACE_LED_CONNECTORS:
7645 return &pDrv->ILedConnectors;
7646 default:
7647 return NULL;
7648 }
7649}
7650
7651
7652/**
7653 * Destruct a status driver instance.
7654 *
7655 * @returns VBox status.
7656 * @param pDrvIns The driver instance data.
7657 */
7658DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
7659{
7660 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7661 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7662 if (pData->papLeds)
7663 {
7664 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
7665 while (iLed-- > 0)
7666 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL);
7667 }
7668}
7669
7670
7671/**
7672 * Construct a status driver instance.
7673 *
7674 * @copydoc FNPDMDRVCONSTRUCT
7675 */
7676DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
7677{
7678 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7679 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7680
7681 /*
7682 * Validate configuration.
7683 */
7684 if (!CFGMR3AreValuesValid(pCfgHandle, "papLeds\0First\0Last\0"))
7685 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
7686 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
7687 ("Configuration error: Not possible to attach anything to this driver!\n"),
7688 VERR_PDM_DRVINS_NO_ATTACH);
7689
7690 /*
7691 * Data.
7692 */
7693 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
7694 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
7695
7696 /*
7697 * Read config.
7698 */
7699 int rc = CFGMR3QueryPtr(pCfgHandle, "papLeds", (void **)&pData->papLeds);
7700 if (RT_FAILURE(rc))
7701 {
7702 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
7703 return rc;
7704 }
7705
7706 rc = CFGMR3QueryU32(pCfgHandle, "First", &pData->iFirstLUN);
7707 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7708 pData->iFirstLUN = 0;
7709 else if (RT_FAILURE(rc))
7710 {
7711 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
7712 return rc;
7713 }
7714
7715 rc = CFGMR3QueryU32(pCfgHandle, "Last", &pData->iLastLUN);
7716 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7717 pData->iLastLUN = 0;
7718 else if (RT_FAILURE(rc))
7719 {
7720 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
7721 return rc;
7722 }
7723 if (pData->iFirstLUN > pData->iLastLUN)
7724 {
7725 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
7726 return VERR_GENERAL_FAILURE;
7727 }
7728
7729 /*
7730 * Get the ILedPorts interface of the above driver/device and
7731 * query the LEDs we want.
7732 */
7733 pData->pLedPorts = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
7734 if (!pData->pLedPorts)
7735 {
7736 AssertMsgFailed(("Configuration error: No led ports interface above!\n"));
7737 return VERR_PDM_MISSING_INTERFACE_ABOVE;
7738 }
7739
7740 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
7741 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
7742
7743 return VINF_SUCCESS;
7744}
7745
7746
7747/**
7748 * Keyboard driver registration record.
7749 */
7750const PDMDRVREG Console::DrvStatusReg =
7751{
7752 /* u32Version */
7753 PDM_DRVREG_VERSION,
7754 /* szDriverName */
7755 "MainStatus",
7756 /* pszDescription */
7757 "Main status driver (Main as in the API).",
7758 /* fFlags */
7759 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
7760 /* fClass. */
7761 PDM_DRVREG_CLASS_STATUS,
7762 /* cMaxInstances */
7763 ~0,
7764 /* cbInstance */
7765 sizeof(DRVMAINSTATUS),
7766 /* pfnConstruct */
7767 Console::drvStatus_Construct,
7768 /* pfnDestruct */
7769 Console::drvStatus_Destruct,
7770 /* pfnIOCtl */
7771 NULL,
7772 /* pfnPowerOn */
7773 NULL,
7774 /* pfnReset */
7775 NULL,
7776 /* pfnSuspend */
7777 NULL,
7778 /* pfnResume */
7779 NULL,
7780 /* pfnAttach */
7781 NULL,
7782 /* pfnDetach */
7783 NULL,
7784 /* pfnPowerOff */
7785 NULL,
7786 /* pfnSoftReset */
7787 NULL,
7788 /* u32EndVersion */
7789 PDM_DRVREG_VERSION
7790};
7791
7792/* 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