VirtualBox

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

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

Implemented new 'MonitorCount' VM setting, to be used by VGA device and NT guest driver.

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