VirtualBox

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

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

Main/Console: eliminate dead variable

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