VirtualBox

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

Last change on this file since 23080 was 23080, checked in by vboxsync, 16 years ago

VMM,ConsoleImpl.cpp: Added fFlags to VMR3ReqCall.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 243.7 KB
Line 
1/* $Id: ConsoleImpl.cpp 23080 2009-09-17 10:08:00Z 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! */, VMREQFLAGS_VBOX_STATUS,
2927 (PFNRT) Console::changeDrive, 8,
2928 this, pszDevice, uInstance, uLun, eState, peState, pszPath, fPassthrough);
2929
2930 /* leave the lock before waiting for a result (EMT will call us back!) */
2931 alock.leave();
2932
2933 if (vrc == VERR_TIMEOUT || VBOX_SUCCESS (vrc))
2934 {
2935 vrc = VMR3ReqWait (pReq, RT_INDEFINITE_WAIT);
2936 AssertRC (vrc);
2937 if (VBOX_SUCCESS (vrc))
2938 vrc = pReq->iStatus;
2939 }
2940 VMR3ReqFree (pReq);
2941
2942 if (VBOX_SUCCESS (vrc))
2943 {
2944 LogFlowThisFunc(("Returns S_OK\n"));
2945 return S_OK;
2946 }
2947
2948 if (pszPath)
2949 return setError (E_FAIL,
2950 tr ("Could not mount the media/drive '%s' (%Rrc)"), pszPath, vrc);
2951
2952 return setError (E_FAIL,
2953 tr ("Could not unmount the currently mounted media/drive (%Rrc)"), vrc);
2954}
2955
2956
2957/**
2958 * Performs the Floppy/DVD change in EMT.
2959 *
2960 * @returns VBox status code.
2961 *
2962 * @param pThis Pointer to the Console object.
2963 * @param pszDevice The PDM device name.
2964 * @param uInstance The PDM device instance.
2965 * @param uLun The PDM LUN number of the drive.
2966 * @param eState The new state.
2967 * @param peState Pointer to the variable keeping the actual state of the drive.
2968 * This will be both read and updated to eState or other appropriate state.
2969 * @param pszPath The path to the media / drive which is now being mounted / captured.
2970 * If NULL no media or drive is attached and the LUN will be configured with
2971 * the default block driver with no media. This will also be the state if
2972 * mounting / capturing the specified media / drive fails.
2973 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
2974 *
2975 * @thread EMT
2976 * @note Locks the Console object for writing.
2977 */
2978DECLCALLBACK(int) Console::changeDrive (Console *pThis, const char *pszDevice, unsigned uInstance, unsigned uLun,
2979 DriveState_T eState, DriveState_T *peState,
2980 const char *pszPath, bool fPassthrough)
2981{
2982 LogFlowFunc (("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u eState=%d "
2983 "peState=%p:{%d} pszPath=%p:{%s} fPassthrough=%d\n",
2984 pThis, pszDevice, pszDevice, uInstance, uLun, eState,
2985 peState, *peState, pszPath, pszPath, fPassthrough));
2986
2987 AssertReturn(pThis, VERR_INVALID_PARAMETER);
2988
2989 AssertMsg ( (!strcmp (pszDevice, "i82078") && uLun == 0 && uInstance == 0)
2990 || (!strcmp (pszDevice, "piix3ide") && uLun == 2 && uInstance == 0),
2991 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
2992
2993 AutoCaller autoCaller(pThis);
2994 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);
2995
2996 /* protect mpVM */
2997 AutoVMCaller autoVMCaller (pThis);
2998 CheckComRCReturnRC(autoVMCaller.rc());
2999
3000 PVM pVM = pThis->mpVM;
3001
3002 /*
3003 * Suspend the VM first.
3004 *
3005 * The VM must not be running since it might have pending I/O to
3006 * the drive which is being changed.
3007 */
3008 bool fResume;
3009 VMSTATE enmVMState = VMR3GetState (pVM);
3010 switch (enmVMState)
3011 {
3012 case VMSTATE_RESETTING:
3013 case VMSTATE_RUNNING:
3014 {
3015 LogFlowFunc (("Suspending the VM...\n"));
3016 /* disable the callback to prevent Console-level state change */
3017 pThis->mVMStateChangeCallbackDisabled = true;
3018 int rc = VMR3Suspend (pVM);
3019 pThis->mVMStateChangeCallbackDisabled = false;
3020 AssertRCReturn (rc, rc);
3021 fResume = true;
3022 break;
3023 }
3024
3025 case VMSTATE_SUSPENDED:
3026 case VMSTATE_CREATED:
3027 case VMSTATE_OFF:
3028 fResume = false;
3029 break;
3030
3031 default:
3032 AssertMsgFailedReturn (("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3033 }
3034
3035 int rc = VINF_SUCCESS;
3036 int rcRet = VINF_SUCCESS;
3037
3038 /*
3039 In general locking the object before doing VMR3* calls is quite safe
3040 here, since we're on EMT. Anyway we lock for write after eventually
3041 suspending the vm. The reason is that in the vmstateChangeCallback the
3042 var mVMStateChangeCallbackDisabled is checked under a lock also, which
3043 can lead to an dead lock. The write lock is necessary because we
3044 indirectly modify the meDVDState/meFloppyState members (pointed to by
3045 peState).
3046 */
3047 AutoWriteLock alock(pThis);
3048
3049 do
3050 {
3051 /*
3052 * Unmount existing media / detach host drive.
3053 */
3054 PPDMIMOUNT pIMount = NULL;
3055 switch (*peState)
3056 {
3057
3058 case DriveState_ImageMounted:
3059 {
3060 /*
3061 * Resolve the interface.
3062 */
3063 PPDMIBASE pBase;
3064 rc = PDMR3QueryLun (pVM, pszDevice, uInstance, uLun, &pBase);
3065 if (VBOX_FAILURE (rc))
3066 {
3067 if (rc == VERR_PDM_LUN_NOT_FOUND)
3068 rc = VINF_SUCCESS;
3069 AssertRC (rc);
3070 break;
3071 }
3072
3073 pIMount = (PPDMIMOUNT) pBase->pfnQueryInterface (pBase, PDMINTERFACE_MOUNT);
3074 AssertBreakStmt (pIMount, rc = VERR_INVALID_POINTER);
3075
3076 /*
3077 * Unmount the media.
3078 */
3079 rc = pIMount->pfnUnmount (pIMount, false);
3080 if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
3081 rc = VINF_SUCCESS;
3082 break;
3083 }
3084
3085 case DriveState_HostDriveCaptured:
3086 {
3087 rc = PDMR3DeviceDetach (pVM, pszDevice, uInstance, uLun, PDM_TACH_FLAGS_NOT_HOT_PLUG);
3088 if (rc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)
3089 rc = VINF_SUCCESS;
3090 AssertRC (rc);
3091 break;
3092 }
3093
3094 case DriveState_NotMounted:
3095 break;
3096
3097 default:
3098 AssertMsgFailed (("Invalid *peState: %d\n", peState));
3099 break;
3100 }
3101
3102 if (VBOX_FAILURE (rc))
3103 {
3104 rcRet = rc;
3105 break;
3106 }
3107
3108 /*
3109 * Nothing is currently mounted.
3110 */
3111 *peState = DriveState_NotMounted;
3112
3113
3114 /*
3115 * Process the HostDriveCaptured state first, as the fallback path
3116 * means mounting the normal block driver without media.
3117 */
3118 if (eState == DriveState_HostDriveCaptured)
3119 {
3120 /*
3121 * Detach existing driver chain (block).
3122 */
3123 int rc = PDMR3DeviceDetach (pVM, pszDevice, uInstance, uLun, PDM_TACH_FLAGS_NOT_HOT_PLUG);
3124 if (VBOX_FAILURE (rc))
3125 {
3126 if (rc == VERR_PDM_LUN_NOT_FOUND)
3127 rc = VINF_SUCCESS;
3128 AssertReleaseRC (rc);
3129 break; /* we're toast */
3130 }
3131 pIMount = NULL;
3132
3133 /*
3134 * Construct a new driver configuration.
3135 */
3136 PCFGMNODE pInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
3137 AssertRelease (pInst);
3138 /* nuke anything which might have been left behind. */
3139 CFGMR3RemoveNode (CFGMR3GetChildF (pInst, "LUN#%d", uLun));
3140
3141 /* create a new block driver config */
3142 PCFGMNODE pLunL0;
3143 PCFGMNODE pCfg;
3144 if ( VBOX_SUCCESS (rc = CFGMR3InsertNodeF (pInst, &pLunL0, "LUN#%u", uLun))
3145 && VBOX_SUCCESS (rc = CFGMR3InsertString (pLunL0, "Driver", !strcmp (pszDevice, "i82078") ? "HostFloppy" : "HostDVD"))
3146 && VBOX_SUCCESS (rc = CFGMR3InsertNode (pLunL0, "Config", &pCfg))
3147 && VBOX_SUCCESS (rc = CFGMR3InsertString (pCfg, "Path", pszPath))
3148 && VBOX_SUCCESS (rc = !strcmp (pszDevice, "i82078") ? VINF_SUCCESS : CFGMR3InsertInteger(pCfg, "Passthrough", fPassthrough)))
3149 {
3150 /*
3151 * Attempt to attach the driver.
3152 */
3153 rc = PDMR3DeviceAttach(pVM, pszDevice, uInstance, uLun, PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
3154 AssertRC (rc);
3155 }
3156 if (VBOX_FAILURE (rc))
3157 rcRet = rc;
3158 }
3159
3160 /*
3161 * Process the ImageMounted, NotMounted and failed HostDriveCapture cases.
3162 */
3163 rc = VINF_SUCCESS;
3164 switch (eState)
3165 {
3166#define RC_CHECK() do { if (VBOX_FAILURE (rc)) { AssertReleaseRC (rc); break; } } while (0)
3167
3168 case DriveState_HostDriveCaptured:
3169 if (VBOX_SUCCESS (rcRet))
3170 break;
3171 /* fallback: umounted block driver. */
3172 pszPath = NULL;
3173 eState = DriveState_NotMounted;
3174 /* fallthru */
3175 case DriveState_ImageMounted:
3176 case DriveState_NotMounted:
3177 {
3178 /*
3179 * Resolve the drive interface / create the driver.
3180 */
3181 if (!pIMount)
3182 {
3183 PPDMIBASE pBase;
3184 rc = PDMR3QueryLun (pVM, pszDevice, uInstance, uLun, &pBase);
3185 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
3186 {
3187 /*
3188 * We have to create it, so we'll do the full config setup and everything.
3189 */
3190 PCFGMNODE pIdeInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
3191 AssertRelease (pIdeInst);
3192
3193 /* nuke anything which might have been left behind. */
3194 CFGMR3RemoveNode (CFGMR3GetChildF (pIdeInst, "LUN#%d", uLun));
3195
3196 /* create a new block driver config */
3197 PCFGMNODE pLunL0;
3198 rc = CFGMR3InsertNodeF (pIdeInst, &pLunL0, "LUN#%d", uLun); RC_CHECK();
3199 rc = CFGMR3InsertString (pLunL0, "Driver", "Block"); RC_CHECK();
3200 PCFGMNODE pCfg;
3201 rc = CFGMR3InsertNode (pLunL0, "Config", &pCfg); RC_CHECK();
3202 rc = CFGMR3InsertString (pCfg, "Type", !strcmp (pszDevice, "i82078") ? "Floppy 1.44" : "DVD");
3203 RC_CHECK();
3204 rc = CFGMR3InsertInteger (pCfg, "Mountable", 1); RC_CHECK();
3205
3206 /*
3207 * Attach the driver.
3208 */
3209 rc = PDMR3DeviceAttach(pVM, pszDevice, uInstance, uLun, PDM_TACH_FLAGS_NOT_HOT_PLUG, &pBase);
3210 RC_CHECK();
3211 }
3212 else if (VBOX_FAILURE(rc))
3213 {
3214 AssertRC (rc);
3215 return rc;
3216 }
3217
3218 pIMount = (PPDMIMOUNT) pBase->pfnQueryInterface (pBase, PDMINTERFACE_MOUNT);
3219 if (!pIMount)
3220 {
3221 AssertFailed();
3222 return rc;
3223 }
3224 }
3225
3226 /*
3227 * If we've got an image, let's mount it.
3228 */
3229 if (pszPath && *pszPath)
3230 {
3231 rc = pIMount->pfnMount (pIMount, pszPath, strcmp (pszDevice, "i82078") ? "MediaISO" : "RawImage");
3232 if (VBOX_FAILURE (rc))
3233 eState = DriveState_NotMounted;
3234 }
3235 break;
3236 }
3237
3238 default:
3239 AssertMsgFailed (("Invalid eState: %d\n", eState));
3240 break;
3241
3242#undef RC_CHECK
3243 }
3244
3245 if (VBOX_FAILURE (rc) && VBOX_SUCCESS (rcRet))
3246 rcRet = rc;
3247
3248 *peState = eState;
3249 }
3250 while (0);
3251
3252 /*
3253 Unlock before resuming because the vmstateChangeCallback problem
3254 described above.
3255 */
3256 alock.unlock();
3257
3258 /*
3259 * Resume the VM if necessary.
3260 */
3261 if (fResume)
3262 {
3263 LogFlowFunc (("Resuming the VM...\n"));
3264 /* disable the callback to prevent Console-level state change */
3265 pThis->mVMStateChangeCallbackDisabled = true;
3266 rc = VMR3Resume (pVM);
3267 pThis->mVMStateChangeCallbackDisabled = false;
3268 AssertRC (rc);
3269 if (VBOX_FAILURE (rc))
3270 {
3271 /* too bad, we failed. try to sync the console state with the VMM state */
3272 vmstateChangeCallback (pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3273 }
3274 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3275 // error (if any) will be hidden from the caller. For proper reporting
3276 // of such multiple errors to the caller we need to enhance the
3277 // IVurtualBoxError interface. For now, give the first error the higher
3278 // priority.
3279 if (VBOX_SUCCESS (rcRet))
3280 rcRet = rc;
3281 }
3282
3283 LogFlowFunc (("Returning %Rrc\n", rcRet));
3284 return rcRet;
3285}
3286
3287
3288/**
3289 * Called by IInternalSessionControl::OnNetworkAdapterChange().
3290 *
3291 * @note Locks this object for writing.
3292 */
3293HRESULT Console::onNetworkAdapterChange (INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
3294{
3295 LogFlowThisFunc(("\n"));
3296
3297 AutoCaller autoCaller(this);
3298 AssertComRCReturnRC(autoCaller.rc());
3299
3300 AutoWriteLock alock(this);
3301
3302 /* Don't do anything if the VM isn't running */
3303 if (!mpVM)
3304 return S_OK;
3305
3306 /* protect mpVM */
3307 AutoVMCaller autoVMCaller (this);
3308 CheckComRCReturnRC(autoVMCaller.rc());
3309
3310 /* Get the properties we need from the adapter */
3311 BOOL fCableConnected, fTraceEnabled;
3312 HRESULT rc = aNetworkAdapter->COMGETTER(CableConnected) (&fCableConnected);
3313 AssertComRC(rc);
3314 if (SUCCEEDED(rc))
3315 {
3316 rc = aNetworkAdapter->COMGETTER(TraceEnabled) (&fTraceEnabled);
3317 AssertComRC(rc);
3318 }
3319 if (SUCCEEDED(rc))
3320 {
3321 ULONG ulInstance;
3322 rc = aNetworkAdapter->COMGETTER(Slot) (&ulInstance);
3323 AssertComRC (rc);
3324 if (SUCCEEDED(rc))
3325 {
3326 /*
3327 * Find the pcnet instance, get the config interface and update
3328 * the link state.
3329 */
3330 PPDMIBASE pBase;
3331 const char *pszAdapterName = "pcnet";
3332#ifdef VBOX_WITH_E1000
3333 /*
3334 * Perhaps it would be much wiser to wrap both 'pcnet' and 'e1000'
3335 * into generic 'net' device.
3336 */
3337 NetworkAdapterType_T adapterType;
3338 rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3339 AssertComRC(rc);
3340 if (adapterType == NetworkAdapterType_I82540EM ||
3341 adapterType == NetworkAdapterType_I82543GC ||
3342 adapterType == NetworkAdapterType_I82545EM)
3343 pszAdapterName = "e1000";
3344#ifdef VBOX_WITH_VIRTIO
3345 if (adapterType == NetworkAdapterType_Virtio)
3346 pszAdapterName = "virtio";
3347#endif /* VBOX_WITH_VIRTIO */
3348#endif
3349 int vrc = PDMR3QueryDeviceLun (mpVM, pszAdapterName,
3350 (unsigned) ulInstance, 0, &pBase);
3351 ComAssertRC (vrc);
3352 if (VBOX_SUCCESS(vrc))
3353 {
3354 Assert(pBase);
3355 PPDMINETWORKCONFIG pINetCfg = (PPDMINETWORKCONFIG) pBase->
3356 pfnQueryInterface(pBase, PDMINTERFACE_NETWORK_CONFIG);
3357 if (pINetCfg)
3358 {
3359 Log (("Console::onNetworkAdapterChange: setting link state to %d\n",
3360 fCableConnected));
3361 vrc = pINetCfg->pfnSetLinkState (pINetCfg,
3362 fCableConnected ? PDMNETWORKLINKSTATE_UP
3363 : PDMNETWORKLINKSTATE_DOWN);
3364 ComAssertRC(vrc);
3365 }
3366#ifdef VBOX_DYNAMIC_NET_ATTACH
3367 if (RT_SUCCESS(vrc) && changeAdapter)
3368 {
3369 VMSTATE enmVMState = VMR3GetState(mpVM);
3370
3371 if ( enmVMState == VMSTATE_RUNNING
3372 || enmVMState == VMSTATE_SUSPENDED)
3373 {
3374 if (fTraceEnabled && fCableConnected && pINetCfg)
3375 {
3376 vrc = pINetCfg->pfnSetLinkState (pINetCfg, PDMNETWORKLINKSTATE_DOWN);
3377 ComAssertRC(vrc);
3378 }
3379
3380 rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
3381
3382 if (fTraceEnabled && fCableConnected && pINetCfg)
3383 {
3384 vrc = pINetCfg->pfnSetLinkState (pINetCfg, PDMNETWORKLINKSTATE_UP);
3385 ComAssertRC(vrc);
3386 }
3387 }
3388 }
3389#endif /* VBOX_DYNAMIC_NET_ATTACH */
3390 }
3391
3392 if (VBOX_FAILURE (vrc))
3393 rc = E_FAIL;
3394 }
3395 }
3396
3397 /* notify console callbacks on success */
3398 if (SUCCEEDED(rc))
3399 {
3400 CallbackList::iterator it = mCallbacks.begin();
3401 while (it != mCallbacks.end())
3402 (*it++)->OnNetworkAdapterChange (aNetworkAdapter);
3403 }
3404
3405 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3406 return rc;
3407}
3408
3409
3410#ifdef VBOX_DYNAMIC_NET_ATTACH
3411/**
3412 * Process a network adaptor change.
3413 *
3414 * @returns COM status code.
3415 *
3416 * @param pszDevice The PDM device name.
3417 * @param uInstance The PDM device instance.
3418 * @param uLun The PDM LUN number of the drive.
3419 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3420 *
3421 * @note Locks this object for writing.
3422 */
3423HRESULT Console::doNetworkAdapterChange (const char *pszDevice,
3424 unsigned uInstance,
3425 unsigned uLun,
3426 INetworkAdapter *aNetworkAdapter)
3427{
3428 LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3429 pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3430
3431 AutoCaller autoCaller(this);
3432 AssertComRCReturnRC(autoCaller.rc());
3433
3434 /* We will need to release the write lock before calling EMT */
3435 AutoWriteLock alock(this);
3436
3437 /* protect mpVM */
3438 AutoVMCaller autoVMCaller (this);
3439 CheckComRCReturnRC(autoVMCaller.rc());
3440
3441 /*
3442 * Call worker in EMT, that's faster and safer than doing everything
3443 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
3444 * here to make requests from under the lock in order to serialize them.
3445 */
3446 PVMREQ pReq;
3447 int vrc = VMR3ReqCall (mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
3448 (PFNRT) Console::changeNetworkAttachment, 5,
3449 this, pszDevice, uInstance, uLun, aNetworkAdapter);
3450
3451 /* leave the lock before waiting for a result (EMT will call us back!) */
3452 alock.leave();
3453
3454 if (vrc == VERR_TIMEOUT || VBOX_SUCCESS (vrc))
3455 {
3456 vrc = VMR3ReqWait (pReq, RT_INDEFINITE_WAIT);
3457 AssertRC (vrc);
3458 if (VBOX_SUCCESS (vrc))
3459 vrc = pReq->iStatus;
3460 }
3461 VMR3ReqFree (pReq);
3462
3463 if (VBOX_SUCCESS (vrc))
3464 {
3465 LogFlowThisFunc(("Returns S_OK\n"));
3466 return S_OK;
3467 }
3468
3469 return setError (E_FAIL,
3470 tr ("Could not change the network adaptor attachement type (%Rrc)"), vrc);
3471}
3472
3473
3474/**
3475 * Performs the Network Adaptor change in EMT.
3476 *
3477 * @returns VBox status code.
3478 *
3479 * @param pThis Pointer to the Console object.
3480 * @param pszDevice The PDM device name.
3481 * @param uInstance The PDM device instance.
3482 * @param uLun The PDM LUN number of the drive.
3483 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
3484 *
3485 * @thread EMT
3486 * @note Locks the Console object for writing.
3487 */
3488DECLCALLBACK(int) Console::changeNetworkAttachment (Console *pThis,
3489 const char *pszDevice,
3490 unsigned uInstance,
3491 unsigned uLun,
3492 INetworkAdapter *aNetworkAdapter)
3493{
3494 LogFlowFunc (("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
3495 pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
3496
3497 AssertReturn(pThis, VERR_INVALID_PARAMETER);
3498
3499 AssertMsg ( (!strcmp (pszDevice, "pcnet") && uLun == 0 && uInstance < SchemaDefs::NetworkAdapterCount)
3500 || (!strcmp (pszDevice, "e1000") && uLun == 0 && uInstance < SchemaDefs::NetworkAdapterCount),
3501 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3502 Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
3503
3504 AutoCaller autoCaller(pThis);
3505 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);
3506
3507 /* protect mpVM */
3508 AutoVMCaller autoVMCaller (pThis);
3509 CheckComRCReturnRC(autoVMCaller.rc());
3510
3511 PVM pVM = pThis->mpVM;
3512
3513 /*
3514 * Suspend the VM first.
3515 *
3516 * The VM must not be running since it might have pending I/O to
3517 * the drive which is being changed.
3518 */
3519 bool fResume;
3520 VMSTATE enmVMState = VMR3GetState (pVM);
3521 switch (enmVMState)
3522 {
3523 case VMSTATE_RESETTING:
3524 case VMSTATE_RUNNING:
3525 {
3526 LogFlowFunc (("Suspending the VM...\n"));
3527 /* disable the callback to prevent Console-level state change */
3528 pThis->mVMStateChangeCallbackDisabled = true;
3529 int rc = VMR3Suspend (pVM);
3530 pThis->mVMStateChangeCallbackDisabled = false;
3531 AssertRCReturn (rc, rc);
3532 fResume = true;
3533 break;
3534 }
3535
3536 case VMSTATE_SUSPENDED:
3537 case VMSTATE_CREATED:
3538 case VMSTATE_OFF:
3539 fResume = false;
3540 break;
3541
3542 default:
3543 AssertMsgFailedReturn (("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
3544 }
3545
3546 int rc = VINF_SUCCESS;
3547 int rcRet = VINF_SUCCESS;
3548
3549 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
3550 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3551 PCFGMNODE pInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);
3552 AssertRelease (pInst);
3553
3554 /** @todo pritesh: Need to store the previous network configuration
3555 * and restore it if configNetwork fails, currently not sure if the
3556 * previous atachment will also cleanly reattach with the later one
3557 * failing to attach.
3558 */
3559 rcRet = configNetwork(pThis, pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
3560
3561 /*
3562 * Resume the VM if necessary.
3563 */
3564 if (fResume)
3565 {
3566 LogFlowFunc (("Resuming the VM...\n"));
3567 /* disable the callback to prevent Console-level state change */
3568 pThis->mVMStateChangeCallbackDisabled = true;
3569 rc = VMR3Resume (pVM);
3570 pThis->mVMStateChangeCallbackDisabled = false;
3571 AssertRC (rc);
3572 if (VBOX_FAILURE (rc))
3573 {
3574 /* too bad, we failed. try to sync the console state with the VMM state */
3575 vmstateChangeCallback (pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
3576 }
3577 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
3578 // error (if any) will be hidden from the caller. For proper reporting
3579 // of such multiple errors to the caller we need to enhance the
3580 // IVirtualBoxError interface. For now, give the first error the higher
3581 // priority.
3582 if (VBOX_SUCCESS (rcRet))
3583 rcRet = rc;
3584 }
3585
3586 LogFlowFunc (("Returning %Rrc\n", rcRet));
3587 return rcRet;
3588}
3589#endif /* VBOX_DYNAMIC_NET_ATTACH */
3590
3591
3592/**
3593 * Called by IInternalSessionControl::OnSerialPortChange().
3594 *
3595 * @note Locks this object for writing.
3596 */
3597HRESULT Console::onSerialPortChange (ISerialPort *aSerialPort)
3598{
3599 LogFlowThisFunc(("\n"));
3600
3601 AutoCaller autoCaller(this);
3602 AssertComRCReturnRC(autoCaller.rc());
3603
3604 AutoWriteLock alock(this);
3605
3606 /* Don't do anything if the VM isn't running */
3607 if (!mpVM)
3608 return S_OK;
3609
3610 HRESULT rc = S_OK;
3611
3612 /* protect mpVM */
3613 AutoVMCaller autoVMCaller (this);
3614 CheckComRCReturnRC(autoVMCaller.rc());
3615
3616 /* nothing to do so far */
3617
3618 /* notify console callbacks on success */
3619 if (SUCCEEDED(rc))
3620 {
3621 CallbackList::iterator it = mCallbacks.begin();
3622 while (it != mCallbacks.end())
3623 (*it++)->OnSerialPortChange (aSerialPort);
3624 }
3625
3626 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3627 return rc;
3628}
3629
3630/**
3631 * Called by IInternalSessionControl::OnParallelPortChange().
3632 *
3633 * @note Locks this object for writing.
3634 */
3635HRESULT Console::onParallelPortChange (IParallelPort *aParallelPort)
3636{
3637 LogFlowThisFunc(("\n"));
3638
3639 AutoCaller autoCaller(this);
3640 AssertComRCReturnRC(autoCaller.rc());
3641
3642 AutoWriteLock alock(this);
3643
3644 /* Don't do anything if the VM isn't running */
3645 if (!mpVM)
3646 return S_OK;
3647
3648 HRESULT rc = S_OK;
3649
3650 /* protect mpVM */
3651 AutoVMCaller autoVMCaller (this);
3652 CheckComRCReturnRC(autoVMCaller.rc());
3653
3654 /* nothing to do so far */
3655
3656 /* notify console callbacks on success */
3657 if (SUCCEEDED(rc))
3658 {
3659 CallbackList::iterator it = mCallbacks.begin();
3660 while (it != mCallbacks.end())
3661 (*it++)->OnParallelPortChange (aParallelPort);
3662 }
3663
3664 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3665 return rc;
3666}
3667
3668/**
3669 * Called by IInternalSessionControl::OnStorageControllerChange().
3670 *
3671 * @note Locks this object for writing.
3672 */
3673HRESULT Console::onStorageControllerChange ()
3674{
3675 LogFlowThisFunc(("\n"));
3676
3677 AutoCaller autoCaller(this);
3678 AssertComRCReturnRC(autoCaller.rc());
3679
3680 AutoWriteLock alock(this);
3681
3682 /* Don't do anything if the VM isn't running */
3683 if (!mpVM)
3684 return S_OK;
3685
3686 HRESULT rc = S_OK;
3687
3688 /* protect mpVM */
3689 AutoVMCaller autoVMCaller (this);
3690 CheckComRCReturnRC(autoVMCaller.rc());
3691
3692 /* nothing to do so far */
3693
3694 /* notify console callbacks on success */
3695 if (SUCCEEDED(rc))
3696 {
3697 CallbackList::iterator it = mCallbacks.begin();
3698 while (it != mCallbacks.end())
3699 (*it++)->OnStorageControllerChange ();
3700 }
3701
3702 LogFlowThisFunc(("Leaving rc=%#x\n", rc));
3703 return rc;
3704}
3705
3706/**
3707 * Called by IInternalSessionControl::OnVRDPServerChange().
3708 *
3709 * @note Locks this object for writing.
3710 */
3711HRESULT Console::onVRDPServerChange()
3712{
3713 AutoCaller autoCaller(this);
3714 AssertComRCReturnRC(autoCaller.rc());
3715
3716 AutoWriteLock alock(this);
3717
3718 HRESULT rc = S_OK;
3719
3720 if (mVRDPServer && mMachineState == MachineState_Running)
3721 {
3722 BOOL vrdpEnabled = FALSE;
3723
3724 rc = mVRDPServer->COMGETTER(Enabled) (&vrdpEnabled);
3725 ComAssertComRCRetRC (rc);
3726
3727 /* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
3728 alock.leave();
3729
3730 if (vrdpEnabled)
3731 {
3732 // If there was no VRDP server started the 'stop' will do nothing.
3733 // However if a server was started and this notification was called,
3734 // we have to restart the server.
3735 mConsoleVRDPServer->Stop ();
3736
3737 if (VBOX_FAILURE(mConsoleVRDPServer->Launch ()))
3738 {
3739 rc = E_FAIL;
3740 }
3741 else
3742 {
3743 mConsoleVRDPServer->EnableConnections ();
3744 }
3745 }
3746 else
3747 {
3748 mConsoleVRDPServer->Stop ();
3749 }
3750
3751 alock.enter();
3752 }
3753
3754 /* notify console callbacks on success */
3755 if (SUCCEEDED(rc))
3756 {
3757 CallbackList::iterator it = mCallbacks.begin();
3758 while (it != mCallbacks.end())
3759 (*it++)->OnVRDPServerChange();
3760 }
3761
3762 return rc;
3763}
3764
3765/**
3766 * Called by IInternalSessionControl::OnUSBControllerChange().
3767 *
3768 * @note Locks this object for writing.
3769 */
3770HRESULT Console::onUSBControllerChange()
3771{
3772 LogFlowThisFunc(("\n"));
3773
3774 AutoCaller autoCaller(this);
3775 AssertComRCReturnRC(autoCaller.rc());
3776
3777 AutoWriteLock alock(this);
3778
3779 /* Ignore if no VM is running yet. */
3780 if (!mpVM)
3781 return S_OK;
3782
3783 HRESULT rc = S_OK;
3784
3785/// @todo (dmik)
3786// check for the Enabled state and disable virtual USB controller??
3787// Anyway, if we want to query the machine's USB Controller we need to cache
3788// it to mUSBController in #init() (as it is done with mDVDDrive).
3789//
3790// bird: While the VM supports hot-plugging, I doubt any guest can handle it at this time... :-)
3791//
3792// /* protect mpVM */
3793// AutoVMCaller autoVMCaller (this);
3794// CheckComRCReturnRC(autoVMCaller.rc());
3795
3796 /* notify console callbacks on success */
3797 if (SUCCEEDED(rc))
3798 {
3799 CallbackList::iterator it = mCallbacks.begin();
3800 while (it != mCallbacks.end())
3801 (*it++)->OnUSBControllerChange();
3802 }
3803
3804 return rc;
3805}
3806
3807/**
3808 * Called by IInternalSessionControl::OnSharedFolderChange().
3809 *
3810 * @note Locks this object for writing.
3811 */
3812HRESULT Console::onSharedFolderChange (BOOL aGlobal)
3813{
3814 LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
3815
3816 AutoCaller autoCaller(this);
3817 AssertComRCReturnRC(autoCaller.rc());
3818
3819 AutoWriteLock alock(this);
3820
3821 HRESULT rc = fetchSharedFolders (aGlobal);
3822
3823 /* notify console callbacks on success */
3824 if (SUCCEEDED(rc))
3825 {
3826 CallbackList::iterator it = mCallbacks.begin();
3827 while (it != mCallbacks.end())
3828 (*it++)->OnSharedFolderChange (aGlobal ? (Scope_T) Scope_Global
3829 : (Scope_T) Scope_Machine);
3830 }
3831
3832 return rc;
3833}
3834
3835/**
3836 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
3837 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
3838 * returns TRUE for a given remote USB device.
3839 *
3840 * @return S_OK if the device was attached to the VM.
3841 * @return failure if not attached.
3842 *
3843 * @param aDevice
3844 * The device in question.
3845 * @param aMaskedIfs
3846 * The interfaces to hide from the guest.
3847 *
3848 * @note Locks this object for writing.
3849 */
3850HRESULT Console::onUSBDeviceAttach (IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
3851{
3852#ifdef VBOX_WITH_USB
3853 LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
3854
3855 AutoCaller autoCaller(this);
3856 ComAssertComRCRetRC (autoCaller.rc());
3857
3858 AutoWriteLock alock(this);
3859
3860 /* protect mpVM (we don't need error info, since it's a callback) */
3861 AutoVMCallerQuiet autoVMCaller (this);
3862 if (FAILED (autoVMCaller.rc()))
3863 {
3864 /* The VM may be no more operational when this message arrives
3865 * (e.g. it may be Saving or Stopping or just PoweredOff) --
3866 * autoVMCaller.rc() will return a failure in this case. */
3867 LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
3868 mMachineState));
3869 return autoVMCaller.rc();
3870 }
3871
3872 if (aError != NULL)
3873 {
3874 /* notify callbacks about the error */
3875 onUSBDeviceStateChange (aDevice, true /* aAttached */, aError);
3876 return S_OK;
3877 }
3878
3879 /* Don't proceed unless there's at least one USB hub. */
3880 if (!PDMR3USBHasHub (mpVM))
3881 {
3882 LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
3883 return E_FAIL;
3884 }
3885
3886 HRESULT rc = attachUSBDevice (aDevice, aMaskedIfs);
3887 if (FAILED (rc))
3888 {
3889 /* take the current error info */
3890 com::ErrorInfoKeeper eik;
3891 /* the error must be a VirtualBoxErrorInfo instance */
3892 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
3893 Assert (!error.isNull());
3894 if (!error.isNull())
3895 {
3896 /* notify callbacks about the error */
3897 onUSBDeviceStateChange (aDevice, true /* aAttached */, error);
3898 }
3899 }
3900
3901 return rc;
3902
3903#else /* !VBOX_WITH_USB */
3904 return E_FAIL;
3905#endif /* !VBOX_WITH_USB */
3906}
3907
3908/**
3909 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
3910 * processRemoteUSBDevices().
3911 *
3912 * @note Locks this object for writing.
3913 */
3914HRESULT Console::onUSBDeviceDetach (IN_BSTR aId,
3915 IVirtualBoxErrorInfo *aError)
3916{
3917#ifdef VBOX_WITH_USB
3918 Guid Uuid (aId);
3919 LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
3920
3921 AutoCaller autoCaller(this);
3922 AssertComRCReturnRC(autoCaller.rc());
3923
3924 AutoWriteLock alock(this);
3925
3926 /* Find the device. */
3927 ComObjPtr<OUSBDevice> device;
3928 USBDeviceList::iterator it = mUSBDevices.begin();
3929 while (it != mUSBDevices.end())
3930 {
3931 LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
3932 if ((*it)->id() == Uuid)
3933 {
3934 device = *it;
3935 break;
3936 }
3937 ++ it;
3938 }
3939
3940
3941 if (device.isNull())
3942 {
3943 LogFlowThisFunc(("USB device not found.\n"));
3944
3945 /* The VM may be no more operational when this message arrives
3946 * (e.g. it may be Saving or Stopping or just PoweredOff). Use
3947 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
3948 * failure in this case. */
3949
3950 AutoVMCallerQuiet autoVMCaller (this);
3951 if (FAILED (autoVMCaller.rc()))
3952 {
3953 LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
3954 mMachineState));
3955 return autoVMCaller.rc();
3956 }
3957
3958 /* the device must be in the list otherwise */
3959 AssertFailedReturn (E_FAIL);
3960 }
3961
3962 if (aError != NULL)
3963 {
3964 /* notify callback about an error */
3965 onUSBDeviceStateChange (device, false /* aAttached */, aError);
3966 return S_OK;
3967 }
3968
3969 HRESULT rc = detachUSBDevice (it);
3970
3971 if (FAILED (rc))
3972 {
3973 /* take the current error info */
3974 com::ErrorInfoKeeper eik;
3975 /* the error must be a VirtualBoxErrorInfo instance */
3976 ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
3977 Assert (!error.isNull());
3978 if (!error.isNull())
3979 {
3980 /* notify callbacks about the error */
3981 onUSBDeviceStateChange (device, false /* aAttached */, error);
3982 }
3983 }
3984
3985 return rc;
3986
3987#else /* !VBOX_WITH_USB */
3988 return E_FAIL;
3989#endif /* !VBOX_WITH_USB */
3990}
3991
3992/**
3993 * @note Temporarily locks this object for writing.
3994 */
3995HRESULT Console::getGuestProperty (IN_BSTR aName, BSTR *aValue,
3996 ULONG64 *aTimestamp, BSTR *aFlags)
3997{
3998#if !defined (VBOX_WITH_GUEST_PROPS)
3999 ReturnComNotImplemented();
4000#else
4001 if (!VALID_PTR (aName))
4002 return E_INVALIDARG;
4003 if (!VALID_PTR (aValue))
4004 return E_POINTER;
4005 if ((aTimestamp != NULL) && !VALID_PTR (aTimestamp))
4006 return E_POINTER;
4007 if ((aFlags != NULL) && !VALID_PTR (aFlags))
4008 return E_POINTER;
4009
4010 AutoCaller autoCaller(this);
4011 AssertComRCReturnRC(autoCaller.rc());
4012
4013 /* protect mpVM (if not NULL) */
4014 AutoVMCallerWeak autoVMCaller (this);
4015 CheckComRCReturnRC(autoVMCaller.rc());
4016
4017 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4018 * autoVMCaller, so there is no need to hold a lock of this */
4019
4020 HRESULT rc = E_UNEXPECTED;
4021 using namespace guestProp;
4022
4023 try
4024 {
4025 VBOXHGCMSVCPARM parm[4];
4026 Utf8Str Utf8Name = aName;
4027 char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
4028
4029 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4030 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4031 /* The + 1 is the null terminator */
4032 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4033 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4034 parm[1].u.pointer.addr = pszBuffer;
4035 parm[1].u.pointer.size = sizeof(pszBuffer);
4036 int vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", GET_PROP_HOST,
4037 4, &parm[0]);
4038 /* The returned string should never be able to be greater than our buffer */
4039 AssertLogRel (vrc != VERR_BUFFER_OVERFLOW);
4040 AssertLogRel (RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
4041 if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
4042 {
4043 rc = S_OK;
4044 if (vrc != VERR_NOT_FOUND)
4045 {
4046 Utf8Str strBuffer(pszBuffer);
4047 strBuffer.cloneTo(aValue);
4048
4049 *aTimestamp = parm[2].u.uint64;
4050
4051 size_t iFlags = strBuffer.length() + 1;
4052 Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
4053 }
4054 else
4055 aValue = NULL;
4056 }
4057 else
4058 rc = setError (E_UNEXPECTED,
4059 tr ("The service call failed with the error %Rrc"), vrc);
4060 }
4061 catch(std::bad_alloc & /*e*/)
4062 {
4063 rc = E_OUTOFMEMORY;
4064 }
4065 return rc;
4066#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
4067}
4068
4069/**
4070 * @note Temporarily locks this object for writing.
4071 */
4072HRESULT Console::setGuestProperty (IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
4073{
4074#if !defined (VBOX_WITH_GUEST_PROPS)
4075 ReturnComNotImplemented();
4076#else
4077 if (!VALID_PTR (aName))
4078 return E_INVALIDARG;
4079 if ((aValue != NULL) && !VALID_PTR (aValue))
4080 return E_INVALIDARG;
4081 if ((aFlags != NULL) && !VALID_PTR (aFlags))
4082 return E_INVALIDARG;
4083
4084 AutoCaller autoCaller(this);
4085 AssertComRCReturnRC(autoCaller.rc());
4086
4087 /* protect mpVM (if not NULL) */
4088 AutoVMCallerWeak autoVMCaller (this);
4089 CheckComRCReturnRC(autoVMCaller.rc());
4090
4091 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4092 * autoVMCaller, so there is no need to hold a lock of this */
4093
4094 HRESULT rc = E_UNEXPECTED;
4095 using namespace guestProp;
4096
4097 VBOXHGCMSVCPARM parm[3];
4098 Utf8Str Utf8Name = aName;
4099 int vrc = VINF_SUCCESS;
4100
4101 parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
4102 parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
4103 /* The + 1 is the null terminator */
4104 parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
4105 Utf8Str Utf8Value = aValue;
4106 if (aValue != NULL)
4107 {
4108 parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
4109 parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
4110 /* The + 1 is the null terminator */
4111 parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
4112 }
4113 Utf8Str Utf8Flags = aFlags;
4114 if (aFlags != NULL)
4115 {
4116 parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
4117 parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
4118 /* The + 1 is the null terminator */
4119 parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
4120 }
4121 if ((aValue != NULL) && (aFlags != NULL))
4122 vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", SET_PROP_HOST,
4123 3, &parm[0]);
4124 else if (aValue != NULL)
4125 vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
4126 2, &parm[0]);
4127 else
4128 vrc = mVMMDev->hgcmHostCall ("VBoxGuestPropSvc", DEL_PROP_HOST,
4129 1, &parm[0]);
4130 if (RT_SUCCESS(vrc))
4131 rc = S_OK;
4132 else
4133 rc = setError (E_UNEXPECTED,
4134 tr ("The service call failed with the error %Rrc"), vrc);
4135 return rc;
4136#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
4137}
4138
4139
4140/**
4141 * @note Temporarily locks this object for writing.
4142 */
4143HRESULT Console::enumerateGuestProperties (IN_BSTR aPatterns,
4144 ComSafeArrayOut(BSTR, aNames),
4145 ComSafeArrayOut(BSTR, aValues),
4146 ComSafeArrayOut(ULONG64, aTimestamps),
4147 ComSafeArrayOut(BSTR, aFlags))
4148{
4149#if !defined (VBOX_WITH_GUEST_PROPS)
4150 ReturnComNotImplemented();
4151#else
4152 if (!VALID_PTR (aPatterns) && (aPatterns != NULL))
4153 return E_POINTER;
4154 if (ComSafeArrayOutIsNull(aNames))
4155 return E_POINTER;
4156 if (ComSafeArrayOutIsNull(aValues))
4157 return E_POINTER;
4158 if (ComSafeArrayOutIsNull(aTimestamps))
4159 return E_POINTER;
4160 if (ComSafeArrayOutIsNull(aFlags))
4161 return E_POINTER;
4162
4163 AutoCaller autoCaller(this);
4164 AssertComRCReturnRC(autoCaller.rc());
4165
4166 /* protect mpVM (if not NULL) */
4167 AutoVMCallerWeak autoVMCaller (this);
4168 CheckComRCReturnRC(autoVMCaller.rc());
4169
4170 /* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
4171 * autoVMCaller, so there is no need to hold a lock of this */
4172
4173 return doEnumerateGuestProperties (aPatterns, ComSafeArrayOutArg(aNames),
4174 ComSafeArrayOutArg(aValues),
4175 ComSafeArrayOutArg(aTimestamps),
4176 ComSafeArrayOutArg(aFlags));
4177#endif /* else !defined (VBOX_WITH_GUEST_PROPS) */
4178}
4179
4180/**
4181 * Gets called by Session::UpdateMachineState()
4182 * (IInternalSessionControl::updateMachineState()).
4183 *
4184 * Must be called only in certain cases (see the implementation).
4185 *
4186 * @note Locks this object for writing.
4187 */
4188HRESULT Console::updateMachineState (MachineState_T aMachineState)
4189{
4190 AutoCaller autoCaller(this);
4191 AssertComRCReturnRC(autoCaller.rc());
4192
4193 AutoWriteLock alock(this);
4194
4195 AssertReturn(mMachineState == MachineState_Saving ||
4196 mMachineState == MachineState_Discarding,
4197 E_FAIL);
4198
4199 return setMachineStateLocally (aMachineState);
4200}
4201
4202/**
4203 * @note Locks this object for writing.
4204 */
4205void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
4206 uint32_t xHot, uint32_t yHot,
4207 uint32_t width, uint32_t height,
4208 void *pShape)
4209{
4210#if 0
4211 LogFlowThisFuncEnter();
4212 LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, "
4213 "height=%d, shape=%p\n",
4214 fVisible, fAlpha, xHot, yHot, width, height, pShape));
4215#endif
4216
4217 AutoCaller autoCaller(this);
4218 AssertComRCReturnVoid (autoCaller.rc());
4219
4220 /* We need a write lock because we alter the cached callback data */
4221 AutoWriteLock alock(this);
4222
4223 /* Save the callback arguments */
4224 mCallbackData.mpsc.visible = fVisible;
4225 mCallbackData.mpsc.alpha = fAlpha;
4226 mCallbackData.mpsc.xHot = xHot;
4227 mCallbackData.mpsc.yHot = yHot;
4228 mCallbackData.mpsc.width = width;
4229 mCallbackData.mpsc.height = height;
4230
4231 /* start with not valid */
4232 bool wasValid = mCallbackData.mpsc.valid;
4233 mCallbackData.mpsc.valid = false;
4234
4235 if (pShape != NULL)
4236 {
4237 size_t cb = (width + 7) / 8 * height; /* size of the AND mask */
4238 cb = ((cb + 3) & ~3) + width * 4 * height; /* + gap + size of the XOR mask */
4239 /* try to reuse the old shape buffer if the size is the same */
4240 if (!wasValid)
4241 mCallbackData.mpsc.shape = NULL;
4242 else
4243 if (mCallbackData.mpsc.shape != NULL && mCallbackData.mpsc.shapeSize != cb)
4244 {
4245 RTMemFree (mCallbackData.mpsc.shape);
4246 mCallbackData.mpsc.shape = NULL;
4247 }
4248 if (mCallbackData.mpsc.shape == NULL)
4249 {
4250 mCallbackData.mpsc.shape = (BYTE *) RTMemAllocZ (cb);
4251 AssertReturnVoid (mCallbackData.mpsc.shape);
4252 }
4253 mCallbackData.mpsc.shapeSize = cb;
4254 memcpy (mCallbackData.mpsc.shape, pShape, cb);
4255 }
4256 else
4257 {
4258 if (wasValid && mCallbackData.mpsc.shape != NULL)
4259 RTMemFree (mCallbackData.mpsc.shape);
4260 mCallbackData.mpsc.shape = NULL;
4261 mCallbackData.mpsc.shapeSize = 0;
4262 }
4263
4264 mCallbackData.mpsc.valid = true;
4265
4266 CallbackList::iterator it = mCallbacks.begin();
4267 while (it != mCallbacks.end())
4268 (*it++)->OnMousePointerShapeChange (fVisible, fAlpha, xHot, yHot,
4269 width, height, (BYTE *) pShape);
4270
4271#if 0
4272 LogFlowThisFuncLeave();
4273#endif
4274}
4275
4276/**
4277 * @note Locks this object for writing.
4278 */
4279void Console::onMouseCapabilityChange (BOOL supportsAbsolute, BOOL needsHostCursor)
4280{
4281 LogFlowThisFunc(("supportsAbsolute=%d needsHostCursor=%d\n",
4282 supportsAbsolute, needsHostCursor));
4283
4284 AutoCaller autoCaller(this);
4285 AssertComRCReturnVoid (autoCaller.rc());
4286
4287 /* We need a write lock because we alter the cached callback data */
4288 AutoWriteLock alock(this);
4289
4290 /* save the callback arguments */
4291 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
4292 mCallbackData.mcc.needsHostCursor = needsHostCursor;
4293 mCallbackData.mcc.valid = true;
4294
4295 CallbackList::iterator it = mCallbacks.begin();
4296 while (it != mCallbacks.end())
4297 {
4298 Log2(("Console::onMouseCapabilityChange: calling %p\n", (void*)*it));
4299 (*it++)->OnMouseCapabilityChange (supportsAbsolute, needsHostCursor);
4300 }
4301}
4302
4303/**
4304 * @note Locks this object for reading.
4305 */
4306void Console::onStateChange (MachineState_T machineState)
4307{
4308 AutoCaller autoCaller(this);
4309 AssertComRCReturnVoid (autoCaller.rc());
4310
4311 AutoReadLock alock(this);
4312
4313 CallbackList::iterator it = mCallbacks.begin();
4314 while (it != mCallbacks.end())
4315 (*it++)->OnStateChange (machineState);
4316}
4317
4318/**
4319 * @note Locks this object for reading.
4320 */
4321void Console::onAdditionsStateChange()
4322{
4323 AutoCaller autoCaller(this);
4324 AssertComRCReturnVoid (autoCaller.rc());
4325
4326 AutoReadLock alock(this);
4327
4328 CallbackList::iterator it = mCallbacks.begin();
4329 while (it != mCallbacks.end())
4330 (*it++)->OnAdditionsStateChange();
4331}
4332
4333/**
4334 * @note Locks this object for reading.
4335 */
4336void Console::onAdditionsOutdated()
4337{
4338 AutoCaller autoCaller(this);
4339 AssertComRCReturnVoid (autoCaller.rc());
4340
4341 AutoReadLock alock(this);
4342
4343 /** @todo Use the On-Screen Display feature to report the fact.
4344 * The user should be told to install additions that are
4345 * provided with the current VBox build:
4346 * VBOX_VERSION_MAJOR.VBOX_VERSION_MINOR.VBOX_VERSION_BUILD
4347 */
4348}
4349
4350/**
4351 * @note Locks this object for writing.
4352 */
4353void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
4354{
4355 AutoCaller autoCaller(this);
4356 AssertComRCReturnVoid (autoCaller.rc());
4357
4358 /* We need a write lock because we alter the cached callback data */
4359 AutoWriteLock alock(this);
4360
4361 /* save the callback arguments */
4362 mCallbackData.klc.numLock = fNumLock;
4363 mCallbackData.klc.capsLock = fCapsLock;
4364 mCallbackData.klc.scrollLock = fScrollLock;
4365 mCallbackData.klc.valid = true;
4366
4367 CallbackList::iterator it = mCallbacks.begin();
4368 while (it != mCallbacks.end())
4369 (*it++)->OnKeyboardLedsChange(fNumLock, fCapsLock, fScrollLock);
4370}
4371
4372/**
4373 * @note Locks this object for reading.
4374 */
4375void Console::onUSBDeviceStateChange (IUSBDevice *aDevice, bool aAttached,
4376 IVirtualBoxErrorInfo *aError)
4377{
4378 AutoCaller autoCaller(this);
4379 AssertComRCReturnVoid (autoCaller.rc());
4380
4381 AutoReadLock alock(this);
4382
4383 CallbackList::iterator it = mCallbacks.begin();
4384 while (it != mCallbacks.end())
4385 (*it++)->OnUSBDeviceStateChange (aDevice, aAttached, aError);
4386}
4387
4388/**
4389 * @note Locks this object for reading.
4390 */
4391void Console::onRuntimeError (BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
4392{
4393 AutoCaller autoCaller(this);
4394 AssertComRCReturnVoid (autoCaller.rc());
4395
4396 AutoReadLock alock(this);
4397
4398 CallbackList::iterator it = mCallbacks.begin();
4399 while (it != mCallbacks.end())
4400 (*it++)->OnRuntimeError (aFatal, aErrorID, aMessage);
4401}
4402
4403/**
4404 * @note Locks this object for reading.
4405 */
4406HRESULT Console::onShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
4407{
4408 AssertReturn(aCanShow, E_POINTER);
4409 AssertReturn(aWinId, E_POINTER);
4410
4411 *aCanShow = FALSE;
4412 *aWinId = 0;
4413
4414 AutoCaller autoCaller(this);
4415 AssertComRCReturnRC(autoCaller.rc());
4416
4417 AutoReadLock alock(this);
4418
4419 HRESULT rc = S_OK;
4420 CallbackList::iterator it = mCallbacks.begin();
4421
4422 if (aCheck)
4423 {
4424 while (it != mCallbacks.end())
4425 {
4426 BOOL canShow = FALSE;
4427 rc = (*it++)->OnCanShowWindow (&canShow);
4428 AssertComRC (rc);
4429 if (FAILED (rc) || !canShow)
4430 return rc;
4431 }
4432 *aCanShow = TRUE;
4433 }
4434 else
4435 {
4436 while (it != mCallbacks.end())
4437 {
4438 ULONG64 winId = 0;
4439 rc = (*it++)->OnShowWindow (&winId);
4440 AssertComRC (rc);
4441 if (FAILED (rc))
4442 return rc;
4443 /* only one callback may return non-null winId */
4444 Assert (*aWinId == 0 || winId == 0);
4445 if (*aWinId == 0)
4446 *aWinId = winId;
4447 }
4448 }
4449
4450 return S_OK;
4451}
4452
4453// private methods
4454////////////////////////////////////////////////////////////////////////////////
4455
4456/**
4457 * Increases the usage counter of the mpVM pointer. Guarantees that
4458 * VMR3Destroy() will not be called on it at least until releaseVMCaller()
4459 * is called.
4460 *
4461 * If this method returns a failure, the caller is not allowed to use mpVM
4462 * and may return the failed result code to the upper level. This method sets
4463 * the extended error info on failure if \a aQuiet is false.
4464 *
4465 * Setting \a aQuiet to true is useful for methods that don't want to return
4466 * the failed result code to the caller when this method fails (e.g. need to
4467 * silently check for the mpVM availability).
4468 *
4469 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
4470 * returned instead of asserting. Having it false is intended as a sanity check
4471 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
4472 *
4473 * @param aQuiet true to suppress setting error info
4474 * @param aAllowNullVM true to accept mpVM being NULL and return a failure
4475 * (otherwise this method will assert if mpVM is NULL)
4476 *
4477 * @note Locks this object for writing.
4478 */
4479HRESULT Console::addVMCaller (bool aQuiet /* = false */,
4480 bool aAllowNullVM /* = false */)
4481{
4482 AutoCaller autoCaller (this);
4483 AssertComRCReturnRC(autoCaller.rc());
4484
4485 AutoWriteLock alock(this);
4486
4487 if (mVMDestroying)
4488 {
4489 /* powerDown() is waiting for all callers to finish */
4490 return aQuiet ? E_ACCESSDENIED : setError (E_ACCESSDENIED,
4491 tr ("Virtual machine is being powered down"));
4492 }
4493
4494 if (mpVM == NULL)
4495 {
4496 Assert (aAllowNullVM == true);
4497
4498 /* The machine is not powered up */
4499 return aQuiet ? E_ACCESSDENIED : setError (E_ACCESSDENIED,
4500 tr ("Virtual machine is not powered up"));
4501 }
4502
4503 ++ mVMCallers;
4504
4505 return S_OK;
4506}
4507
4508/**
4509 * Decreases the usage counter of the mpVM pointer. Must always complete
4510 * the addVMCaller() call after the mpVM pointer is no more necessary.
4511 *
4512 * @note Locks this object for writing.
4513 */
4514void Console::releaseVMCaller()
4515{
4516 AutoCaller autoCaller (this);
4517 AssertComRCReturnVoid (autoCaller.rc());
4518
4519 AutoWriteLock alock(this);
4520
4521 AssertReturnVoid (mpVM != NULL);
4522
4523 Assert (mVMCallers > 0);
4524 -- mVMCallers;
4525
4526 if (mVMCallers == 0 && mVMDestroying)
4527 {
4528 /* inform powerDown() there are no more callers */
4529 RTSemEventSignal (mVMZeroCallersSem);
4530 }
4531}
4532
4533/**
4534 * Initialize the release logging facility. In case something
4535 * goes wrong, there will be no release logging. Maybe in the future
4536 * we can add some logic to use different file names in this case.
4537 * Note that the logic must be in sync with Machine::DeleteSettings().
4538 */
4539HRESULT Console::consoleInitReleaseLog (const ComPtr<IMachine> aMachine)
4540{
4541 HRESULT hrc = S_OK;
4542
4543 Bstr logFolder;
4544 hrc = aMachine->COMGETTER(LogFolder) (logFolder.asOutParam());
4545 CheckComRCReturnRC(hrc);
4546
4547 Utf8Str logDir = logFolder;
4548
4549 /* make sure the Logs folder exists */
4550 Assert(logDir.length());
4551 if (!RTDirExists(logDir.c_str()))
4552 RTDirCreateFullPath(logDir.c_str(), 0777);
4553
4554 Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
4555 logDir.raw(), RTPATH_DELIMITER);
4556 Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
4557 logDir.raw(), RTPATH_DELIMITER);
4558
4559 /*
4560 * Age the old log files
4561 * Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
4562 * Overwrite target files in case they exist.
4563 */
4564 ComPtr<IVirtualBox> virtualBox;
4565 aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
4566 ComPtr<ISystemProperties> systemProperties;
4567 virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4568 ULONG uLogHistoryCount = 3;
4569 systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4570 ComPtr<IHost> host;
4571 virtualBox->COMGETTER(Host)(host.asOutParam());
4572 ULONG uHostRamMb = 0, uHostRamAvailMb = 0;
4573 host->COMGETTER(MemorySize)(&uHostRamMb);
4574 host->COMGETTER(MemoryAvailable)(&uHostRamAvailMb);
4575 if (uLogHistoryCount)
4576 {
4577 for (int i = uLogHistoryCount-1; i >= 0; i--)
4578 {
4579 Utf8Str *files[] = { &logFile, &pngFile };
4580 Utf8Str oldName, newName;
4581
4582 for (unsigned int j = 0; j < RT_ELEMENTS (files); ++ j)
4583 {
4584 if (i > 0)
4585 oldName = Utf8StrFmt ("%s.%d", files [j]->raw(), i);
4586 else
4587 oldName = *files [j];
4588 newName = Utf8StrFmt ("%s.%d", files [j]->raw(), i + 1);
4589 /* If the old file doesn't exist, delete the new file (if it
4590 * exists) to provide correct rotation even if the sequence is
4591 * broken */
4592 if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
4593 == VERR_FILE_NOT_FOUND)
4594 RTFileDelete(newName.c_str());
4595 }
4596 }
4597 }
4598
4599 PRTLOGGER loggerRelease;
4600 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
4601 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
4602#if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2)
4603 fFlags |= RTLOGFLAGS_USECRLF;
4604#endif
4605 char szError[RTPATH_MAX + 128] = "";
4606 int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
4607 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
4608 RTLOGDEST_FILE, szError, sizeof(szError), logFile.raw());
4609 if (RT_SUCCESS(vrc))
4610 {
4611 /* some introductory information */
4612 RTTIMESPEC timeSpec;
4613 char szTmp[256];
4614 RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
4615 RTLogRelLogger(loggerRelease, 0, ~0U,
4616 "VirtualBox %s r%u %s (%s %s) release log\n"
4617#ifdef VBOX_BLEEDING_EDGE
4618 "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
4619#endif
4620 "Log opened %s\n",
4621 VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
4622 __DATE__, __TIME__, szTmp);
4623
4624 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
4625 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4626 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
4627 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
4628 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4629 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
4630 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
4631 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4632 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
4633 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
4634 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
4635 RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
4636 RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
4637 uHostRamMb, uHostRamAvailMb);
4638 /* the package type is interesting for Linux distributions */
4639 char szExecName[RTPATH_MAX];
4640 char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
4641 RTLogRelLogger(loggerRelease, 0, ~0U,
4642 "Executable: %s\n"
4643 "Process ID: %u\n"
4644 "Package type: %s"
4645#ifdef VBOX_OSE
4646 " (OSE)"
4647#endif
4648 "\n",
4649 pszExecName ? pszExecName : "unknown",
4650 RTProcSelf(),
4651 VBOX_PACKAGE_STRING);
4652
4653 /* register this logger as the release logger */
4654 RTLogRelSetDefaultInstance(loggerRelease);
4655 hrc = S_OK;
4656 }
4657 else
4658 hrc = setError (E_FAIL,
4659 tr ("Failed to open release log (%s, %Rrc)"), szError, vrc);
4660
4661 return hrc;
4662}
4663
4664/**
4665 * Common worker for PowerUp and PowerUpPaused.
4666 *
4667 * @returns COM status code.
4668 *
4669 * @param aProgress Where to return the progress object.
4670 * @param aPaused true if PowerUpPaused called.
4671 *
4672 * @todo move down to powerDown();
4673 */
4674HRESULT Console::powerUp (IProgress **aProgress, bool aPaused)
4675{
4676 if (aProgress == NULL)
4677 return E_POINTER;
4678
4679 LogFlowThisFuncEnter();
4680 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
4681
4682 AutoCaller autoCaller(this);
4683 CheckComRCReturnRC(autoCaller.rc());
4684
4685 AutoWriteLock alock(this);
4686
4687 if (Global::IsOnlineOrTransient (mMachineState))
4688 return setError(VBOX_E_INVALID_VM_STATE,
4689 tr ("Virtual machine is already running or busy "
4690 "(machine state: %d)"), mMachineState);
4691
4692 HRESULT rc = S_OK;
4693
4694 /* the network cards will undergo a quick consistency check */
4695 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
4696 {
4697 ComPtr<INetworkAdapter> adapter;
4698 mMachine->GetNetworkAdapter (slot, adapter.asOutParam());
4699 BOOL enabled = FALSE;
4700 adapter->COMGETTER(Enabled) (&enabled);
4701 if (!enabled)
4702 continue;
4703
4704 NetworkAttachmentType_T netattach;
4705 adapter->COMGETTER(AttachmentType)(&netattach);
4706 switch (netattach)
4707 {
4708 case NetworkAttachmentType_Bridged:
4709 {
4710#ifdef RT_OS_WINDOWS
4711 /* a valid host interface must have been set */
4712 Bstr hostif;
4713 adapter->COMGETTER(HostInterface)(hostif.asOutParam());
4714 if (!hostif)
4715 {
4716 return setError (VBOX_E_HOST_ERROR,
4717 tr ("VM cannot start because host interface networking "
4718 "requires a host interface name to be set"));
4719 }
4720 ComPtr<IVirtualBox> virtualBox;
4721 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
4722 ComPtr<IHost> host;
4723 virtualBox->COMGETTER(Host)(host.asOutParam());
4724 ComPtr<IHostNetworkInterface> hostInterface;
4725 if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif, hostInterface.asOutParam())))
4726 {
4727 return setError (VBOX_E_HOST_ERROR,
4728 tr ("VM cannot start because the host interface '%ls' "
4729 "does not exist"),
4730 hostif.raw());
4731 }
4732#endif /* RT_OS_WINDOWS */
4733 break;
4734 }
4735 default:
4736 break;
4737 }
4738 }
4739
4740 /* Read console data stored in the saved state file (if not yet done) */
4741 rc = loadDataFromSavedState();
4742 CheckComRCReturnRC(rc);
4743
4744 /* Check all types of shared folders and compose a single list */
4745 SharedFolderDataMap sharedFolders;
4746 {
4747 /* first, insert global folders */
4748 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
4749 it != mGlobalSharedFolders.end(); ++ it)
4750 sharedFolders [it->first] = it->second;
4751
4752 /* second, insert machine folders */
4753 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
4754 it != mMachineSharedFolders.end(); ++ it)
4755 sharedFolders [it->first] = it->second;
4756
4757 /* third, insert console folders */
4758 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
4759 it != mSharedFolders.end(); ++ it)
4760 sharedFolders [it->first] = SharedFolderData(it->second->hostPath(), it->second->writable());
4761 }
4762
4763 Bstr savedStateFile;
4764
4765 /*
4766 * Saved VMs will have to prove that their saved states seem kosher.
4767 */
4768 if (mMachineState == MachineState_Saved)
4769 {
4770 rc = mMachine->COMGETTER(StateFilePath) (savedStateFile.asOutParam());
4771 CheckComRCReturnRC(rc);
4772 ComAssertRet (!!savedStateFile, E_FAIL);
4773 int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
4774 if (VBOX_FAILURE(vrc))
4775 return setError(VBOX_E_FILE_ERROR,
4776 tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). "
4777 "Discard the saved state prior to starting the VM"),
4778 savedStateFile.raw(), vrc);
4779 }
4780
4781 /* create a progress object to track progress of this operation */
4782 ComObjPtr<Progress> powerupProgress;
4783 powerupProgress.createObject();
4784 Bstr progressDesc;
4785 if (mMachineState == MachineState_Saved)
4786 progressDesc = tr ("Restoring virtual machine");
4787 else
4788 progressDesc = tr ("Starting virtual machine");
4789 rc = powerupProgress->init (static_cast <IConsole *> (this),
4790 progressDesc, FALSE /* aCancelable */);
4791 CheckComRCReturnRC(rc);
4792
4793 /* setup task object and thread to carry out the operation
4794 * asynchronously */
4795
4796 std::auto_ptr <VMPowerUpTask> task (new VMPowerUpTask (this, powerupProgress));
4797 ComAssertComRCRetRC (task->rc());
4798
4799 task->mSetVMErrorCallback = setVMErrorCallback;
4800 task->mConfigConstructor = configConstructor;
4801 task->mSharedFolders = sharedFolders;
4802 task->mStartPaused = aPaused;
4803 if (mMachineState == MachineState_Saved)
4804 task->mSavedStateFile = savedStateFile;
4805
4806 /* Reset differencing hard disks for which autoReset is true */
4807 {
4808 com::SafeIfaceArray<IHardDiskAttachment> atts;
4809 rc = mMachine->
4810 COMGETTER(HardDiskAttachments) (ComSafeArrayAsOutParam (atts));
4811 CheckComRCReturnRC(rc);
4812
4813 for (size_t i = 0; i < atts.size(); ++ i)
4814 {
4815 ComPtr<IHardDisk> hardDisk;
4816 rc = atts [i]->COMGETTER(HardDisk) (hardDisk.asOutParam());
4817 CheckComRCReturnRC(rc);
4818
4819 /* save for later use on the powerup thread */
4820 task->hardDisks.push_back (hardDisk);
4821
4822 /* needs autoreset? */
4823 BOOL autoReset = FALSE;
4824 rc = hardDisk->COMGETTER(AutoReset)(&autoReset);
4825 CheckComRCReturnRC(rc);
4826
4827 if (autoReset)
4828 {
4829 ComPtr<IProgress> resetProgress;
4830 rc = hardDisk->Reset (resetProgress.asOutParam());
4831 CheckComRCReturnRC(rc);
4832
4833 /* save for later use on the powerup thread */
4834 task->hardDiskProgresses.push_back (resetProgress);
4835 }
4836 }
4837 }
4838
4839 rc = consoleInitReleaseLog (mMachine);
4840 CheckComRCReturnRC(rc);
4841
4842 /* pass the progress object to the caller if requested */
4843 if (aProgress)
4844 {
4845 if (task->hardDiskProgresses.size() == 0)
4846 {
4847 /* there are no other operations to track, return the powerup
4848 * progress only */
4849 powerupProgress.queryInterfaceTo(aProgress);
4850 }
4851 else
4852 {
4853 /* create a combined progress object */
4854 ComObjPtr<CombinedProgress> progress;
4855 progress.createObject();
4856 VMPowerUpTask::ProgressList progresses (task->hardDiskProgresses);
4857 progresses.push_back (ComPtr<IProgress> (powerupProgress));
4858 rc = progress->init (static_cast <IConsole *> (this),
4859 progressDesc, progresses.begin(),
4860 progresses.end());
4861 AssertComRCReturnRC(rc);
4862 progress.queryInterfaceTo(aProgress);
4863 }
4864 }
4865
4866 int vrc = RTThreadCreate (NULL, Console::powerUpThread, (void *) task.get(),
4867 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
4868
4869 ComAssertMsgRCRet (vrc, ("Could not create VMPowerUp thread (%Rrc)", vrc),
4870 E_FAIL);
4871
4872 /* task is now owned by powerUpThread(), so release it */
4873 task.release();
4874
4875 /* finally, set the state: no right to fail in this method afterwards
4876 * since we've already started the thread and it is now responsible for
4877 * any error reporting and appropriate state change! */
4878
4879 if (mMachineState == MachineState_Saved)
4880 setMachineState (MachineState_Restoring);
4881 else
4882 setMachineState (MachineState_Starting);
4883
4884 LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
4885 LogFlowThisFuncLeave();
4886 return S_OK;
4887}
4888
4889/**
4890 * Internal power off worker routine.
4891 *
4892 * This method may be called only at certain places with the following meaning
4893 * as shown below:
4894 *
4895 * - if the machine state is either Running or Paused, a normal
4896 * Console-initiated powerdown takes place (e.g. PowerDown());
4897 * - if the machine state is Saving, saveStateThread() has successfully done its
4898 * job;
4899 * - if the machine state is Starting or Restoring, powerUpThread() has failed
4900 * to start/load the VM;
4901 * - if the machine state is Stopping, the VM has powered itself off (i.e. not
4902 * as a result of the powerDown() call).
4903 *
4904 * Calling it in situations other than the above will cause unexpected behavior.
4905 *
4906 * Note that this method should be the only one that destroys mpVM and sets it
4907 * to NULL.
4908 *
4909 * @param aProgress Progress object to run (may be NULL).
4910 *
4911 * @note Locks this object for writing.
4912 *
4913 * @note Never call this method from a thread that called addVMCaller() or
4914 * instantiated an AutoVMCaller object; first call releaseVMCaller() or
4915 * release(). Otherwise it will deadlock.
4916 */
4917HRESULT Console::powerDown (Progress *aProgress /*= NULL*/)
4918{
4919 LogFlowThisFuncEnter();
4920
4921 AutoCaller autoCaller(this);
4922 AssertComRCReturnRC(autoCaller.rc());
4923
4924 AutoWriteLock alock(this);
4925
4926 /* Total # of steps for the progress object. Must correspond to the
4927 * number of "advance percent count" comments in this method! */
4928 enum { StepCount = 7 };
4929 /* current step */
4930 ULONG step = 0;
4931
4932 HRESULT rc = S_OK;
4933 int vrc = VINF_SUCCESS;
4934
4935 /* sanity */
4936 Assert (mVMDestroying == false);
4937
4938 Assert (mpVM != NULL);
4939
4940 AssertMsg (mMachineState == MachineState_Running ||
4941 mMachineState == MachineState_Paused ||
4942 mMachineState == MachineState_Stuck ||
4943 mMachineState == MachineState_Saving ||
4944 mMachineState == MachineState_Starting ||
4945 mMachineState == MachineState_Restoring ||
4946 mMachineState == MachineState_Stopping,
4947 ("Invalid machine state: %d\n", mMachineState));
4948
4949 LogRel (("Console::powerDown(): A request to power off the VM has been "
4950 "issued (mMachineState=%d, InUninit=%d)\n",
4951 mMachineState, autoCaller.state() == InUninit));
4952
4953 /* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
4954 * VM has already powered itself off in vmstateChangeCallback() and is just
4955 * notifying Console about that. In case of Starting or Restoring,
4956 * powerUpThread() is calling us on failure, so the VM is already off at
4957 * that point. */
4958 if (!mVMPoweredOff &&
4959 (mMachineState == MachineState_Starting ||
4960 mMachineState == MachineState_Restoring))
4961 mVMPoweredOff = true;
4962
4963 /* go to Stopping state if not already there. Note that we don't go from
4964 * Saving/Restoring to Stopping because vmstateChangeCallback() needs it to
4965 * set the state to Saved on VMSTATE_TERMINATED. In terms of protecting from
4966 * inappropriate operations while leaving the lock below, Saving or
4967 * Restoring should be fine too */
4968 if (mMachineState != MachineState_Saving &&
4969 mMachineState != MachineState_Restoring &&
4970 mMachineState != MachineState_Stopping)
4971 setMachineState (MachineState_Stopping);
4972
4973 /* ----------------------------------------------------------------------
4974 * DONE with necessary state changes, perform the power down actions (it's
4975 * safe to leave the object lock now if needed)
4976 * ---------------------------------------------------------------------- */
4977
4978 /* Stop the VRDP server to prevent new clients connection while VM is being
4979 * powered off. */
4980 if (mConsoleVRDPServer)
4981 {
4982 LogFlowThisFunc(("Stopping VRDP server...\n"));
4983
4984 /* Leave the lock since EMT will call us back as addVMCaller()
4985 * in updateDisplayData(). */
4986 alock.leave();
4987
4988 mConsoleVRDPServer->Stop();
4989
4990 alock.enter();
4991 }
4992
4993 /* advance percent count */
4994 if (aProgress)
4995 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
4996
4997#ifdef VBOX_WITH_HGCM
4998
4999# ifdef VBOX_WITH_GUEST_PROPS
5000
5001 /* Save all guest property store entries to the machine XML file */
5002 com::SafeArray<BSTR> namesOut;
5003 com::SafeArray<BSTR> valuesOut;
5004 com::SafeArray<ULONG64> timestampsOut;
5005 com::SafeArray<BSTR> flagsOut;
5006 Bstr pattern("");
5007 if (pattern.isNull())
5008 rc = E_OUTOFMEMORY;
5009 else
5010 rc = doEnumerateGuestProperties (Bstr (""), ComSafeArrayAsOutParam (namesOut),
5011 ComSafeArrayAsOutParam (valuesOut),
5012 ComSafeArrayAsOutParam (timestampsOut),
5013 ComSafeArrayAsOutParam (flagsOut));
5014 if (SUCCEEDED(rc))
5015 {
5016 try
5017 {
5018 std::vector <BSTR> names;
5019 std::vector <BSTR> values;
5020 std::vector <ULONG64> timestamps;
5021 std::vector <BSTR> flags;
5022 for (unsigned i = 0; i < namesOut.size(); ++i)
5023 {
5024 uint32_t fFlags;
5025 guestProp::validateFlags (Utf8Str(flagsOut[i]).raw(), &fFlags);
5026 if ( !( fFlags & guestProp::TRANSIENT)
5027 || (mMachineState == MachineState_Saving)
5028 )
5029 {
5030 names.push_back(namesOut[i]);
5031 values.push_back(valuesOut[i]);
5032 timestamps.push_back(timestampsOut[i]);
5033 flags.push_back(flagsOut[i]);
5034 }
5035 }
5036 com::SafeArray<BSTR> namesIn (names);
5037 com::SafeArray<BSTR> valuesIn (values);
5038 com::SafeArray<ULONG64> timestampsIn (timestamps);
5039 com::SafeArray<BSTR> flagsIn (flags);
5040 if ( namesIn.isNull()
5041 || valuesIn.isNull()
5042 || timestampsIn.isNull()
5043 || flagsIn.isNull()
5044 )
5045 throw std::bad_alloc();
5046 /* PushGuestProperties() calls DiscardSettings(), which calls us back */
5047 alock.leave();
5048 mControl->PushGuestProperties (ComSafeArrayAsInParam (namesIn),
5049 ComSafeArrayAsInParam (valuesIn),
5050 ComSafeArrayAsInParam (timestampsIn),
5051 ComSafeArrayAsInParam (flagsIn));
5052 alock.enter();
5053 }
5054 catch (std::bad_alloc)
5055 {
5056 rc = E_OUTOFMEMORY;
5057 }
5058 }
5059
5060 /* advance percent count */
5061 if (aProgress)
5062 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5063
5064# endif /* VBOX_WITH_GUEST_PROPS defined */
5065
5066 /* Shutdown HGCM services before stopping the guest, because they might
5067 * need a cleanup. */
5068 if (mVMMDev)
5069 {
5070 LogFlowThisFunc(("Shutdown HGCM...\n"));
5071
5072 /* Leave the lock since EMT will call us back as addVMCaller() */
5073 alock.leave();
5074
5075 mVMMDev->hgcmShutdown ();
5076
5077 alock.enter();
5078 }
5079
5080 /* advance percent count */
5081 if (aProgress)
5082 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5083
5084#endif /* VBOX_WITH_HGCM */
5085
5086 /* ----------------------------------------------------------------------
5087 * Now, wait for all mpVM callers to finish their work if there are still
5088 * some on other threads. NO methods that need mpVM (or initiate other calls
5089 * that need it) may be called after this point
5090 * ---------------------------------------------------------------------- */
5091
5092 if (mVMCallers > 0)
5093 {
5094 /* go to the destroying state to prevent from adding new callers */
5095 mVMDestroying = true;
5096
5097 /* lazy creation */
5098 if (mVMZeroCallersSem == NIL_RTSEMEVENT)
5099 RTSemEventCreate (&mVMZeroCallersSem);
5100
5101 LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
5102 mVMCallers));
5103
5104 alock.leave();
5105
5106 RTSemEventWait (mVMZeroCallersSem, RT_INDEFINITE_WAIT);
5107
5108 alock.enter();
5109 }
5110
5111 /* advance percent count */
5112 if (aProgress)
5113 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5114
5115 vrc = VINF_SUCCESS;
5116
5117 /* Power off the VM if not already done that */
5118 if (!mVMPoweredOff)
5119 {
5120 LogFlowThisFunc(("Powering off the VM...\n"));
5121
5122 /* Leave the lock since EMT will call us back on VMR3PowerOff() */
5123 alock.leave();
5124
5125 vrc = VMR3PowerOff (mpVM);
5126
5127 /* Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
5128 * VM-(guest-)initiated power off happened in parallel a ms before this
5129 * call. So far, we let this error pop up on the user's side. */
5130
5131 alock.enter();
5132
5133 }
5134 else
5135 {
5136 /* reset the flag for further re-use */
5137 mVMPoweredOff = false;
5138 }
5139
5140 /* advance percent count */
5141 if (aProgress)
5142 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5143
5144 LogFlowThisFunc(("Ready for VM destruction.\n"));
5145
5146 /* If we are called from Console::uninit(), then try to destroy the VM even
5147 * on failure (this will most likely fail too, but what to do?..) */
5148 if (VBOX_SUCCESS (vrc) || autoCaller.state() == InUninit)
5149 {
5150 /* If the machine has an USB comtroller, release all USB devices
5151 * (symmetric to the code in captureUSBDevices()) */
5152 bool fHasUSBController = false;
5153 {
5154 PPDMIBASE pBase;
5155 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);
5156 if (VBOX_SUCCESS (vrc))
5157 {
5158 fHasUSBController = true;
5159 detachAllUSBDevices (false /* aDone */);
5160 }
5161 }
5162
5163 /* Now we've got to destroy the VM as well. (mpVM is not valid beyond
5164 * this point). We leave the lock before calling VMR3Destroy() because
5165 * it will result into calling destructors of drivers associated with
5166 * Console children which may in turn try to lock Console (e.g. by
5167 * instantiating SafeVMPtr to access mpVM). It's safe here because
5168 * mVMDestroying is set which should prevent any activity. */
5169
5170 /* Set mpVM to NULL early just in case if some old code is not using
5171 * addVMCaller()/releaseVMCaller(). */
5172 PVM pVM = mpVM;
5173 mpVM = NULL;
5174
5175 LogFlowThisFunc(("Destroying the VM...\n"));
5176
5177 alock.leave();
5178
5179 vrc = VMR3Destroy (pVM);
5180
5181 /* take the lock again */
5182 alock.enter();
5183
5184 /* advance percent count */
5185 if (aProgress)
5186 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5187
5188 if (VBOX_SUCCESS (vrc))
5189 {
5190 LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
5191 mMachineState));
5192 /* Note: the Console-level machine state change happens on the
5193 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
5194 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()
5195 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
5196 * occurred yet. This is okay, because mMachineState is already
5197 * Stopping in this case, so any other attempt to call PowerDown()
5198 * will be rejected. */
5199 }
5200 else
5201 {
5202 /* bad bad bad, but what to do? */
5203 mpVM = pVM;
5204 rc = setError (VBOX_E_VM_ERROR,
5205 tr ("Could not destroy the machine. (Error: %Rrc)"), vrc);
5206 }
5207
5208 /* Complete the detaching of the USB devices. */
5209 if (fHasUSBController)
5210 detachAllUSBDevices (true /* aDone */);
5211
5212 /* advance percent count */
5213 if (aProgress)
5214 aProgress->setCurrentOperationProgress(99 * (++ step) / StepCount );
5215 }
5216 else
5217 {
5218 rc = setError (VBOX_E_VM_ERROR,
5219 tr ("Could not power off the machine. (Error: %Rrc)"), vrc);
5220 }
5221
5222 /* Finished with destruction. Note that if something impossible happened and
5223 * we've failed to destroy the VM, mVMDestroying will remain true and
5224 * mMachineState will be something like Stopping, so most Console methods
5225 * will return an error to the caller. */
5226 if (mpVM == NULL)
5227 mVMDestroying = false;
5228
5229 if (SUCCEEDED(rc))
5230 {
5231 /* uninit dynamically allocated members of mCallbackData */
5232 if (mCallbackData.mpsc.valid)
5233 {
5234 if (mCallbackData.mpsc.shape != NULL)
5235 RTMemFree (mCallbackData.mpsc.shape);
5236 }
5237 memset (&mCallbackData, 0, sizeof (mCallbackData));
5238 }
5239
5240 /* complete the progress */
5241 if (aProgress)
5242 aProgress->notifyComplete (rc);
5243
5244 LogFlowThisFuncLeave();
5245 return rc;
5246}
5247
5248/**
5249 * @note Locks this object for writing.
5250 */
5251HRESULT Console::setMachineState (MachineState_T aMachineState,
5252 bool aUpdateServer /* = true */)
5253{
5254 AutoCaller autoCaller(this);
5255 AssertComRCReturnRC(autoCaller.rc());
5256
5257 AutoWriteLock alock(this);
5258
5259 HRESULT rc = S_OK;
5260
5261 if (mMachineState != aMachineState)
5262 {
5263 LogFlowThisFunc(("machineState=%d\n", aMachineState));
5264 mMachineState = aMachineState;
5265
5266 /// @todo (dmik)
5267 // possibly, we need to redo onStateChange() using the dedicated
5268 // Event thread, like it is done in VirtualBox. This will make it
5269 // much safer (no deadlocks possible if someone tries to use the
5270 // console from the callback), however, listeners will lose the
5271 // ability to synchronously react to state changes (is it really
5272 // necessary??)
5273 LogFlowThisFunc(("Doing onStateChange()...\n"));
5274 onStateChange (aMachineState);
5275 LogFlowThisFunc(("Done onStateChange()\n"));
5276
5277 if (aUpdateServer)
5278 {
5279 /* Server notification MUST be done from under the lock; otherwise
5280 * the machine state here and on the server might go out of sync
5281 * whihc can lead to various unexpected results (like the machine
5282 * state being >= MachineState_Running on the server, while the
5283 * session state is already SessionState_Closed at the same time
5284 * there).
5285 *
5286 * Cross-lock conditions should be carefully watched out: calling
5287 * UpdateState we will require Machine and SessionMachine locks
5288 * (remember that here we're holding the Console lock here, and also
5289 * all locks that have been entered by the thread before calling
5290 * this method).
5291 */
5292 LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
5293 rc = mControl->UpdateState (aMachineState);
5294 LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
5295 }
5296 }
5297
5298 return rc;
5299}
5300
5301/**
5302 * Searches for a shared folder with the given logical name
5303 * in the collection of shared folders.
5304 *
5305 * @param aName logical name of the shared folder
5306 * @param aSharedFolder where to return the found object
5307 * @param aSetError whether to set the error info if the folder is
5308 * not found
5309 * @return
5310 * S_OK when found or E_INVALIDARG when not found
5311 *
5312 * @note The caller must lock this object for writing.
5313 */
5314HRESULT Console::findSharedFolder (CBSTR aName,
5315 ComObjPtr<SharedFolder> &aSharedFolder,
5316 bool aSetError /* = false */)
5317{
5318 /* sanity check */
5319 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5320
5321 SharedFolderMap::const_iterator it = mSharedFolders.find (aName);
5322 if (it != mSharedFolders.end())
5323 {
5324 aSharedFolder = it->second;
5325 return S_OK;
5326 }
5327
5328 if (aSetError)
5329 setError (VBOX_E_FILE_ERROR,
5330 tr ("Could not find a shared folder named '%ls'."), aName);
5331
5332 return VBOX_E_FILE_ERROR;
5333}
5334
5335/**
5336 * Fetches the list of global or machine shared folders from the server.
5337 *
5338 * @param aGlobal true to fetch global folders.
5339 *
5340 * @note The caller must lock this object for writing.
5341 */
5342HRESULT Console::fetchSharedFolders (BOOL aGlobal)
5343{
5344 /* sanity check */
5345 AssertReturn(AutoCaller (this).state() == InInit ||
5346 isWriteLockOnCurrentThread(), E_FAIL);
5347
5348 /* protect mpVM (if not NULL) */
5349 AutoVMCallerQuietWeak autoVMCaller (this);
5350
5351 HRESULT rc = S_OK;
5352
5353 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();
5354
5355 if (aGlobal)
5356 {
5357 /// @todo grab & process global folders when they are done
5358 }
5359 else
5360 {
5361 SharedFolderDataMap oldFolders;
5362 if (online)
5363 oldFolders = mMachineSharedFolders;
5364
5365 mMachineSharedFolders.clear();
5366
5367 SafeIfaceArray<ISharedFolder> folders;
5368 rc = mMachine->COMGETTER(SharedFolders) (ComSafeArrayAsOutParam(folders));
5369 AssertComRCReturnRC(rc);
5370
5371 for (size_t i = 0; i < folders.size(); ++i)
5372 {
5373 ComPtr<ISharedFolder> folder = folders[i];
5374
5375 Bstr name;
5376 Bstr hostPath;
5377 BOOL writable;
5378
5379 rc = folder->COMGETTER(Name) (name.asOutParam());
5380 CheckComRCBreakRC (rc);
5381 rc = folder->COMGETTER(HostPath) (hostPath.asOutParam());
5382 CheckComRCBreakRC (rc);
5383 rc = folder->COMGETTER(Writable) (&writable);
5384
5385 mMachineSharedFolders.insert (std::make_pair (name, SharedFolderData (hostPath, writable)));
5386
5387 /* send changes to HGCM if the VM is running */
5388 /// @todo report errors as runtime warnings through VMSetError
5389 if (online)
5390 {
5391 SharedFolderDataMap::iterator it = oldFolders.find (name);
5392 if (it == oldFolders.end() || it->second.mHostPath != hostPath)
5393 {
5394 /* a new machine folder is added or
5395 * the existing machine folder is changed */
5396 if (mSharedFolders.find (name) != mSharedFolders.end())
5397 ; /* the console folder exists, nothing to do */
5398 else
5399 {
5400 /* remove the old machine folder (when changed)
5401 * or the global folder if any (when new) */
5402 if (it != oldFolders.end() ||
5403 mGlobalSharedFolders.find (name) !=
5404 mGlobalSharedFolders.end())
5405 rc = removeSharedFolder (name);
5406 /* create the new machine folder */
5407 rc = createSharedFolder (name, SharedFolderData (hostPath, writable));
5408 }
5409 }
5410 /* forget the processed (or identical) folder */
5411 if (it != oldFolders.end())
5412 oldFolders.erase (it);
5413
5414 rc = S_OK;
5415 }
5416 }
5417
5418 AssertComRCReturnRC(rc);
5419
5420 /* process outdated (removed) folders */
5421 /// @todo report errors as runtime warnings through VMSetError
5422 if (online)
5423 {
5424 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
5425 it != oldFolders.end(); ++ it)
5426 {
5427 if (mSharedFolders.find (it->first) != mSharedFolders.end())
5428 ; /* the console folder exists, nothing to do */
5429 else
5430 {
5431 /* remove the outdated machine folder */
5432 rc = removeSharedFolder (it->first);
5433 /* create the global folder if there is any */
5434 SharedFolderDataMap::const_iterator git =
5435 mGlobalSharedFolders.find (it->first);
5436 if (git != mGlobalSharedFolders.end())
5437 rc = createSharedFolder (git->first, git->second);
5438 }
5439 }
5440
5441 rc = S_OK;
5442 }
5443 }
5444
5445 return rc;
5446}
5447
5448/**
5449 * Searches for a shared folder with the given name in the list of machine
5450 * shared folders and then in the list of the global shared folders.
5451 *
5452 * @param aName Name of the folder to search for.
5453 * @param aIt Where to store the pointer to the found folder.
5454 * @return @c true if the folder was found and @c false otherwise.
5455 *
5456 * @note The caller must lock this object for reading.
5457 */
5458bool Console::findOtherSharedFolder (IN_BSTR aName,
5459 SharedFolderDataMap::const_iterator &aIt)
5460{
5461 /* sanity check */
5462 AssertReturn(isWriteLockOnCurrentThread(), false);
5463
5464 /* first, search machine folders */
5465 aIt = mMachineSharedFolders.find (aName);
5466 if (aIt != mMachineSharedFolders.end())
5467 return true;
5468
5469 /* second, search machine folders */
5470 aIt = mGlobalSharedFolders.find (aName);
5471 if (aIt != mGlobalSharedFolders.end())
5472 return true;
5473
5474 return false;
5475}
5476
5477/**
5478 * Calls the HGCM service to add a shared folder definition.
5479 *
5480 * @param aName Shared folder name.
5481 * @param aHostPath Shared folder path.
5482 *
5483 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5484 * @note Doesn't lock anything.
5485 */
5486HRESULT Console::createSharedFolder (CBSTR aName, SharedFolderData aData)
5487{
5488 ComAssertRet (aName && *aName, E_FAIL);
5489 ComAssertRet (aData.mHostPath, E_FAIL);
5490
5491 /* sanity checks */
5492 AssertReturn(mpVM, E_FAIL);
5493 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5494
5495 VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING];
5496 SHFLSTRING *pFolderName, *pMapName;
5497 size_t cbString;
5498
5499 Log (("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
5500
5501 cbString = (RTUtf16Len (aData.mHostPath) + 1) * sizeof (RTUTF16);
5502 if (cbString >= UINT16_MAX)
5503 return setError (E_INVALIDARG, tr ("The name is too long"));
5504 pFolderName = (SHFLSTRING *) RTMemAllocZ (sizeof (SHFLSTRING) + cbString);
5505 Assert (pFolderName);
5506 memcpy (pFolderName->String.ucs2, aData.mHostPath, cbString);
5507
5508 pFolderName->u16Size = (uint16_t)cbString;
5509 pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
5510
5511 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
5512 parms[0].u.pointer.addr = pFolderName;
5513 parms[0].u.pointer.size = sizeof (SHFLSTRING) + (uint16_t)cbString;
5514
5515 cbString = (RTUtf16Len (aName) + 1) * sizeof (RTUTF16);
5516 if (cbString >= UINT16_MAX)
5517 {
5518 RTMemFree (pFolderName);
5519 return setError (E_INVALIDARG, tr ("The host path is too long"));
5520 }
5521 pMapName = (SHFLSTRING *) RTMemAllocZ (sizeof(SHFLSTRING) + cbString);
5522 Assert (pMapName);
5523 memcpy (pMapName->String.ucs2, aName, cbString);
5524
5525 pMapName->u16Size = (uint16_t)cbString;
5526 pMapName->u16Length = (uint16_t)cbString - sizeof (RTUTF16);
5527
5528 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
5529 parms[1].u.pointer.addr = pMapName;
5530 parms[1].u.pointer.size = sizeof (SHFLSTRING) + (uint16_t)cbString;
5531
5532 parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
5533 parms[2].u.uint32 = aData.mWritable;
5534
5535 int vrc = mVMMDev->hgcmHostCall ("VBoxSharedFolders",
5536 SHFL_FN_ADD_MAPPING,
5537 SHFL_CPARMS_ADD_MAPPING, &parms[0]);
5538 RTMemFree (pFolderName);
5539 RTMemFree (pMapName);
5540
5541 if (VBOX_FAILURE (vrc))
5542 return setError (E_FAIL,
5543 tr ("Could not create a shared folder '%ls' "
5544 "mapped to '%ls' (%Rrc)"),
5545 aName, aData.mHostPath.raw(), vrc);
5546
5547 return S_OK;
5548}
5549
5550/**
5551 * Calls the HGCM service to remove the shared folder definition.
5552 *
5553 * @param aName Shared folder name.
5554 *
5555 * @note Must be called from under AutoVMCaller and when mpVM != NULL!
5556 * @note Doesn't lock anything.
5557 */
5558HRESULT Console::removeSharedFolder (CBSTR aName)
5559{
5560 ComAssertRet (aName && *aName, E_FAIL);
5561
5562 /* sanity checks */
5563 AssertReturn(mpVM, E_FAIL);
5564 AssertReturn(mVMMDev->isShFlActive(), E_FAIL);
5565
5566 VBOXHGCMSVCPARM parms;
5567 SHFLSTRING *pMapName;
5568 size_t cbString;
5569
5570 Log (("Removing shared folder '%ls'\n", aName));
5571
5572 cbString = (RTUtf16Len (aName) + 1) * sizeof (RTUTF16);
5573 if (cbString >= UINT16_MAX)
5574 return setError (E_INVALIDARG, tr ("The name is too long"));
5575 pMapName = (SHFLSTRING *) RTMemAllocZ (sizeof (SHFLSTRING) + cbString);
5576 Assert (pMapName);
5577 memcpy (pMapName->String.ucs2, aName, cbString);
5578
5579 pMapName->u16Size = (uint16_t)cbString;
5580 pMapName->u16Length = (uint16_t)cbString - sizeof (RTUTF16);
5581
5582 parms.type = VBOX_HGCM_SVC_PARM_PTR;
5583 parms.u.pointer.addr = pMapName;
5584 parms.u.pointer.size = sizeof (SHFLSTRING) + (uint16_t)cbString;
5585
5586 int vrc = mVMMDev->hgcmHostCall ("VBoxSharedFolders",
5587 SHFL_FN_REMOVE_MAPPING,
5588 1, &parms);
5589 RTMemFree(pMapName);
5590 if (VBOX_FAILURE (vrc))
5591 return setError (E_FAIL,
5592 tr ("Could not remove the shared folder '%ls' (%Rrc)"),
5593 aName, vrc);
5594
5595 return S_OK;
5596}
5597
5598/**
5599 * VM state callback function. Called by the VMM
5600 * using its state machine states.
5601 *
5602 * Primarily used to handle VM initiated power off, suspend and state saving,
5603 * but also for doing termination completed work (VMSTATE_TERMINATE).
5604 *
5605 * In general this function is called in the context of the EMT.
5606 *
5607 * @param aVM The VM handle.
5608 * @param aState The new state.
5609 * @param aOldState The old state.
5610 * @param aUser The user argument (pointer to the Console object).
5611 *
5612 * @note Locks the Console object for writing.
5613 */
5614DECLCALLBACK(void)
5615Console::vmstateChangeCallback(PVM aVM, VMSTATE aState, VMSTATE aOldState,
5616 void *aUser)
5617{
5618 LogFlowFunc(("Changing state from %d to %d (aVM=%p)\n",
5619 aOldState, aState, aVM));
5620
5621 Console *that = static_cast<Console *> (aUser);
5622 AssertReturnVoid(that);
5623
5624 AutoCaller autoCaller(that);
5625
5626 /* Note that we must let this method proceed even if Console::uninit() has
5627 * been already called. In such case this VMSTATE change is a result of:
5628 * 1) powerDown() called from uninit() itself, or
5629 * 2) VM-(guest-)initiated power off. */
5630 AssertReturnVoid( autoCaller.isOk()
5631 || autoCaller.state() == InUninit);
5632
5633 switch (aState)
5634 {
5635 /*
5636 * The VM has terminated
5637 */
5638 case VMSTATE_OFF:
5639 {
5640 AutoWriteLock alock(that);
5641
5642 if (that->mVMStateChangeCallbackDisabled)
5643 break;
5644
5645 /* Do we still think that it is running? It may happen if this is a
5646 * VM-(guest-)initiated shutdown/poweroff.
5647 */
5648 if ( that->mMachineState != MachineState_Stopping
5649 && that->mMachineState != MachineState_Saving
5650 && that->mMachineState != MachineState_Restoring)
5651 {
5652 LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
5653
5654 /* prevent powerDown() from calling VMR3PowerOff() again */
5655 Assert(that->mVMPoweredOff == false);
5656 that->mVMPoweredOff = true;
5657
5658 /* we are stopping now */
5659 that->setMachineState(MachineState_Stopping);
5660
5661 /* Setup task object and thread to carry out the operation
5662 * asynchronously (if we call powerDown() right here but there
5663 * is one or more mpVM callers (added with addVMCaller()) we'll
5664 * deadlock).
5665 */
5666 std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
5667 true /* aUsesVMPtr */));
5668
5669 /* If creating a task is falied, this can currently mean one of
5670 * two: either Console::uninit() has been called just a ms
5671 * before (so a powerDown() call is already on the way), or
5672 * powerDown() itself is being already executed. Just do
5673 * nothing.
5674 */
5675 if (!task->isOk())
5676 {
5677 LogFlowFunc(("Console is already being uninitialized.\n"));
5678 break;
5679 }
5680
5681 int vrc = RTThreadCreate(NULL, Console::powerDownThread,
5682 (void *) task.get(), 0,
5683 RTTHREADTYPE_MAIN_WORKER, 0,
5684 "VMPowerDown");
5685 AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
5686
5687 /* task is now owned by powerDownThread(), so release it */
5688 task.release();
5689 }
5690 break;
5691 }
5692
5693 /* The VM has been completely destroyed.
5694 *
5695 * Note: This state change can happen at two points:
5696 * 1) At the end of VMR3Destroy() if it was not called from EMT.
5697 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was
5698 * called by EMT.
5699 */
5700 case VMSTATE_TERMINATED:
5701 {
5702 AutoWriteLock alock(that);
5703
5704 if (that->mVMStateChangeCallbackDisabled)
5705 break;
5706
5707 /* Terminate host interface networking. If aVM is NULL, we've been
5708 * manually called from powerUpThread() either before calling
5709 * VMR3Create() or after VMR3Create() failed, so no need to touch
5710 * networking.
5711 */
5712 if (aVM)
5713 that->powerDownHostInterfaces();
5714
5715 /* From now on the machine is officially powered down or remains in
5716 * the Saved state.
5717 */
5718 switch (that->mMachineState)
5719 {
5720 default:
5721 AssertFailed();
5722 /* fall through */
5723 case MachineState_Stopping:
5724 /* successfully powered down */
5725 that->setMachineState(MachineState_PoweredOff);
5726 break;
5727 case MachineState_Saving:
5728 /* successfully saved (note that the machine is already in
5729 * the Saved state on the server due to EndSavingState()
5730 * called from saveStateThread(), so only change the local
5731 * state) */
5732 that->setMachineStateLocally(MachineState_Saved);
5733 break;
5734 case MachineState_Starting:
5735 /* failed to start, but be patient: set back to PoweredOff
5736 * (for similarity with the below) */
5737 that->setMachineState(MachineState_PoweredOff);
5738 break;
5739 case MachineState_Restoring:
5740 /* failed to load the saved state file, but be patient: set
5741 * back to Saved (to preserve the saved state file) */
5742 that->setMachineState(MachineState_Saved);
5743 break;
5744 }
5745 break;
5746 }
5747
5748 case VMSTATE_SUSPENDED:
5749 {
5750 /** @todo state/live VMSTATE_SUSPENDING_LS. */
5751 if ( aOldState == VMSTATE_SUSPENDING)
5752 {
5753 AutoWriteLock alock(that);
5754
5755 if (that->mVMStateChangeCallbackDisabled)
5756 break;
5757
5758 /* Change the machine state from Running to Paused. */
5759 AssertBreak(that->mMachineState == MachineState_Running);
5760 that->setMachineState(MachineState_Paused);
5761 }
5762 break;
5763 }
5764
5765 case VMSTATE_RUNNING:
5766 {
5767 if ( aOldState == VMSTATE_POWERING_ON
5768 || aOldState == VMSTATE_RESUMING)
5769 {
5770 AutoWriteLock alock(that);
5771
5772 if (that->mVMStateChangeCallbackDisabled)
5773 break;
5774
5775 /* Change the machine state from Starting, Restoring or Paused
5776 * to Running */
5777 Assert ( ( ( that->mMachineState == MachineState_Starting
5778 || that->mMachineState == MachineState_Paused)
5779 && aOldState == VMSTATE_POWERING_ON)
5780 || ( ( that->mMachineState == MachineState_Restoring
5781 || that->mMachineState == MachineState_Paused)
5782 && aOldState == VMSTATE_RESUMING));
5783
5784 that->setMachineState (MachineState_Running);
5785 }
5786
5787 break;
5788 }
5789
5790 case VMSTATE_FATAL_ERROR:
5791 {
5792 AutoWriteLock alock(that);
5793
5794 if (that->mVMStateChangeCallbackDisabled)
5795 break;
5796
5797 /* Fatal errors are only for running VMs. */
5798 Assert(Global::IsOnline(that->mMachineState));
5799
5800 /* Note! 'Pause' is used here in want of something better. There
5801 * are currently only two places where fatal errors might be
5802 * raised, so it is not worth adding a new externally
5803 * visible state for this yet. */
5804 that->setMachineState(MachineState_Paused);
5805 break;
5806 }
5807
5808 case VMSTATE_GURU_MEDITATION:
5809 {
5810 AutoWriteLock alock(that);
5811
5812 if (that->mVMStateChangeCallbackDisabled)
5813 break;
5814
5815 /* Guru are only for running VMs */
5816 Assert(Global::IsOnline (that->mMachineState));
5817
5818 that->setMachineState(MachineState_Stuck);
5819 break;
5820 }
5821
5822 default: /* shut up gcc */
5823 break;
5824 }
5825}
5826
5827#ifdef VBOX_WITH_USB
5828
5829/**
5830 * Sends a request to VMM to attach the given host device.
5831 * After this method succeeds, the attached device will appear in the
5832 * mUSBDevices collection.
5833 *
5834 * @param aHostDevice device to attach
5835 *
5836 * @note Synchronously calls EMT.
5837 * @note Must be called from under this object's lock.
5838 */
5839HRESULT Console::attachUSBDevice (IUSBDevice *aHostDevice, ULONG aMaskedIfs)
5840{
5841 AssertReturn(aHostDevice, E_FAIL);
5842 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5843
5844 /* still want a lock object because we need to leave it */
5845 AutoWriteLock alock(this);
5846
5847 HRESULT hrc;
5848
5849 /*
5850 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
5851 * method in EMT (using usbAttachCallback()).
5852 */
5853 Bstr BstrAddress;
5854 hrc = aHostDevice->COMGETTER (Address) (BstrAddress.asOutParam());
5855 ComAssertComRCRetRC (hrc);
5856
5857 Utf8Str Address (BstrAddress);
5858
5859 Bstr id;
5860 hrc = aHostDevice->COMGETTER (Id) (id.asOutParam());
5861 ComAssertComRCRetRC (hrc);
5862 Guid uuid(id);
5863
5864 BOOL fRemote = FALSE;
5865 hrc = aHostDevice->COMGETTER (Remote) (&fRemote);
5866 ComAssertComRCRetRC (hrc);
5867
5868 /* protect mpVM */
5869 AutoVMCaller autoVMCaller (this);
5870 CheckComRCReturnRC(autoVMCaller.rc());
5871
5872 LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
5873 Address.raw(), uuid.ptr()));
5874
5875 /* leave the lock before a VMR3* call (EMT will call us back)! */
5876 alock.leave();
5877
5878/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
5879 int vrc = VMR3ReqCallWait (mpVM, VMCPUID_ANY,
5880 (PFNRT) usbAttachCallback, 6, this, aHostDevice, uuid.ptr(), fRemote, Address.raw(), aMaskedIfs);
5881
5882 /* restore the lock */
5883 alock.enter();
5884
5885 /* hrc is S_OK here */
5886
5887 if (VBOX_FAILURE (vrc))
5888 {
5889 LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
5890 Address.raw(), uuid.ptr(), vrc));
5891
5892 switch (vrc)
5893 {
5894 case VERR_VUSB_NO_PORTS:
5895 hrc = setError (E_FAIL,
5896 tr ("Failed to attach the USB device. (No available ports on the USB controller)."));
5897 break;
5898 case VERR_VUSB_USBFS_PERMISSION:
5899 hrc = setError (E_FAIL,
5900 tr ("Not permitted to open the USB device, check usbfs options"));
5901 break;
5902 default:
5903 hrc = setError (E_FAIL,
5904 tr ("Failed to create a proxy device for the USB device. (Error: %Rrc)"), vrc);
5905 break;
5906 }
5907 }
5908
5909 return hrc;
5910}
5911
5912/**
5913 * USB device attach callback used by AttachUSBDevice().
5914 * Note that AttachUSBDevice() doesn't return until this callback is executed,
5915 * so we don't use AutoCaller and don't care about reference counters of
5916 * interface pointers passed in.
5917 *
5918 * @thread EMT
5919 * @note Locks the console object for writing.
5920 */
5921//static
5922DECLCALLBACK(int)
5923Console::usbAttachCallback (Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
5924{
5925 LogFlowFuncEnter();
5926 LogFlowFunc (("that={%p}\n", that));
5927
5928 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
5929
5930 void *pvRemoteBackend = NULL;
5931 if (aRemote)
5932 {
5933 RemoteUSBDevice *pRemoteUSBDevice = static_cast <RemoteUSBDevice *> (aHostDevice);
5934 Guid guid (*aUuid);
5935
5936 pvRemoteBackend = that->consoleVRDPServer ()->USBBackendRequestPointer (pRemoteUSBDevice->clientId (), &guid);
5937 if (!pvRemoteBackend)
5938 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
5939 }
5940
5941 USHORT portVersion = 1;
5942 HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
5943 AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
5944 Assert(portVersion == 1 || portVersion == 2);
5945
5946 int vrc = PDMR3USBCreateProxyDevice (that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
5947 portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
5948 if (VBOX_SUCCESS (vrc))
5949 {
5950 /* Create a OUSBDevice and add it to the device list */
5951 ComObjPtr<OUSBDevice> device;
5952 device.createObject();
5953 HRESULT hrc = device->init (aHostDevice);
5954 AssertComRC (hrc);
5955
5956 AutoWriteLock alock(that);
5957 that->mUSBDevices.push_back (device);
5958 LogFlowFunc (("Attached device {%RTuuid}\n", device->id().raw()));
5959
5960 /* notify callbacks */
5961 that->onUSBDeviceStateChange (device, true /* aAttached */, NULL);
5962 }
5963
5964 LogFlowFunc (("vrc=%Rrc\n", vrc));
5965 LogFlowFuncLeave();
5966 return vrc;
5967}
5968
5969/**
5970 * Sends a request to VMM to detach the given host device. After this method
5971 * succeeds, the detached device will disappear from the mUSBDevices
5972 * collection.
5973 *
5974 * @param aIt Iterator pointing to the device to detach.
5975 *
5976 * @note Synchronously calls EMT.
5977 * @note Must be called from under this object's lock.
5978 */
5979HRESULT Console::detachUSBDevice (USBDeviceList::iterator &aIt)
5980{
5981 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5982
5983 /* still want a lock object because we need to leave it */
5984 AutoWriteLock alock(this);
5985
5986 /* protect mpVM */
5987 AutoVMCaller autoVMCaller (this);
5988 CheckComRCReturnRC(autoVMCaller.rc());
5989
5990 /* if the device is attached, then there must at least one USB hub. */
5991 AssertReturn(PDMR3USBHasHub (mpVM), E_FAIL);
5992
5993 LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
5994 (*aIt)->id().raw()));
5995
5996 /* leave the lock before a VMR3* call (EMT will call us back)! */
5997 alock.leave();
5998
5999/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
6000 int vrc = VMR3ReqCallWait (mpVM, VMCPUID_ANY,
6001 (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
6002 ComAssertRCRet (vrc, E_FAIL);
6003
6004 return S_OK;
6005}
6006
6007/**
6008 * USB device detach callback used by DetachUSBDevice().
6009 * Note that DetachUSBDevice() doesn't return until this callback is executed,
6010 * so we don't use AutoCaller and don't care about reference counters of
6011 * interface pointers passed in.
6012 *
6013 * @thread EMT
6014 * @note Locks the console object for writing.
6015 */
6016//static
6017DECLCALLBACK(int)
6018Console::usbDetachCallback (Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
6019{
6020 LogFlowFuncEnter();
6021 LogFlowFunc (("that={%p}\n", that));
6022
6023 AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
6024 ComObjPtr<OUSBDevice> device = **aIt;
6025
6026 /*
6027 * If that was a remote device, release the backend pointer.
6028 * The pointer was requested in usbAttachCallback.
6029 */
6030 BOOL fRemote = FALSE;
6031
6032 HRESULT hrc2 = (**aIt)->COMGETTER (Remote) (&fRemote);
6033 ComAssertComRC (hrc2);
6034
6035 if (fRemote)
6036 {
6037 Guid guid (*aUuid);
6038 that->consoleVRDPServer ()->USBBackendReleasePointer (&guid);
6039 }
6040
6041 int vrc = PDMR3USBDetachDevice (that->mpVM, aUuid);
6042
6043 if (VBOX_SUCCESS (vrc))
6044 {
6045 AutoWriteLock alock(that);
6046
6047 /* Remove the device from the collection */
6048 that->mUSBDevices.erase (*aIt);
6049 LogFlowFunc (("Detached device {%RTuuid}\n", device->id().raw()));
6050
6051 /* notify callbacks */
6052 that->onUSBDeviceStateChange (device, false /* aAttached */, NULL);
6053 }
6054
6055 LogFlowFunc (("vrc=%Rrc\n", vrc));
6056 LogFlowFuncLeave();
6057 return vrc;
6058}
6059
6060#endif /* VBOX_WITH_USB */
6061#if (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT)
6062
6063/**
6064 * Helper function to handle host interface device creation and attachment.
6065 *
6066 * @param networkAdapter the network adapter which attachment should be reset
6067 * @return COM status code
6068 *
6069 * @note The caller must lock this object for writing.
6070 *
6071 * @todo Move this back into the driver!
6072 */
6073HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
6074{
6075 LogFlowThisFunc(("\n"));
6076 /* sanity check */
6077 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6078
6079# ifdef VBOX_STRICT
6080 /* paranoia */
6081 NetworkAttachmentType_T attachment;
6082 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6083 Assert(attachment == NetworkAttachmentType_Bridged);
6084# endif /* VBOX_STRICT */
6085
6086 HRESULT rc = S_OK;
6087
6088 ULONG slot = 0;
6089 rc = networkAdapter->COMGETTER(Slot)(&slot);
6090 AssertComRC(rc);
6091
6092# ifdef RT_OS_LINUX
6093 /*
6094 * Allocate a host interface device
6095 */
6096 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
6097 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
6098 if (VBOX_SUCCESS(rcVBox))
6099 {
6100 /*
6101 * Set/obtain the tap interface.
6102 */
6103 struct ifreq IfReq;
6104 memset(&IfReq, 0, sizeof(IfReq));
6105 /* The name of the TAP interface we are using */
6106 Bstr tapDeviceName;
6107 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6108 if (FAILED(rc))
6109 tapDeviceName.setNull(); /* Is this necessary? */
6110 if (tapDeviceName.isEmpty())
6111 {
6112 LogRel(("No TAP device name was supplied.\n"));
6113 rc = setError(E_FAIL, tr ("No TAP device name was supplied for the host networking interface"));
6114 }
6115
6116 if (SUCCEEDED(rc))
6117 {
6118 /* If we are using a static TAP device then try to open it. */
6119 Utf8Str str(tapDeviceName);
6120 if (str.length() <= sizeof(IfReq.ifr_name))
6121 strcpy(IfReq.ifr_name, str.raw());
6122 else
6123 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
6124 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
6125 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
6126 if (rcVBox != 0)
6127 {
6128 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
6129 rc = setError(E_FAIL, tr ("Failed to open the host network interface %ls"),
6130 tapDeviceName.raw());
6131 }
6132 }
6133 if (SUCCEEDED(rc))
6134 {
6135 /*
6136 * Make it pollable.
6137 */
6138 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
6139 {
6140 Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
6141 /*
6142 * Here is the right place to communicate the TAP file descriptor and
6143 * the host interface name to the server if/when it becomes really
6144 * necessary.
6145 */
6146 maTAPDeviceName[slot] = tapDeviceName;
6147 rcVBox = VINF_SUCCESS;
6148 }
6149 else
6150 {
6151 int iErr = errno;
6152
6153 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
6154 rcVBox = VERR_HOSTIF_BLOCKING;
6155 rc = setError(E_FAIL, tr ("could not set up the host networking device for non blocking access: %s"),
6156 strerror(errno));
6157 }
6158 }
6159 }
6160 else
6161 {
6162 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
6163 switch (rcVBox)
6164 {
6165 case VERR_ACCESS_DENIED:
6166 /* will be handled by our caller */
6167 rc = rcVBox;
6168 break;
6169 default:
6170 rc = setError(E_FAIL, tr ("Could not set up the host networking device: %Rrc"), rcVBox);
6171 break;
6172 }
6173 }
6174
6175# elif defined(RT_OS_FREEBSD)
6176 /*
6177 * Set/obtain the tap interface.
6178 */
6179 /* The name of the TAP interface we are using */
6180 Bstr tapDeviceName;
6181 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6182 if (FAILED(rc))
6183 tapDeviceName.setNull(); /* Is this necessary? */
6184 if (tapDeviceName.isEmpty())
6185 {
6186 LogRel(("No TAP device name was supplied.\n"));
6187 rc = setError(E_FAIL, tr ("No TAP device name was supplied for the host networking interface"));
6188 }
6189 char szTapdev[1024] = "/dev/";
6190 /* If we are using a static TAP device then try to open it. */
6191 Utf8Str str(tapDeviceName);
6192 if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
6193 strcat(szTapdev, str.raw());
6194 else
6195 memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
6196 int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
6197 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
6198
6199 if (RT_SUCCESS(rcVBox))
6200 maTAPDeviceName[slot] = tapDeviceName;
6201 else
6202 {
6203 switch (rcVBox)
6204 {
6205 case VERR_ACCESS_DENIED:
6206 /* will be handled by our caller */
6207 rc = rcVBox;
6208 break;
6209 default:
6210 rc = setError(E_FAIL, tr ("Failed to open the host network interface %ls"),
6211 tapDeviceName.raw());
6212 break;
6213 }
6214 }
6215# else
6216# error "huh?"
6217# endif
6218 /* in case of failure, cleanup. */
6219 if (VBOX_FAILURE(rcVBox) && SUCCEEDED(rc))
6220 {
6221 LogRel(("General failure attaching to host interface\n"));
6222 rc = setError(E_FAIL, tr ("General failure attaching to host interface"));
6223 }
6224 LogFlowThisFunc(("rc=%d\n", rc));
6225 return rc;
6226}
6227
6228
6229/**
6230 * Helper function to handle detachment from a host interface
6231 *
6232 * @param networkAdapter the network adapter which attachment should be reset
6233 * @return COM status code
6234 *
6235 * @note The caller must lock this object for writing.
6236 *
6237 * @todo Move this back into the driver!
6238 */
6239HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
6240{
6241 /* sanity check */
6242 LogFlowThisFunc(("\n"));
6243 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6244
6245 HRESULT rc = S_OK;
6246# ifdef VBOX_STRICT
6247 /* paranoia */
6248 NetworkAttachmentType_T attachment;
6249 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6250 Assert(attachment == NetworkAttachmentType_Bridged);
6251# endif /* VBOX_STRICT */
6252
6253 ULONG slot = 0;
6254 rc = networkAdapter->COMGETTER(Slot)(&slot);
6255 AssertComRC(rc);
6256
6257 /* is there an open TAP device? */
6258 if (maTapFD[slot] != NIL_RTFILE)
6259 {
6260 /*
6261 * Close the file handle.
6262 */
6263 Bstr tapDeviceName, tapTerminateApplication;
6264 bool isStatic = true;
6265 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
6266 if (FAILED(rc) || tapDeviceName.isEmpty())
6267 {
6268 /* If the name is empty, this is a dynamic TAP device, so close it now,
6269 so that the termination script can remove the interface. Otherwise we still
6270 need the FD to pass to the termination script. */
6271 isStatic = false;
6272 int rcVBox = RTFileClose(maTapFD[slot]);
6273 AssertRC(rcVBox);
6274 maTapFD[slot] = NIL_RTFILE;
6275 }
6276 if (isStatic)
6277 {
6278 /* If we are using a static TAP device, we close it now, after having called the
6279 termination script. */
6280 int rcVBox = RTFileClose(maTapFD[slot]);
6281 AssertRC(rcVBox);
6282 }
6283 /* the TAP device name and handle are no longer valid */
6284 maTapFD[slot] = NIL_RTFILE;
6285 maTAPDeviceName[slot] = "";
6286 }
6287 LogFlowThisFunc(("returning %d\n", rc));
6288 return rc;
6289}
6290
6291#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
6292
6293/**
6294 * Called at power down to terminate host interface networking.
6295 *
6296 * @note The caller must lock this object for writing.
6297 */
6298HRESULT Console::powerDownHostInterfaces()
6299{
6300 LogFlowThisFunc(("\n"));
6301
6302 /* sanity check */
6303 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
6304
6305 /*
6306 * host interface termination handling
6307 */
6308 HRESULT rc;
6309 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
6310 {
6311 ComPtr<INetworkAdapter> networkAdapter;
6312 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
6313 CheckComRCBreakRC (rc);
6314
6315 BOOL enabled = FALSE;
6316 networkAdapter->COMGETTER(Enabled) (&enabled);
6317 if (!enabled)
6318 continue;
6319
6320 NetworkAttachmentType_T attachment;
6321 networkAdapter->COMGETTER(AttachmentType)(&attachment);
6322 if (attachment == NetworkAttachmentType_Bridged)
6323 {
6324#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
6325 HRESULT rc2 = detachFromTapInterface(networkAdapter);
6326 if (FAILED(rc2) && SUCCEEDED(rc))
6327 rc = rc2;
6328#endif
6329 }
6330 }
6331
6332 return rc;
6333}
6334
6335
6336/**
6337 * Process callback handler for VMR3Load and VMR3Save.
6338 *
6339 * @param pVM The VM handle.
6340 * @param uPercent Completetion precentage (0-100).
6341 * @param pvUser Pointer to the VMProgressTask structure.
6342 * @return VINF_SUCCESS.
6343 */
6344/*static*/ DECLCALLBACK (int)
6345Console::stateProgressCallback (PVM pVM, unsigned uPercent, void *pvUser)
6346{
6347 VMProgressTask *task = static_cast <VMProgressTask *> (pvUser);
6348 AssertReturn(task, VERR_INVALID_PARAMETER);
6349
6350 /* update the progress object */
6351 if (task->mProgress)
6352 task->mProgress->setCurrentOperationProgress(uPercent);
6353
6354 return VINF_SUCCESS;
6355}
6356
6357/**
6358 * VM error callback function. Called by the various VM components.
6359 *
6360 * @param pVM VM handle. Can be NULL if an error occurred before
6361 * successfully creating a VM.
6362 * @param pvUser Pointer to the VMProgressTask structure.
6363 * @param rc VBox status code.
6364 * @param pszFormat Printf-like error message.
6365 * @param args Various number of arguments for the error message.
6366 *
6367 * @thread EMT, VMPowerUp...
6368 *
6369 * @note The VMProgressTask structure modified by this callback is not thread
6370 * safe.
6371 */
6372/* static */ DECLCALLBACK (void)
6373Console::setVMErrorCallback (PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
6374 const char *pszFormat, va_list args)
6375{
6376 VMProgressTask *task = static_cast <VMProgressTask *> (pvUser);
6377 AssertReturnVoid (task);
6378
6379 /* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */
6380 va_list va2;
6381 va_copy (va2, args); /* Have to make a copy here or GCC will break. */
6382
6383 /* append to the existing error message if any */
6384 if (task->mErrorMsg.length())
6385 task->mErrorMsg = Utf8StrFmt ("%s.\n%N (%Rrc)", task->mErrorMsg.raw(),
6386 pszFormat, &va2, rc, rc);
6387 else
6388 task->mErrorMsg = Utf8StrFmt ("%N (%Rrc)",
6389 pszFormat, &va2, rc, rc);
6390
6391 va_end (va2);
6392}
6393
6394/**
6395 * VM runtime error callback function.
6396 * See VMSetRuntimeError for the detailed description of parameters.
6397 *
6398 * @param pVM The VM handle.
6399 * @param pvUser The user argument.
6400 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
6401 * @param pszErrorId Error ID string.
6402 * @param pszFormat Error message format string.
6403 * @param va Error message arguments.
6404 * @thread EMT.
6405 */
6406/* static */ DECLCALLBACK(void)
6407Console::setVMRuntimeErrorCallback (PVM pVM, void *pvUser, uint32_t fFlags,
6408 const char *pszErrorId,
6409 const char *pszFormat, va_list va)
6410{
6411 bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
6412 LogFlowFuncEnter();
6413
6414 Console *that = static_cast <Console *> (pvUser);
6415 AssertReturnVoid (that);
6416
6417 Utf8Str message = Utf8StrFmtVA (pszFormat, va);
6418
6419 LogRel (("Console: VM runtime error: fatal=%RTbool, "
6420 "errorID=%s message=\"%s\"\n",
6421 fFatal, pszErrorId, message.raw()));
6422
6423 that->onRuntimeError (BOOL (fFatal), Bstr (pszErrorId), Bstr (message));
6424
6425 LogFlowFuncLeave();
6426}
6427
6428/**
6429 * Captures USB devices that match filters of the VM.
6430 * Called at VM startup.
6431 *
6432 * @param pVM The VM handle.
6433 *
6434 * @note The caller must lock this object for writing.
6435 */
6436HRESULT Console::captureUSBDevices (PVM pVM)
6437{
6438 LogFlowThisFunc(("\n"));
6439
6440 /* sanity check */
6441 ComAssertRet (isWriteLockOnCurrentThread(), E_FAIL);
6442
6443 /* If the machine has an USB controller, ask the USB proxy service to
6444 * capture devices */
6445 PPDMIBASE pBase;
6446 int vrc = PDMR3QueryLun (pVM, "usb-ohci", 0, 0, &pBase);
6447 if (VBOX_SUCCESS (vrc))
6448 {
6449 /* leave the lock before calling Host in VBoxSVC since Host may call
6450 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6451 * produce an inter-process dead-lock otherwise. */
6452 AutoWriteLock alock(this);
6453 alock.leave();
6454
6455 HRESULT hrc = mControl->AutoCaptureUSBDevices();
6456 ComAssertComRCRetRC (hrc);
6457 }
6458 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
6459 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
6460 vrc = VINF_SUCCESS;
6461 else
6462 AssertRC (vrc);
6463
6464 return VBOX_SUCCESS (vrc) ? S_OK : E_FAIL;
6465}
6466
6467
6468/**
6469 * Detach all USB device which are attached to the VM for the
6470 * purpose of clean up and such like.
6471 *
6472 * @note The caller must lock this object for writing.
6473 */
6474void Console::detachAllUSBDevices (bool aDone)
6475{
6476 LogFlowThisFunc(("aDone=%RTbool\n", aDone));
6477
6478 /* sanity check */
6479 AssertReturnVoid (isWriteLockOnCurrentThread());
6480
6481 mUSBDevices.clear();
6482
6483 /* leave the lock before calling Host in VBoxSVC since Host may call
6484 * us back from under its lock (e.g. onUSBDeviceAttach()) which would
6485 * produce an inter-process dead-lock otherwise. */
6486 AutoWriteLock alock(this);
6487 alock.leave();
6488
6489 mControl->DetachAllUSBDevices (aDone);
6490}
6491
6492/**
6493 * @note Locks this object for writing.
6494 */
6495void Console::processRemoteUSBDevices (uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
6496{
6497 LogFlowThisFuncEnter();
6498 LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
6499
6500 AutoCaller autoCaller(this);
6501 if (!autoCaller.isOk())
6502 {
6503 /* Console has been already uninitialized, deny request */
6504 AssertMsgFailed (("Temporary assertion to prove that it happens, "
6505 "please report to dmik\n"));
6506 LogFlowThisFunc(("Console is already uninitialized\n"));
6507 LogFlowThisFuncLeave();
6508 return;
6509 }
6510
6511 AutoWriteLock alock(this);
6512
6513 /*
6514 * Mark all existing remote USB devices as dirty.
6515 */
6516 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6517 while (it != mRemoteUSBDevices.end())
6518 {
6519 (*it)->dirty (true);
6520 ++ it;
6521 }
6522
6523 /*
6524 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
6525 */
6526 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
6527 VRDPUSBDEVICEDESC *e = pDevList;
6528
6529 /* The cbDevList condition must be checked first, because the function can
6530 * receive pDevList = NULL and cbDevList = 0 on client disconnect.
6531 */
6532 while (cbDevList >= 2 && e->oNext)
6533 {
6534 LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
6535 e->idVendor, e->idProduct,
6536 e->oProduct? (char *)e + e->oProduct: ""));
6537
6538 bool fNewDevice = true;
6539
6540 it = mRemoteUSBDevices.begin();
6541 while (it != mRemoteUSBDevices.end())
6542 {
6543 if ((*it)->devId () == e->id
6544 && (*it)->clientId () == u32ClientId)
6545 {
6546 /* The device is already in the list. */
6547 (*it)->dirty (false);
6548 fNewDevice = false;
6549 break;
6550 }
6551
6552 ++ it;
6553 }
6554
6555 if (fNewDevice)
6556 {
6557 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
6558 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""
6559 ));
6560
6561 /* Create the device object and add the new device to list. */
6562 ComObjPtr<RemoteUSBDevice> device;
6563 device.createObject();
6564 device->init (u32ClientId, e);
6565
6566 mRemoteUSBDevices.push_back (device);
6567
6568 /* Check if the device is ok for current USB filters. */
6569 BOOL fMatched = FALSE;
6570 ULONG fMaskedIfs = 0;
6571
6572 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
6573
6574 AssertComRC (hrc);
6575
6576 LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
6577
6578 if (fMatched)
6579 {
6580 hrc = onUSBDeviceAttach (device, NULL, fMaskedIfs);
6581
6582 /// @todo (r=dmik) warning reporting subsystem
6583
6584 if (hrc == S_OK)
6585 {
6586 LogFlowThisFunc(("Device attached\n"));
6587 device->captured (true);
6588 }
6589 }
6590 }
6591
6592 if (cbDevList < e->oNext)
6593 {
6594 LogWarningThisFunc(("cbDevList %d > oNext %d\n",
6595 cbDevList, e->oNext));
6596 break;
6597 }
6598
6599 cbDevList -= e->oNext;
6600
6601 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
6602 }
6603
6604 /*
6605 * Remove dirty devices, that is those which are not reported by the server anymore.
6606 */
6607 for (;;)
6608 {
6609 ComObjPtr<RemoteUSBDevice> device;
6610
6611 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
6612 while (it != mRemoteUSBDevices.end())
6613 {
6614 if ((*it)->dirty ())
6615 {
6616 device = *it;
6617 break;
6618 }
6619
6620 ++ it;
6621 }
6622
6623 if (!device)
6624 {
6625 break;
6626 }
6627
6628 USHORT vendorId = 0;
6629 device->COMGETTER(VendorId) (&vendorId);
6630
6631 USHORT productId = 0;
6632 device->COMGETTER(ProductId) (&productId);
6633
6634 Bstr product;
6635 device->COMGETTER(Product) (product.asOutParam());
6636
6637 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
6638 vendorId, productId, product.raw ()
6639 ));
6640
6641 /* Detach the device from VM. */
6642 if (device->captured ())
6643 {
6644 Bstr uuid;
6645 device->COMGETTER (Id) (uuid.asOutParam());
6646 onUSBDeviceDetach (uuid, NULL);
6647 }
6648
6649 /* And remove it from the list. */
6650 mRemoteUSBDevices.erase (it);
6651 }
6652
6653 LogFlowThisFuncLeave();
6654}
6655
6656/**
6657 * Thread function which starts the VM (also from saved state) and
6658 * track progress.
6659 *
6660 * @param Thread The thread id.
6661 * @param pvUser Pointer to a VMPowerUpTask structure.
6662 * @return VINF_SUCCESS (ignored).
6663 *
6664 * @note Locks the Console object for writing.
6665 */
6666/*static*/
6667DECLCALLBACK (int) Console::powerUpThread (RTTHREAD Thread, void *pvUser)
6668{
6669 LogFlowFuncEnter();
6670
6671 std::auto_ptr <VMPowerUpTask> task (static_cast <VMPowerUpTask *> (pvUser));
6672 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
6673
6674 AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
6675 AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
6676
6677#if defined(RT_OS_WINDOWS)
6678 {
6679 /* initialize COM */
6680 HRESULT hrc = CoInitializeEx (NULL,
6681 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
6682 COINIT_SPEED_OVER_MEMORY);
6683 LogFlowFunc (("CoInitializeEx()=%08X\n", hrc));
6684 }
6685#endif
6686
6687 HRESULT rc = S_OK;
6688 int vrc = VINF_SUCCESS;
6689
6690 /* Set up a build identifier so that it can be seen from core dumps what
6691 * exact build was used to produce the core. */
6692 static char saBuildID[40];
6693 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
6694 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
6695
6696 ComObjPtr<Console> console = task->mConsole;
6697
6698 /* Note: no need to use addCaller() because VMPowerUpTask does that */
6699
6700 /* The lock is also used as a signal from the task initiator (which
6701 * releases it only after RTThreadCreate()) that we can start the job */
6702 AutoWriteLock alock(console);
6703
6704 /* sanity */
6705 Assert (console->mpVM == NULL);
6706
6707 try
6708 {
6709 /* wait for auto reset ops to complete so that we can successfully lock
6710 * the attached hard disks by calling LockMedia() below */
6711 for (VMPowerUpTask::ProgressList::const_iterator
6712 it = task->hardDiskProgresses.begin();
6713 it != task->hardDiskProgresses.end(); ++ it)
6714 {
6715 HRESULT rc2 = (*it)->WaitForCompletion (-1);
6716 AssertComRC (rc2);
6717 }
6718
6719 /* lock attached media. This method will also check their
6720 * accessibility. Note that the media will be unlocked automatically
6721 * by SessionMachine::setMachineState() when the VM is powered down. */
6722 rc = console->mControl->LockMedia();
6723 CheckComRCThrowRC (rc);
6724
6725#ifdef VBOX_WITH_VRDP
6726
6727 /* Create the VRDP server. In case of headless operation, this will
6728 * also create the framebuffer, required at VM creation.
6729 */
6730 ConsoleVRDPServer *server = console->consoleVRDPServer();
6731 Assert (server);
6732
6733 /// @todo (dmik)
6734 // does VRDP server call Console from the other thread?
6735 // Not sure, so leave the lock just in case
6736 alock.leave();
6737 vrc = server->Launch();
6738 alock.enter();
6739
6740 if (VBOX_FAILURE (vrc))
6741 {
6742 Utf8Str errMsg;
6743 switch (vrc)
6744 {
6745 case VERR_NET_ADDRESS_IN_USE:
6746 {
6747 ULONG port = 0;
6748 console->mVRDPServer->COMGETTER(Port) (&port);
6749 errMsg = Utf8StrFmt (tr ("VRDP server port %d is already in use"),
6750 port);
6751 break;
6752 }
6753 case VERR_FILE_NOT_FOUND:
6754 {
6755 errMsg = Utf8StrFmt (tr ("Could not load the VRDP library"));
6756 break;
6757 }
6758 default:
6759 errMsg = Utf8StrFmt (tr ("Failed to launch VRDP server (%Rrc)"),
6760 vrc);
6761 }
6762 LogRel (("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
6763 vrc, errMsg.raw()));
6764 throw setError(E_FAIL, errMsg.c_str());
6765 }
6766
6767#endif /* VBOX_WITH_VRDP */
6768
6769 ComPtr<IMachine> pMachine = console->machine();
6770 ULONG cCpus = 1;
6771 pMachine->COMGETTER(CPUCount)(&cCpus);
6772
6773 /*
6774 * Create the VM
6775 */
6776 PVM pVM;
6777 /*
6778 * leave the lock since EMT will call Console. It's safe because
6779 * mMachineState is either Starting or Restoring state here.
6780 */
6781 alock.leave();
6782
6783 vrc = VMR3Create (cCpus, task->mSetVMErrorCallback, task.get(),
6784 task->mConfigConstructor, static_cast <Console *> (console),
6785 &pVM);
6786
6787 alock.enter();
6788
6789#ifdef VBOX_WITH_VRDP
6790 /* Enable client connections to the server. */
6791 console->consoleVRDPServer()->EnableConnections ();
6792#endif /* VBOX_WITH_VRDP */
6793
6794 if (VBOX_SUCCESS (vrc))
6795 {
6796 do
6797 {
6798 /*
6799 * Register our load/save state file handlers
6800 */
6801 vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
6802 NULL, NULL, NULL,
6803 NULL, saveStateFileExec, NULL,
6804 NULL, loadStateFileExec, NULL,
6805 static_cast<Console *>(console));
6806 AssertRCBreak(vrc);
6807
6808 vrc = static_cast <Console *>(console)->getDisplay()->registerSSM(pVM);
6809 AssertRC (vrc);
6810 if (VBOX_FAILURE (vrc))
6811 break;
6812
6813 /*
6814 * Synchronize debugger settings
6815 */
6816 MachineDebugger *machineDebugger = console->getMachineDebugger();
6817 if (machineDebugger)
6818 {
6819 machineDebugger->flushQueuedSettings();
6820 }
6821
6822 /*
6823 * Shared Folders
6824 */
6825 if (console->getVMMDev()->isShFlActive())
6826 {
6827 /// @todo (dmik)
6828 // does the code below call Console from the other thread?
6829 // Not sure, so leave the lock just in case
6830 alock.leave();
6831
6832 for (SharedFolderDataMap::const_iterator
6833 it = task->mSharedFolders.begin();
6834 it != task->mSharedFolders.end();
6835 ++ it)
6836 {
6837 rc = console->createSharedFolder ((*it).first, (*it).second);
6838 CheckComRCBreakRC (rc);
6839 }
6840
6841 /* enter the lock again */
6842 alock.enter();
6843
6844 CheckComRCBreakRC (rc);
6845 }
6846
6847 /*
6848 * Capture USB devices.
6849 */
6850 rc = console->captureUSBDevices (pVM);
6851 CheckComRCBreakRC (rc);
6852
6853 /* leave the lock before a lengthy operation */
6854 alock.leave();
6855
6856 /* Load saved state? */
6857 if (task->mSavedStateFile.length())
6858 {
6859 LogFlowFunc (("Restoring saved state from '%s'...\n",
6860 task->mSavedStateFile.raw()));
6861
6862 vrc = VMR3Load(pVM,
6863 task->mSavedStateFile.c_str(),
6864 Console::stateProgressCallback,
6865 static_cast<VMProgressTask*>(task.get()));
6866
6867 if (VBOX_SUCCESS (vrc))
6868 {
6869 if (task->mStartPaused)
6870 /* done */
6871 console->setMachineState (MachineState_Paused);
6872 else
6873 {
6874 /* Start/Resume the VM execution */
6875 vrc = VMR3Resume (pVM);
6876 AssertRC (vrc);
6877 }
6878 }
6879
6880 /* Power off in case we failed loading or resuming the VM */
6881 if (VBOX_FAILURE (vrc))
6882 {
6883 int vrc2 = VMR3PowerOff (pVM);
6884 AssertRC (vrc2);
6885 }
6886 }
6887 else if (task->mStartPaused)
6888 /* done */
6889 console->setMachineState (MachineState_Paused);
6890 else
6891 {
6892 /* Power on the VM (i.e. start executing) */
6893 vrc = VMR3PowerOn(pVM);
6894 AssertRC (vrc);
6895 }
6896
6897 /* enter the lock again */
6898 alock.enter();
6899 }
6900 while (0);
6901
6902 /* On failure, destroy the VM */
6903 if (FAILED (rc) || VBOX_FAILURE (vrc))
6904 {
6905 /* preserve existing error info */
6906 ErrorInfoKeeper eik;
6907
6908 /* powerDown() will call VMR3Destroy() and do all necessary
6909 * cleanup (VRDP, USB devices) */
6910 HRESULT rc2 = console->powerDown();
6911 AssertComRC (rc2);
6912 }
6913 else
6914 {
6915 /*
6916 * Deregister the VMSetError callback. This is necessary as the
6917 * pfnVMAtError() function passed to VMR3Create() is supposed to
6918 * be sticky but our error callback isn't.
6919 */
6920 alock.leave();
6921 VMR3AtErrorDeregister(pVM, task->mSetVMErrorCallback, task.get());
6922 /** @todo register another VMSetError callback? */
6923 alock.enter();
6924 }
6925 }
6926 else
6927 {
6928 /*
6929 * If VMR3Create() failed it has released the VM memory.
6930 */
6931 console->mpVM = NULL;
6932 }
6933
6934 if (SUCCEEDED(rc) && VBOX_FAILURE (vrc))
6935 {
6936 /* If VMR3Create() or one of the other calls in this function fail,
6937 * an appropriate error message has been set in task->mErrorMsg.
6938 * However since that happens via a callback, the rc status code in
6939 * this function is not updated.
6940 */
6941 if (!task->mErrorMsg.length())
6942 {
6943 /* If the error message is not set but we've got a failure,
6944 * convert the VBox status code into a meaningfulerror message.
6945 * This becomes unused once all the sources of errors set the
6946 * appropriate error message themselves.
6947 */
6948 AssertMsgFailed (("Missing error message during powerup for "
6949 "status code %Rrc\n", vrc));
6950 task->mErrorMsg = Utf8StrFmt (
6951 tr ("Failed to start VM execution (%Rrc)"), vrc);
6952 }
6953
6954 /* Set the error message as the COM error.
6955 * Progress::notifyComplete() will pick it up later. */
6956 throw setError(E_FAIL, task->mErrorMsg.c_str());
6957 }
6958 }
6959 catch (HRESULT aRC) { rc = aRC; }
6960
6961 if (console->mMachineState == MachineState_Starting ||
6962 console->mMachineState == MachineState_Restoring)
6963 {
6964 /* We are still in the Starting/Restoring state. This means one of:
6965 *
6966 * 1) we failed before VMR3Create() was called;
6967 * 2) VMR3Create() failed.
6968 *
6969 * In both cases, there is no need to call powerDown(), but we still
6970 * need to go back to the PoweredOff/Saved state. Reuse
6971 * vmstateChangeCallback() for that purpose.
6972 */
6973
6974 /* preserve existing error info */
6975 ErrorInfoKeeper eik;
6976
6977 Assert (console->mpVM == NULL);
6978 vmstateChangeCallback (NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
6979 console);
6980 }
6981
6982 /*
6983 * Evaluate the final result. Note that the appropriate mMachineState value
6984 * is already set by vmstateChangeCallback() in all cases.
6985 */
6986
6987 /* leave the lock, don't need it any more */
6988 alock.leave();
6989
6990 if (SUCCEEDED(rc))
6991 {
6992 /* Notify the progress object of the success */
6993 task->mProgress->notifyComplete (S_OK);
6994 }
6995 else
6996 {
6997 /* The progress object will fetch the current error info */
6998 task->mProgress->notifyComplete (rc);
6999
7000 LogRel (("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
7001 }
7002
7003#if defined(RT_OS_WINDOWS)
7004 /* uninitialize COM */
7005 CoUninitialize();
7006#endif
7007
7008 LogFlowFuncLeave();
7009
7010 return VINF_SUCCESS;
7011}
7012
7013
7014/**
7015 * Reconfigures a VDI.
7016 *
7017 * @param pVM The VM handle.
7018 * @param lInstance The instance of the controller.
7019 * @param enmController The type of the controller.
7020 * @param hda The harddisk attachment.
7021 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
7022 * @return VBox status code.
7023 */
7024static DECLCALLBACK(int) reconfigureHardDisks(PVM pVM, ULONG lInstance,
7025 StorageControllerType_T enmController,
7026 IHardDiskAttachment *hda,
7027 HRESULT *phrc)
7028{
7029 LogFlowFunc (("pVM=%p hda=%p phrc=%p\n", pVM, hda, phrc));
7030
7031 int rc;
7032 HRESULT hrc;
7033 Bstr bstr;
7034 *phrc = S_OK;
7035#define RC_CHECK() do { if (VBOX_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
7036#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
7037
7038 /*
7039 * Figure out which IDE device this is.
7040 */
7041 ComPtr<IHardDisk> hardDisk;
7042 hrc = hda->COMGETTER(HardDisk)(hardDisk.asOutParam()); H();
7043 LONG lDev;
7044 hrc = hda->COMGETTER(Device)(&lDev); H();
7045 LONG lPort;
7046 hrc = hda->COMGETTER(Port)(&lPort); H();
7047
7048 int iLUN;
7049 const char *pcszDevice = NULL;
7050 bool fSCSI = false;
7051
7052 switch (enmController)
7053 {
7054 case StorageControllerType_PIIX3:
7055 case StorageControllerType_PIIX4:
7056 case StorageControllerType_ICH6:
7057 {
7058 if (lPort >= 2 || lPort < 0)
7059 {
7060 AssertMsgFailed(("invalid controller channel number: %d\n", lPort));
7061 return VERR_GENERAL_FAILURE;
7062 }
7063
7064 if (lDev >= 2 || lDev < 0)
7065 {
7066 AssertMsgFailed(("invalid controller device number: %d\n", lDev));
7067 return VERR_GENERAL_FAILURE;
7068 }
7069
7070 iLUN = 2*lPort + lDev;
7071 pcszDevice = "piix3ide";
7072 break;
7073 }
7074 case StorageControllerType_IntelAhci:
7075 {
7076 iLUN = lPort;
7077 pcszDevice = "ahci";
7078 break;
7079 }
7080 case StorageControllerType_BusLogic:
7081 {
7082 iLUN = lPort;
7083 pcszDevice = "buslogic";
7084 fSCSI = true;
7085 break;
7086 }
7087 case StorageControllerType_LsiLogic:
7088 {
7089 iLUN = lPort;
7090 pcszDevice = "lsilogicscsi";
7091 fSCSI = true;
7092 break;
7093 }
7094 default:
7095 {
7096 AssertMsgFailed(("invalid disk controller type: %d\n", enmController));
7097 return VERR_GENERAL_FAILURE;
7098 }
7099 }
7100
7101 /** @todo this should be unified with the relevant part of
7102 * Console::configConstructor to avoid inconsistencies. */
7103
7104 /*
7105 * Is there an existing LUN? If not create it.
7106 * We ASSUME that this will NEVER collide with the DVD.
7107 */
7108 PCFGMNODE pCfg;
7109 PCFGMNODE pLunL1;
7110
7111 /* SCSI has an extra driver between the device and the block driver. */
7112 if (fSCSI)
7113 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/AttachedDriver/", pcszDevice, lInstance, iLUN);
7114 else
7115 pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/", pcszDevice, lInstance, iLUN);
7116
7117 if (!pLunL1)
7118 {
7119 PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, lInstance);
7120 AssertReturn(pInst, VERR_INTERNAL_ERROR);
7121
7122 PCFGMNODE pLunL0;
7123 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", iLUN); RC_CHECK();
7124
7125 if (fSCSI)
7126 {
7127 rc = CFGMR3InsertString(pLunL0, "Driver", "SCSI"); RC_CHECK();
7128 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
7129
7130 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();
7131 }
7132
7133 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();
7134 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
7135 rc = CFGMR3InsertString(pCfg, "Type", "HardDisk"); RC_CHECK();
7136 rc = CFGMR3InsertInteger(pCfg, "Mountable", 0); RC_CHECK();
7137
7138 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();
7139 rc = CFGMR3InsertString(pLunL1, "Driver", "VD"); RC_CHECK();
7140 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
7141 }
7142 else
7143 {
7144#ifdef VBOX_STRICT
7145 char *pszDriver;
7146 rc = CFGMR3QueryStringAlloc(pLunL1, "Driver", &pszDriver); RC_CHECK();
7147 Assert(!strcmp(pszDriver, "VD"));
7148 MMR3HeapFree(pszDriver);
7149#endif
7150
7151 pCfg = CFGMR3GetChild(pLunL1, "Config");
7152 AssertReturn(pCfg, VERR_INTERNAL_ERROR);
7153
7154 /* Here used to be a lot of code checking if things have changed,
7155 * but that's not really worth it, as with snapshots there is always
7156 * some change, so the code was just logging useless information in
7157 * a hard to analyze form. */
7158
7159 /*
7160 * Detach the driver and replace the config node.
7161 */
7162 rc = PDMR3DeviceDetach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG); RC_CHECK();
7163 CFGMR3RemoveNode(pCfg);
7164 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();
7165 }
7166
7167 /*
7168 * Create the driver configuration.
7169 */
7170 hrc = hardDisk->COMGETTER(Location)(bstr.asOutParam()); H();
7171 LogFlowFunc (("LUN#%d: leaf location '%ls'\n", iLUN, bstr.raw()));
7172 rc = CFGMR3InsertString(pCfg, "Path", Utf8Str(bstr).c_str()); RC_CHECK();
7173 hrc = hardDisk->COMGETTER(Format)(bstr.asOutParam()); H();
7174 LogFlowFunc (("LUN#%d: leaf format '%ls'\n", iLUN, bstr.raw()));
7175 rc = CFGMR3InsertString(pCfg, "Format", Utf8Str(bstr).c_str()); RC_CHECK();
7176
7177 /* Pass all custom parameters. */
7178 bool fHostIP = true;
7179 SafeArray<BSTR> names;
7180 SafeArray<BSTR> values;
7181 hrc = hardDisk->GetProperties (NULL,
7182 ComSafeArrayAsOutParam (names),
7183 ComSafeArrayAsOutParam (values)); H();
7184
7185 if (names.size() != 0)
7186 {
7187 PCFGMNODE pVDC;
7188 rc = CFGMR3InsertNode (pCfg, "VDConfig", &pVDC); RC_CHECK();
7189 for (size_t i = 0; i < names.size(); ++ i)
7190 {
7191 if (values [i])
7192 {
7193 Utf8Str name = names[i];
7194 Utf8Str value = values[i];
7195 rc = CFGMR3InsertString(pVDC, name.c_str(), value.c_str());
7196 if ( !(name.compare("HostIPStack"))
7197 && !(value.compare("0")))
7198 fHostIP = false;
7199 }
7200 }
7201 }
7202
7203 /* Create an inversed tree of parents. */
7204 ComPtr<IHardDisk> parentHardDisk = hardDisk;
7205 for (PCFGMNODE pParent = pCfg;;)
7206 {
7207 hrc = parentHardDisk->COMGETTER(Parent)(hardDisk.asOutParam()); H();
7208 if (hardDisk.isNull())
7209 break;
7210
7211 PCFGMNODE pCur;
7212 rc = CFGMR3InsertNode(pParent, "Parent", &pCur); RC_CHECK();
7213 hrc = hardDisk->COMGETTER(Location)(bstr.asOutParam()); H();
7214 rc = CFGMR3InsertString(pCur, "Path", Utf8Str(bstr).c_str()); RC_CHECK();
7215
7216 hrc = hardDisk->COMGETTER(Format)(bstr.asOutParam()); H();
7217 rc = CFGMR3InsertString(pCur, "Format", Utf8Str(bstr).c_str()); RC_CHECK();
7218
7219 /* Pass all custom parameters. */
7220 SafeArray<BSTR> names;
7221 SafeArray<BSTR> values;
7222 hrc = hardDisk->GetProperties (NULL,
7223 ComSafeArrayAsOutParam (names),
7224 ComSafeArrayAsOutParam (values));H();
7225
7226 if (names.size() != 0)
7227 {
7228 PCFGMNODE pVDC;
7229 rc = CFGMR3InsertNode (pCur, "VDConfig", &pVDC); RC_CHECK();
7230 for (size_t i = 0; i < names.size(); ++ i)
7231 {
7232 if (values [i])
7233 {
7234 Utf8Str name = names [i];
7235 Utf8Str value = values [i];
7236 rc = CFGMR3InsertString (pVDC, name.c_str(), value.c_str());
7237 if ( !(name.compare("HostIPStack"))
7238 && !(value.compare("0")))
7239 fHostIP = false;
7240 }
7241 }
7242 }
7243
7244
7245 /* Custom code: put marker to not use host IP stack to driver
7246 * configuration node. Simplifies life of DrvVD a bit. */
7247 if (!fHostIP)
7248 {
7249 rc = CFGMR3InsertInteger (pCfg, "HostIPStack", 0); RC_CHECK();
7250 }
7251
7252
7253 /* next */
7254 pParent = pCur;
7255 parentHardDisk = hardDisk;
7256 }
7257
7258 CFGMR3Dump(CFGMR3GetRoot(pVM));
7259
7260 /*
7261 * Attach the new driver.
7262 */
7263 rc = PDMR3DeviceAttach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); RC_CHECK();
7264
7265 LogFlowFunc (("Returns success\n"));
7266 return rc;
7267}
7268
7269
7270/**
7271 * Thread for executing the saved state operation.
7272 *
7273 * @param Thread The thread handle.
7274 * @param pvUser Pointer to a VMSaveTask structure.
7275 * @return VINF_SUCCESS (ignored).
7276 *
7277 * @note Locks the Console object for writing.
7278 */
7279/*static*/
7280DECLCALLBACK (int) Console::saveStateThread (RTTHREAD Thread, void *pvUser)
7281{
7282 LogFlowFuncEnter();
7283
7284 std::auto_ptr <VMSaveTask> task (static_cast <VMSaveTask *> (pvUser));
7285 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7286
7287 Assert(task->mSavedStateFile.length());
7288 Assert(!task->mProgress.isNull());
7289
7290 const ComObjPtr<Console> &that = task->mConsole;
7291
7292 /*
7293 * Note: no need to use addCaller() to protect Console or addVMCaller() to
7294 * protect mpVM because VMSaveTask does that
7295 */
7296
7297 Utf8Str errMsg;
7298 HRESULT rc = S_OK;
7299
7300 if (task->mIsSnapshot)
7301 {
7302 Assert (!task->mServerProgress.isNull());
7303 LogFlowFunc (("Waiting until the server creates differencing VDIs...\n"));
7304
7305 rc = task->mServerProgress->WaitForCompletion (-1);
7306 if (SUCCEEDED(rc))
7307 {
7308 LONG iRc = S_OK;
7309 rc = task->mServerProgress->COMGETTER(ResultCode) (&iRc);
7310 if (SUCCEEDED(rc))
7311 rc = iRc;
7312 }
7313 }
7314
7315 if (SUCCEEDED(rc))
7316 {
7317 LogFlowFunc (("Saving the state to '%s'...\n", task->mSavedStateFile.raw()));
7318
7319 int vrc = VMR3Save(that->mpVM,
7320 task->mSavedStateFile.c_str(),
7321 task->mIsSnapshot /*fContinueAfterwards*/,
7322 Console::stateProgressCallback,
7323 static_cast<VMProgressTask*>(task.get()));
7324 if (VBOX_FAILURE (vrc))
7325 {
7326 errMsg = Utf8StrFmt (
7327 Console::tr ("Failed to save the machine state to '%s' (%Rrc)"),
7328 task->mSavedStateFile.raw(), vrc);
7329 rc = E_FAIL;
7330 }
7331 }
7332
7333 /* lock the console once we're going to access it */
7334 AutoWriteLock thatLock (that);
7335
7336 if (SUCCEEDED(rc))
7337 {
7338 if (task->mIsSnapshot)
7339 do
7340 {
7341 LogFlowFunc(("Reattaching new differencing hard disks...\n"));
7342
7343 com::SafeIfaceArray<IHardDiskAttachment> atts;
7344 rc = that->mMachine->COMGETTER(HardDiskAttachments)(ComSafeArrayAsOutParam(atts));
7345 if (FAILED (rc))
7346 break;
7347 for (size_t i = 0; i < atts.size(); ++ i)
7348 {
7349 ComPtr<IStorageController> controller;
7350 BSTR controllerName;
7351 ULONG lInstance;
7352 StorageControllerType_T enmController;
7353
7354 /*
7355 * We can't pass a storage controller object directly
7356 * (g++ complains about not being able to pass non POD types through '...')
7357 * so we have to query needed values here and pass them.
7358 */
7359 rc = atts[i]->COMGETTER(Controller)(&controllerName);
7360 if (FAILED(rc))
7361 break;
7362
7363 rc = that->mMachine->GetStorageControllerByName(controllerName, controller.asOutParam());
7364 if (FAILED(rc))
7365 break;
7366
7367 rc = controller->COMGETTER(ControllerType)(&enmController);
7368 rc = controller->COMGETTER(Instance)(&lInstance);
7369 /*
7370 * don't leave the lock since reconfigureHardDisks isn't going
7371 * to access Console.
7372 */
7373 int vrc = VMR3ReqCallWait(that->mpVM, VMCPUID_ANY,
7374 (PFNRT)reconfigureHardDisks, 5, that->mpVM, lInstance, enmController, atts[i], &rc);
7375 if (RT_FAILURE(vrc))
7376 {
7377 errMsg = Utf8StrFmt(Console::tr("%Rrc"), vrc);
7378 rc = E_FAIL;
7379 break;
7380 }
7381 if (FAILED(rc))
7382 break;
7383 }
7384 }
7385 while (0);
7386 }
7387
7388 /* finalize the procedure regardless of the result */
7389 if (task->mIsSnapshot)
7390 {
7391 /*
7392 * finalize the requested snapshot object.
7393 * This will reset the machine state to the state it had right
7394 * before calling mControl->BeginTakingSnapshot().
7395 */
7396 that->mControl->EndTakingSnapshot (SUCCEEDED(rc));
7397 }
7398 else
7399 {
7400 /*
7401 * finalize the requested save state procedure.
7402 * In case of success, the server will set the machine state to Saved;
7403 * in case of failure it will reset the it to the state it had right
7404 * before calling mControl->BeginSavingState().
7405 */
7406 that->mControl->EndSavingState (SUCCEEDED(rc));
7407 }
7408
7409 /* synchronize the state with the server */
7410 if (task->mIsSnapshot || FAILED (rc))
7411 {
7412 if (task->mLastMachineState == MachineState_Running)
7413 {
7414 /* restore the paused state if appropriate */
7415 that->setMachineStateLocally (MachineState_Paused);
7416 /* restore the running state if appropriate */
7417 that->Resume();
7418 }
7419 else
7420 that->setMachineStateLocally (task->mLastMachineState);
7421 }
7422 else
7423 {
7424 /*
7425 * The machine has been successfully saved, so power it down
7426 * (vmstateChangeCallback() will set state to Saved on success).
7427 * Note: we release the task's VM caller, otherwise it will
7428 * deadlock.
7429 */
7430 task->releaseVMCaller();
7431
7432 rc = that->powerDown();
7433 }
7434
7435 /* notify the progress object about operation completion */
7436 if (SUCCEEDED(rc))
7437 task->mProgress->notifyComplete (S_OK);
7438 else
7439 {
7440 if (errMsg.length())
7441 task->mProgress->notifyComplete(rc,
7442 COM_IIDOF(IConsole),
7443 Console::getComponentName(),
7444 errMsg.c_str());
7445 else
7446 task->mProgress->notifyComplete (rc);
7447 }
7448
7449 LogFlowFuncLeave();
7450 return VINF_SUCCESS;
7451}
7452
7453/**
7454 * Thread for powering down the Console.
7455 *
7456 * @param Thread The thread handle.
7457 * @param pvUser Pointer to the VMTask structure.
7458 * @return VINF_SUCCESS (ignored).
7459 *
7460 * @note Locks the Console object for writing.
7461 */
7462/*static*/
7463DECLCALLBACK (int) Console::powerDownThread (RTTHREAD Thread, void *pvUser)
7464{
7465 LogFlowFuncEnter();
7466
7467 std::auto_ptr <VMProgressTask> task (static_cast <VMProgressTask *> (pvUser));
7468 AssertReturn(task.get(), VERR_INVALID_PARAMETER);
7469
7470 AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
7471
7472 const ComObjPtr<Console> &that = task->mConsole;
7473
7474 /* Note: no need to use addCaller() to protect Console because VMTask does
7475 * that */
7476
7477 /* wait until the method tat started us returns */
7478 AutoWriteLock thatLock (that);
7479
7480 /* release VM caller to avoid the powerDown() deadlock */
7481 task->releaseVMCaller();
7482
7483 that->powerDown (task->mProgress);
7484
7485 LogFlowFuncLeave();
7486 return VINF_SUCCESS;
7487}
7488
7489/**
7490 * The Main status driver instance data.
7491 */
7492typedef struct DRVMAINSTATUS
7493{
7494 /** The LED connectors. */
7495 PDMILEDCONNECTORS ILedConnectors;
7496 /** Pointer to the LED ports interface above us. */
7497 PPDMILEDPORTS pLedPorts;
7498 /** Pointer to the array of LED pointers. */
7499 PPDMLED *papLeds;
7500 /** The unit number corresponding to the first entry in the LED array. */
7501 RTUINT iFirstLUN;
7502 /** The unit number corresponding to the last entry in the LED array.
7503 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
7504 RTUINT iLastLUN;
7505} DRVMAINSTATUS, *PDRVMAINSTATUS;
7506
7507
7508/**
7509 * Notification about a unit which have been changed.
7510 *
7511 * The driver must discard any pointers to data owned by
7512 * the unit and requery it.
7513 *
7514 * @param pInterface Pointer to the interface structure containing the called function pointer.
7515 * @param iLUN The unit number.
7516 */
7517DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
7518{
7519 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
7520 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
7521 {
7522 PPDMLED pLed;
7523 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
7524 if (VBOX_FAILURE(rc))
7525 pLed = NULL;
7526 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
7527 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
7528 }
7529}
7530
7531
7532/**
7533 * Queries an interface to the driver.
7534 *
7535 * @returns Pointer to interface.
7536 * @returns NULL if the interface was not supported by the driver.
7537 * @param pInterface Pointer to this interface structure.
7538 * @param enmInterface The requested interface identification.
7539 */
7540DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
7541{
7542 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
7543 PDRVMAINSTATUS pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7544 switch (enmInterface)
7545 {
7546 case PDMINTERFACE_BASE:
7547 return &pDrvIns->IBase;
7548 case PDMINTERFACE_LED_CONNECTORS:
7549 return &pDrv->ILedConnectors;
7550 default:
7551 return NULL;
7552 }
7553}
7554
7555
7556/**
7557 * Destruct a status driver instance.
7558 *
7559 * @returns VBox status.
7560 * @param pDrvIns The driver instance data.
7561 */
7562DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
7563{
7564 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7565 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7566 if (pData->papLeds)
7567 {
7568 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
7569 while (iLed-- > 0)
7570 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL);
7571 }
7572}
7573
7574
7575/**
7576 * Construct a status driver instance.
7577 *
7578 * @copydoc FNPDMDRVCONSTRUCT
7579 */
7580DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
7581{
7582 PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
7583 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
7584
7585 /*
7586 * Validate configuration.
7587 */
7588 if (!CFGMR3AreValuesValid(pCfgHandle, "papLeds\0First\0Last\0"))
7589 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
7590 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
7591 ("Configuration error: Not possible to attach anything to this driver!\n"),
7592 VERR_PDM_DRVINS_NO_ATTACH);
7593
7594 /*
7595 * Data.
7596 */
7597 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
7598 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
7599
7600 /*
7601 * Read config.
7602 */
7603 int rc = CFGMR3QueryPtr(pCfgHandle, "papLeds", (void **)&pData->papLeds);
7604 if (VBOX_FAILURE(rc))
7605 {
7606 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
7607 return rc;
7608 }
7609
7610 rc = CFGMR3QueryU32(pCfgHandle, "First", &pData->iFirstLUN);
7611 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7612 pData->iFirstLUN = 0;
7613 else if (VBOX_FAILURE(rc))
7614 {
7615 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
7616 return rc;
7617 }
7618
7619 rc = CFGMR3QueryU32(pCfgHandle, "Last", &pData->iLastLUN);
7620 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7621 pData->iLastLUN = 0;
7622 else if (VBOX_FAILURE(rc))
7623 {
7624 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
7625 return rc;
7626 }
7627 if (pData->iFirstLUN > pData->iLastLUN)
7628 {
7629 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
7630 return VERR_GENERAL_FAILURE;
7631 }
7632
7633 /*
7634 * Get the ILedPorts interface of the above driver/device and
7635 * query the LEDs we want.
7636 */
7637 pData->pLedPorts = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
7638 if (!pData->pLedPorts)
7639 {
7640 AssertMsgFailed(("Configuration error: No led ports interface above!\n"));
7641 return VERR_PDM_MISSING_INTERFACE_ABOVE;
7642 }
7643
7644 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; i++)
7645 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
7646
7647 return VINF_SUCCESS;
7648}
7649
7650
7651/**
7652 * Keyboard driver registration record.
7653 */
7654const PDMDRVREG Console::DrvStatusReg =
7655{
7656 /* u32Version */
7657 PDM_DRVREG_VERSION,
7658 /* szDriverName */
7659 "MainStatus",
7660 /* pszDescription */
7661 "Main status driver (Main as in the API).",
7662 /* fFlags */
7663 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
7664 /* fClass. */
7665 PDM_DRVREG_CLASS_STATUS,
7666 /* cMaxInstances */
7667 ~0,
7668 /* cbInstance */
7669 sizeof(DRVMAINSTATUS),
7670 /* pfnConstruct */
7671 Console::drvStatus_Construct,
7672 /* pfnDestruct */
7673 Console::drvStatus_Destruct,
7674 /* pfnIOCtl */
7675 NULL,
7676 /* pfnPowerOn */
7677 NULL,
7678 /* pfnReset */
7679 NULL,
7680 /* pfnSuspend */
7681 NULL,
7682 /* pfnResume */
7683 NULL,
7684 /* pfnAttach */
7685 NULL,
7686 /* pfnDetach */
7687 NULL,
7688 /* pfnPowerOff */
7689 NULL,
7690 /* pfnSoftReset */
7691 NULL,
7692 /* u32EndVersion */
7693 PDM_DRVREG_VERSION
7694};
7695
7696/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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