VirtualBox

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

Last change on this file since 3086 was 3086, checked in by vboxsync, 18 years ago

introduced RTLogCreateEx and RTLogCreateExV to be able to pass an error string back to the caller

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

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