VirtualBox

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

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

Main/VirtualBoxCallback: rename OnSnapshotDiscard to OnSnapshotDeleted for consistency, clean up wording and other minor issues

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