VirtualBox

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

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

StorageController: added support for Instance number

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

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