VirtualBox

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

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

Main,GuestProperties: Moved the HGCM shutdown down after we've powered off the VM. Ditto for moving the guest properties to VBoxSVC. Explicitly flush the guest property change notifications before moving them. Added a handleUnexpectedExceptions to Console that is similar to the one found in VirtualBox (i.e. in VBoxSVC).

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