VirtualBox

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

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

FreeBSD: Add support for tap interface networking. Contributed by Juergen Lock

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