VirtualBox

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

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

ConsoleImpl.cpp: Made IConsole::Pause() while teleporting work.

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