VirtualBox

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

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

Main: rename snapshot APIs, remove DiscardCurrentSnapshotAndState; tree snapshots implementation still missing

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