VirtualBox

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

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

added support for serial ports to Main and VBoxManage

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

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