VirtualBox

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

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

ConsoleImpl.cpp: missing defined() around RT_OS_FREEBSD in the #elif, added @todos about moving the TAP init code out of Main to where it belongs (DrvTAP).

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