VirtualBox

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

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

Main: Fixed a handful status code mixups caught by RTERR_STRICT_RC.

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

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