VirtualBox

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

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

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

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