VirtualBox

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

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

VMM,Devices,Main: VMR3ReqCall w/ RT_INDEFINITE_WAIT -> VMR3ReqCallWait.

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