VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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