VirtualBox

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

Last change on this file since 22379 was 22375, checked in by vboxsync, 15 years ago

NetSniffer: do not erase the dump file even if network attachment is changed

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