VirtualBox

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

Last change on this file since 21649 was 21647, checked in by vboxsync, 16 years ago

Pass RDP client name, username and domain via guest properties (xTracker #4123)

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