VirtualBox

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

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

provide MPS table if IOAPIC is present, use the last 16 bytes of the EBDA for MPS floating pointer structure

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

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