VirtualBox

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

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

pass down the version of the usb device (still gotta find the version of the physical port).

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

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