VirtualBox

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

Last change on this file since 5146 was 5146, checked in by vboxsync, 17 years ago

move the SVN revision stuff into separate files to omit recompilation of expensive files if only the SVN revision changed

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