VirtualBox

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

Last change on this file since 23624 was 23624, checked in by vboxsync, 16 years ago

Main/ConsoleImpl: fix incorrect assertion when changing to host dvd/floppy.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette