VirtualBox

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

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

Main/Console+Machine+Snapshot+Medium: Start with online snapshot merging. Not complete, as the actual merge operation is missing and thus triggers a controlled failure. Made snapshot deletion retryable, by updating the to-be-deleted snapshot, and only deleting it completely if everything was successful.

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